Update the vulkan support classes and enable runtime detection for falling back to non-rayquery rendering if the device does not support it

This commit is contained in:
Magnus Norddahl 2022-09-01 01:04:17 +02:00
parent 0c6954a767
commit 1e62255b28
15 changed files with 1109 additions and 931 deletions

View file

@ -180,6 +180,12 @@ set( SOURCES
src/lightmap/vulkanobjects.h src/lightmap/vulkanobjects.h
src/lightmap/vulkanbuilders.cpp src/lightmap/vulkanbuilders.cpp
src/lightmap/vulkanbuilders.h src/lightmap/vulkanbuilders.h
src/lightmap/vulkancompatibledevice.cpp
src/lightmap/vulkancompatibledevice.h
src/lightmap/vulkaninstance.cpp
src/lightmap/vulkaninstance.h
src/lightmap/vulkansurface.cpp
src/lightmap/vulkansurface.h
src/lightmap/stacktrace.cpp src/lightmap/stacktrace.cpp
src/lightmap/stacktrace.h src/lightmap/stacktrace.h
src/lightmap/surfaceclip.cpp src/lightmap/surfaceclip.cpp

View file

@ -7,6 +7,8 @@
#include "framework/templates.h" #include "framework/templates.h"
#include "framework/halffloat.h" #include "framework/halffloat.h"
#include "vulkanbuilders.h" #include "vulkanbuilders.h"
#include "vulkancompatibledevice.h"
#include "stacktrace.h"
#include <map> #include <map>
#include <vector> #include <vector>
#include <algorithm> #include <algorithm>
@ -21,10 +23,22 @@
extern bool VKDebug; extern bool VKDebug;
extern bool NoRtx; extern bool NoRtx;
void VulkanPrintLog(const char* typestr, const std::string& msg)
{
printf("[%s] %s\n", typestr, msg.c_str());
printf("%s\n", CaptureStackTraceText(2).c_str());
}
void VulkanError(const char* text)
{
throw std::runtime_error(text);
}
GPURaytracer::GPURaytracer() GPURaytracer::GPURaytracer()
{ {
device = std::make_unique<VulkanDevice>(0, VKDebug); auto instance = std::make_shared<VulkanInstance>(VKDebug);
useRayQuery = !NoRtx;// && device->physicalDevice.rayQueryProperties.supportsRayQuery; device = std::make_unique<VulkanDevice>(instance, nullptr, VulkanCompatibleDevice::SelectDevice(instance, nullptr, 0));
useRayQuery = !NoRtx && device->PhysicalDevice.Features.RayQuery.rayQuery;
PrintVulkanInfo(); PrintVulkanInfo();
} }
@ -38,9 +52,6 @@ void GPURaytracer::Raytrace(LevelMesh* level)
printf("Building Vulkan acceleration structures\n"); printf("Building Vulkan acceleration structures\n");
if (device->renderdoc)
device->renderdoc->StartFrameCapture(0, 0);
CreateVulkanObjects(); CreateVulkanObjects();
printf("Ray tracing in progress...\n"); printf("Ray tracing in progress...\n");
@ -79,9 +90,6 @@ void GPURaytracer::Raytrace(LevelMesh* level)
DownloadAtlasImage(pageIndex); DownloadAtlasImage(pageIndex);
} }
if (device->renderdoc)
device->renderdoc->EndFrameCapture(0, 0);
printf("Ray trace complete\n"); printf("Ray trace complete\n");
} }
@ -332,7 +340,7 @@ vec2 GPURaytracer::ToUV(const vec3& vert, const Surface* targetSurface)
void GPURaytracer::CreateVulkanObjects() void GPURaytracer::CreateVulkanObjects()
{ {
submitFence = std::make_unique<VulkanFence>(device.get()); submitFence = std::make_unique<VulkanFence>(device.get());
cmdpool = std::make_unique<VulkanCommandPool>(device.get(), device->graphicsFamily); cmdpool = std::make_unique<VulkanCommandPool>(device.get(), device->GraphicsFamily);
BeginCommands(); BeginCommands();
@ -402,7 +410,7 @@ void GPURaytracer::FinishCommands()
QueueSubmit() QueueSubmit()
.AddCommandBuffer(cmdbuffer.get()) .AddCommandBuffer(cmdbuffer.get())
.Execute(device.get(), device->graphicsQueue, submitFence.get()); .Execute(device.get(), device->GraphicsQueue, submitFence.get());
VkResult result = vkWaitForFences(device->device, 1, &submitFence->fence, VK_TRUE, std::numeric_limits<uint64_t>::max()); VkResult result = vkWaitForFences(device->device, 1, &submitFence->fence, VK_TRUE, std::numeric_limits<uint64_t>::max());
if (result != VK_SUCCESS) if (result != VK_SUCCESS)
@ -623,8 +631,8 @@ void GPURaytracer::CreateTopLevelAccelerationStructure()
void GPURaytracer::CreateShaders() void GPURaytracer::CreateShaders()
{ {
FString prefix = "#version 460\r\n#line 1\r\n"; std::string prefix = "#version 460\r\n#line 1\r\n";
FString traceprefix = "#version 460\r\n"; std::string traceprefix = "#version 460\r\n";
if (useRayQuery) if (useRayQuery)
{ {
traceprefix += "#extension GL_EXT_ray_query : require\r\n"; traceprefix += "#extension GL_EXT_ray_query : require\r\n";
@ -878,7 +886,7 @@ LightmapImage GPURaytracer::CreateImage(int width, int height)
void GPURaytracer::CreateUniformBuffer() void GPURaytracer::CreateUniformBuffer()
{ {
VkDeviceSize align = device->physicalDevice.properties.limits.minUniformBufferOffsetAlignment; VkDeviceSize align = device->PhysicalDevice.Properties.limits.minUniformBufferOffsetAlignment;
uniformStructStride = (sizeof(Uniforms) + align - 1) / align * align; uniformStructStride = (sizeof(Uniforms) + align - 1) / align * align;
uniformBuffer = BufferBuilder() uniformBuffer = BufferBuilder()
@ -932,7 +940,7 @@ std::vector<CollisionNode> GPURaytracer::CreateCollisionNodes()
void GPURaytracer::PrintVulkanInfo() void GPURaytracer::PrintVulkanInfo()
{ {
const auto& props = device->physicalDevice.properties; const auto& props = device->PhysicalDevice.Properties;
std::string deviceType; std::string deviceType;
switch (props.deviceType) switch (props.deviceType)

View file

@ -4,6 +4,7 @@
#ifdef WIN32 #ifdef WIN32
#include <Windows.h> #include <Windows.h>
#include <DbgHelp.h> #include <DbgHelp.h>
#include <psapi.h>
#else #else
#include <execinfo.h> #include <execinfo.h>
#include <cxxabi.h> #include <cxxabi.h>
@ -17,8 +18,16 @@
class NativeSymbolResolver class NativeSymbolResolver
{ {
public: public:
NativeSymbolResolver() { SymInitialize(GetCurrentProcess(), nullptr, TRUE); } NativeSymbolResolver()
~NativeSymbolResolver() { SymCleanup(GetCurrentProcess()); } {
SymInitialize(GetCurrentProcess(), nullptr, TRUE);
GetModuleInformation(GetCurrentProcess(), GetModuleHandle(0), &moduleInfo, sizeof(MODULEINFO));
}
~NativeSymbolResolver()
{
SymCleanup(GetCurrentProcess());
}
std::string GetName(void* frame) std::string GetName(void* frame)
{ {
@ -34,6 +43,9 @@ public:
BOOL result = SymGetSymFromAddr64(GetCurrentProcess(), (DWORD64)frame, &displacement, symbol64); BOOL result = SymGetSymFromAddr64(GetCurrentProcess(), (DWORD64)frame, &displacement, symbol64);
if (result) if (result)
{ {
if ((DWORD64)frame < (DWORD64)moduleInfo.lpBaseOfDll || (DWORD64)frame >= ((DWORD64)moduleInfo.lpBaseOfDll + moduleInfo.SizeOfImage))
return s; // Ignore anything not from the exe itself
IMAGEHLP_LINE64 line64; IMAGEHLP_LINE64 line64;
DWORD displacement = 0; DWORD displacement = 0;
memset(&line64, 0, sizeof(IMAGEHLP_LINE64)); memset(&line64, 0, sizeof(IMAGEHLP_LINE64));
@ -51,6 +63,8 @@ public:
return s; return s;
} }
MODULEINFO moduleInfo = {};
}; };
#else #else
class NativeSymbolResolver class NativeSymbolResolver

View file

@ -109,77 +109,45 @@ static const TBuiltInResource DefaultTBuiltInResource = {
} }
}; };
void ShaderBuilder::Init()
{
ShInitialize();
}
void ShaderBuilder::Deinit()
{
ShFinalize();
}
ShaderBuilder::ShaderBuilder() ShaderBuilder::ShaderBuilder()
{ {
} }
ShaderBuilder& ShaderBuilder::VertexShader(const FString &c) ShaderBuilder& ShaderBuilder::VertexShader(const std::string& c)
{ {
code = c; code = c;
stage = EShLanguage::EShLangVertex; stage = EShLanguage::EShLangVertex;
return *this; return *this;
} }
ShaderBuilder& ShaderBuilder::FragmentShader(const FString &c) ShaderBuilder& ShaderBuilder::FragmentShader(const std::string& c)
{ {
code = c; code = c;
stage = EShLanguage::EShLangFragment; stage = EShLanguage::EShLangFragment;
return *this; return *this;
} }
ShaderBuilder& ShaderBuilder::RayGenShader(const FString& c)
{
code = c;
stage = EShLanguage::EShLangRayGen;
return *this;
}
ShaderBuilder& ShaderBuilder::IntersectShader(const FString& c)
{
code = c;
stage = EShLanguage::EShLangIntersect;
return *this;
}
ShaderBuilder& ShaderBuilder::AnyHitShader(const FString& c)
{
code = c;
stage = EShLanguage::EShLangAnyHit;
return *this;
}
ShaderBuilder& ShaderBuilder::ClosestHitShader(const FString& c)
{
code = c;
stage = EShLanguage::EShLangClosestHit;
return *this;
}
ShaderBuilder& ShaderBuilder::MissShader(const FString& c)
{
code = c;
stage = EShLanguage::EShLangMiss;
return *this;
}
ShaderBuilder& ShaderBuilder::CallableShader(const FString& c)
{
code = c;
stage = EShLanguage::EShLangCallable;
return *this;
}
std::unique_ptr<VulkanShader> ShaderBuilder::Create(const char *shadername, VulkanDevice *device) std::unique_ptr<VulkanShader> ShaderBuilder::Create(const char *shadername, VulkanDevice *device)
{ {
EShLanguage stage = (EShLanguage)this->stage; EShLanguage stage = (EShLanguage)this->stage;
const char *sources[] = { code.GetChars() }; const char *sources[] = { code.c_str() };
TBuiltInResource resources = DefaultTBuiltInResource; TBuiltInResource resources = DefaultTBuiltInResource;
glslang::TShader shader(stage); glslang::TShader shader(stage);
shader.setStrings(sources, 1); shader.setStrings(sources, 1);
shader.setEnvInput(glslang::EShSourceGlsl, stage, glslang::EShClientVulkan, 100); shader.setEnvInput(glslang::EShSourceGlsl, stage, glslang::EShClientVulkan, 100);
if (device->ApiVersion >= VK_API_VERSION_1_2) if (device->Instance->ApiVersion >= VK_API_VERSION_1_2)
{ {
shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_2); shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_2);
shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_4); shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_4);
@ -192,7 +160,7 @@ std::unique_ptr<VulkanShader> ShaderBuilder::Create(const char *shadername, Vulk
bool compileSuccess = shader.parse(&resources, 110, false, EShMsgVulkanRules); bool compileSuccess = shader.parse(&resources, 110, false, EShMsgVulkanRules);
if (!compileSuccess) if (!compileSuccess)
{ {
throw std::runtime_error(std::string("Could not compile shader ") + shadername + ": " + shader.getInfoLog()); throw std::runtime_error(std::string("Shader compile failed: ") + shader.getInfoLog());
} }
glslang::TProgram program; glslang::TProgram program;
@ -200,13 +168,13 @@ std::unique_ptr<VulkanShader> ShaderBuilder::Create(const char *shadername, Vulk
bool linkSuccess = program.link(EShMsgDefault); bool linkSuccess = program.link(EShMsgDefault);
if (!linkSuccess) if (!linkSuccess)
{ {
throw std::runtime_error(std::string("Could not link shader ") + shadername + ": " + program.getInfoLog()); throw std::runtime_error(std::string("Shader link failed: ") + program.getInfoLog());
} }
glslang::TIntermediate *intermediate = program.getIntermediate(stage); glslang::TIntermediate *intermediate = program.getIntermediate(stage);
if (!intermediate) if (!intermediate)
{ {
throw std::runtime_error(std::string("Internal shader compile error while processing: ") + shadername); throw std::runtime_error("Internal shader compiler error");
} }
glslang::SpvOptions spvOptions; glslang::SpvOptions spvOptions;
@ -226,9 +194,7 @@ std::unique_ptr<VulkanShader> ShaderBuilder::Create(const char *shadername, Vulk
VkShaderModule shaderModule; VkShaderModule shaderModule;
VkResult result = vkCreateShaderModule(device->device, &createInfo, nullptr, &shaderModule); VkResult result = vkCreateShaderModule(device->device, &createInfo, nullptr, &shaderModule);
if (result != VK_SUCCESS) if (result != VK_SUCCESS)
{
throw std::runtime_error("Could not create vulkan shader module"); throw std::runtime_error("Could not create vulkan shader module");
}
auto obj = std::make_unique<VulkanShader>(device, shaderModule); auto obj = std::make_unique<VulkanShader>(device, shaderModule);
if (debugName) if (debugName)
@ -297,7 +263,7 @@ ImageBuilder& ImageBuilder::MemoryType(VkMemoryPropertyFlags requiredFlags, VkMe
bool ImageBuilder::IsFormatSupported(VulkanDevice* device, VkFormatFeatureFlags bufferFeatures) bool ImageBuilder::IsFormatSupported(VulkanDevice* device, VkFormatFeatureFlags bufferFeatures)
{ {
VkImageFormatProperties properties = { }; VkImageFormatProperties properties = { };
VkResult result = vkGetPhysicalDeviceImageFormatProperties(device->physicalDevice.device, imageInfo.format, imageInfo.imageType, imageInfo.tiling, imageInfo.usage, imageInfo.flags, &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 (result != VK_SUCCESS) return false;
if (imageInfo.extent.width > properties.maxExtent.width) return false; if (imageInfo.extent.width > properties.maxExtent.width) return false;
if (imageInfo.extent.height > properties.maxExtent.height) return false; if (imageInfo.extent.height > properties.maxExtent.height) return false;
@ -308,7 +274,7 @@ bool ImageBuilder::IsFormatSupported(VulkanDevice* device, VkFormatFeatureFlags
if (bufferFeatures != 0) if (bufferFeatures != 0)
{ {
VkFormatProperties formatProperties = { }; VkFormatProperties formatProperties = { };
vkGetPhysicalDeviceFormatProperties(device->physicalDevice.device, imageInfo.format, &formatProperties); vkGetPhysicalDeviceFormatProperties(device->PhysicalDevice.Device, imageInfo.format, &formatProperties);
if ((formatProperties.bufferFeatures & bufferFeatures) != bufferFeatures) if ((formatProperties.bufferFeatures & bufferFeatures) != bufferFeatures)
return false; return false;
} }
@ -321,8 +287,7 @@ std::unique_ptr<VulkanImage> ImageBuilder::Create(VulkanDevice* device, VkDevice
VmaAllocation allocation; VmaAllocation allocation;
VkResult result = vmaCreateImage(device->allocator, &imageInfo, &allocInfo, &image, &allocation, nullptr); VkResult result = vmaCreateImage(device->allocator, &imageInfo, &allocInfo, &image, &allocation, nullptr);
if (result != VK_SUCCESS) CheckVulkanError(result, "Could not create vulkan image");
throw std::runtime_error("Could not create vulkan image");
if (allocatedBytes != nullptr) if (allocatedBytes != nullptr)
{ {
@ -385,8 +350,7 @@ std::unique_ptr<VulkanImageView> ImageViewBuilder::Create(VulkanDevice* device)
{ {
VkImageView view; VkImageView view;
VkResult result = vkCreateImageView(device->device, &viewInfo, nullptr, &view); VkResult result = vkCreateImageView(device->device, &viewInfo, nullptr, &view);
if (result != VK_SUCCESS) CheckVulkanError(result, "Could not create texture image view");
throw std::runtime_error("Could not create texture image view");
auto obj = std::make_unique<VulkanImageView>(device, view); auto obj = std::make_unique<VulkanImageView>(device, view);
if (debugName) if (debugName)
@ -457,6 +421,12 @@ SamplerBuilder& SamplerBuilder::Anisotropy(float maxAnisotropy)
return *this; return *this;
} }
SamplerBuilder& SamplerBuilder::MipLodBias(float bias)
{
samplerInfo.mipLodBias = bias;
return *this;
}
SamplerBuilder& SamplerBuilder::MaxLod(float value) SamplerBuilder& SamplerBuilder::MaxLod(float value)
{ {
samplerInfo.maxLod = value; samplerInfo.maxLod = value;
@ -467,8 +437,7 @@ std::unique_ptr<VulkanSampler> SamplerBuilder::Create(VulkanDevice* device)
{ {
VkSampler sampler; VkSampler sampler;
VkResult result = vkCreateSampler(device->device, &samplerInfo, nullptr, &sampler); VkResult result = vkCreateSampler(device->device, &samplerInfo, nullptr, &sampler);
if (result != VK_SUCCESS) CheckVulkanError(result, "Could not create texture sampler");
throw std::runtime_error("Could not create texture sampler");
auto obj = std::make_unique<VulkanSampler>(device, sampler); auto obj = std::make_unique<VulkanSampler>(device, sampler);
if (debugName) if (debugName)
obj->SetDebugName(debugName); obj->SetDebugName(debugName);
@ -511,8 +480,7 @@ std::unique_ptr<VulkanBuffer> BufferBuilder::Create(VulkanDevice* device)
VmaAllocation allocation; VmaAllocation allocation;
VkResult result = vmaCreateBuffer(device->allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr); VkResult result = vmaCreateBuffer(device->allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
if (result != VK_SUCCESS) CheckVulkanError(result, "Could not allocate memory for vulkan buffer");
throw std::runtime_error("Could not allocate memory for vulkan buffer");
auto obj = std::make_unique<VulkanBuffer>(device, buffer, allocation, bufferInfo.size); auto obj = std::make_unique<VulkanBuffer>(device, buffer, allocation, bufferInfo.size);
if (debugName) if (debugName)
@ -562,131 +530,6 @@ std::unique_ptr<VulkanAccelerationStructure> AccelerationStructureBuilder::Creat
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
RayTracingPipelineBuilder::RayTracingPipelineBuilder()
{
pipelineInfo.sType = VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_KHR;
}
RayTracingPipelineBuilder& RayTracingPipelineBuilder::Layout(VulkanPipelineLayout* layout)
{
pipelineInfo.layout = layout->layout;
return *this;
}
RayTracingPipelineBuilder& RayTracingPipelineBuilder::MaxPipelineRayRecursionDepth(int depth)
{
pipelineInfo.maxPipelineRayRecursionDepth = depth;
return *this;
}
RayTracingPipelineBuilder& RayTracingPipelineBuilder::AddShader(VkShaderStageFlagBits stage, VulkanShader* shader)
{
VkPipelineShaderStageCreateInfo stageInfo = { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO };
stageInfo.stage = stage;
stageInfo.module = shader->module;
stageInfo.pName = "main";
stages.push_back(stageInfo);
pipelineInfo.pStages = stages.data();
pipelineInfo.stageCount = (uint32_t)stages.size();
return *this;
}
RayTracingPipelineBuilder& RayTracingPipelineBuilder::AddRayGenGroup(int rayGenShader)
{
VkRayTracingShaderGroupCreateInfoKHR group = {};
group.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR;
group.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR;
group.generalShader = rayGenShader;
group.closestHitShader = VK_SHADER_UNUSED_KHR;
group.anyHitShader = VK_SHADER_UNUSED_KHR;
group.intersectionShader = VK_SHADER_UNUSED_KHR;
groups.push_back(group);
pipelineInfo.pGroups = groups.data();
pipelineInfo.groupCount = (uint32_t)groups.size();
return *this;
}
RayTracingPipelineBuilder& RayTracingPipelineBuilder::AddMissGroup(int missShader)
{
VkRayTracingShaderGroupCreateInfoKHR group = {};
group.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR;
group.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR;
group.generalShader = missShader;
group.closestHitShader = VK_SHADER_UNUSED_KHR;
group.anyHitShader = VK_SHADER_UNUSED_KHR;
group.intersectionShader = VK_SHADER_UNUSED_KHR;
groups.push_back(group);
pipelineInfo.pGroups = groups.data();
pipelineInfo.groupCount = (uint32_t)groups.size();
return *this;
}
RayTracingPipelineBuilder& RayTracingPipelineBuilder::AddTrianglesHitGroup(int closestHitShader, int anyHitShader)
{
VkRayTracingShaderGroupCreateInfoKHR group = {};
group.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR;
group.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_KHR;
group.generalShader = VK_SHADER_UNUSED_KHR;
group.closestHitShader = closestHitShader;
group.anyHitShader = anyHitShader;
group.intersectionShader = VK_SHADER_UNUSED_KHR;
groups.push_back(group);
pipelineInfo.pGroups = groups.data();
pipelineInfo.groupCount = (uint32_t)groups.size();
return *this;
}
RayTracingPipelineBuilder& RayTracingPipelineBuilder::AddProceduralHitGroup(int intersectionShader, int closestHitShader, int anyHitShader)
{
VkRayTracingShaderGroupCreateInfoKHR group = {};
group.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR;
group.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_PROCEDURAL_HIT_GROUP_KHR;
group.generalShader = VK_SHADER_UNUSED_KHR;
group.closestHitShader = closestHitShader;
group.anyHitShader = anyHitShader;
group.intersectionShader = intersectionShader;
groups.push_back(group);
pipelineInfo.pGroups = groups.data();
pipelineInfo.groupCount = (uint32_t)groups.size();
return *this;
}
std::unique_ptr<VulkanPipeline> RayTracingPipelineBuilder::Create(VulkanDevice* device)
{
VkPipeline pipeline;
VkResult result = vkCreateRayTracingPipelinesKHR(device->device, VK_NULL_HANDLE, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline);
if (result != VK_SUCCESS)
throw std::runtime_error("vkCreateRayTracingPipelinesKHR failed");
std::vector<uint8_t> shaderGroupHandles(device->physicalDevice.rayTracingProperties.shaderGroupHandleSize * groups.size());
if (!groups.empty())
{
result = vkGetRayTracingShaderGroupHandlesKHR(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("vkGetRayTracingShaderGroupHandlesKHR failed");
}
}
auto obj = std::make_unique<VulkanPipeline>(device, pipeline, shaderGroupHandles);
if (debugName)
obj->SetDebugName(debugName);
return obj;
}
/////////////////////////////////////////////////////////////////////////////
ComputePipelineBuilder::ComputePipelineBuilder() ComputePipelineBuilder::ComputePipelineBuilder()
{ {
pipelineInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; pipelineInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
@ -723,10 +566,15 @@ std::unique_ptr<VulkanPipeline> ComputePipelineBuilder::Create(VulkanDevice* dev
DescriptorSetLayoutBuilder::DescriptorSetLayoutBuilder() DescriptorSetLayoutBuilder::DescriptorSetLayoutBuilder()
{ {
layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
} }
DescriptorSetLayoutBuilder& DescriptorSetLayoutBuilder::AddBinding(int index, VkDescriptorType type, int arrayCount, VkShaderStageFlags stageFlags) DescriptorSetLayoutBuilder& DescriptorSetLayoutBuilder::Flags(VkDescriptorSetLayoutCreateFlags flags)
{
layoutInfo.flags = flags;
return *this;
}
DescriptorSetLayoutBuilder& DescriptorSetLayoutBuilder::AddBinding(int index, VkDescriptorType type, int arrayCount, VkShaderStageFlags stageFlags, VkDescriptorBindingFlags flags)
{ {
VkDescriptorSetLayoutBinding binding = { }; VkDescriptorSetLayoutBinding binding = { };
binding.binding = index; binding.binding = index;
@ -734,10 +582,18 @@ DescriptorSetLayoutBuilder& DescriptorSetLayoutBuilder::AddBinding(int index, Vk
binding.descriptorCount = arrayCount; binding.descriptorCount = arrayCount;
binding.stageFlags = stageFlags; binding.stageFlags = stageFlags;
binding.pImmutableSamplers = nullptr; binding.pImmutableSamplers = nullptr;
bindings.Push(binding); bindings.push_back(binding);
bindingFlags.push_back(flags);
layoutInfo.bindingCount = (uint32_t)bindings.size();
layoutInfo.pBindings = bindings.data();
bindingFlagsInfo.bindingCount = (uint32_t)bindings.size();
bindingFlagsInfo.pBindingFlags = bindingFlags.data();
if (flags != 0)
layoutInfo.pNext = &bindingFlagsInfo;
layoutInfo.bindingCount = (uint32_t)bindings.Size();
layoutInfo.pBindings = &bindings[0];
return *this; return *this;
} }
@ -745,8 +601,7 @@ std::unique_ptr<VulkanDescriptorSetLayout> DescriptorSetLayoutBuilder::Create(Vu
{ {
VkDescriptorSetLayout layout; VkDescriptorSetLayout layout;
VkResult result = vkCreateDescriptorSetLayout(device->device, &layoutInfo, nullptr, &layout); VkResult result = vkCreateDescriptorSetLayout(device->device, &layoutInfo, nullptr, &layout);
if (result != VK_SUCCESS) CheckVulkanError(result, "Could not create descriptor set layout");
throw std::runtime_error("Could not create descriptor set layout");
auto obj = std::make_unique<VulkanDescriptorSetLayout>(device, layout); auto obj = std::make_unique<VulkanDescriptorSetLayout>(device, layout);
if (debugName) if (debugName)
obj->SetDebugName(debugName); obj->SetDebugName(debugName);
@ -762,6 +617,12 @@ DescriptorPoolBuilder::DescriptorPoolBuilder()
poolInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; poolInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
} }
DescriptorPoolBuilder& DescriptorPoolBuilder::Flags(VkDescriptorPoolCreateFlags flags)
{
poolInfo.flags = flags | VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
return *this;
}
DescriptorPoolBuilder& DescriptorPoolBuilder::MaxSets(int value) DescriptorPoolBuilder& DescriptorPoolBuilder::MaxSets(int value)
{ {
poolInfo.maxSets = value; poolInfo.maxSets = value;
@ -784,8 +645,7 @@ std::unique_ptr<VulkanDescriptorPool> DescriptorPoolBuilder::Create(VulkanDevice
{ {
VkDescriptorPool descriptorPool; VkDescriptorPool descriptorPool;
VkResult result = vkCreateDescriptorPool(device->device, &poolInfo, nullptr, &descriptorPool); VkResult result = vkCreateDescriptorPool(device->device, &poolInfo, nullptr, &descriptorPool);
if (result != VK_SUCCESS) CheckVulkanError(result, "Could not create descriptor pool");
throw std::runtime_error("Could not create descriptor pool");
auto obj = std::make_unique<VulkanDescriptorPool>(device, descriptorPool); auto obj = std::make_unique<VulkanDescriptorPool>(device, descriptorPool);
if (debugName) if (debugName)
obj->SetDebugName(debugName); obj->SetDebugName(debugName);
@ -811,8 +671,7 @@ std::unique_ptr<VulkanQueryPool> QueryPoolBuilder::Create(VulkanDevice* device)
{ {
VkQueryPool queryPool; VkQueryPool queryPool;
VkResult result = vkCreateQueryPool(device->device, &poolInfo, nullptr, &queryPool); VkResult result = vkCreateQueryPool(device->device, &poolInfo, nullptr, &queryPool);
if (result != VK_SUCCESS) CheckVulkanError(result, "Could not create query pool");
throw std::runtime_error("Could not create query pool");
auto obj = std::make_unique<VulkanQueryPool>(device, queryPool); auto obj = std::make_unique<VulkanQueryPool>(device, queryPool);
if (debugName) if (debugName)
obj->SetDebugName(debugName); obj->SetDebugName(debugName);
@ -862,8 +721,7 @@ std::unique_ptr<VulkanFramebuffer> FramebufferBuilder::Create(VulkanDevice* devi
{ {
VkFramebuffer framebuffer = 0; VkFramebuffer framebuffer = 0;
VkResult result = vkCreateFramebuffer(device->device, &framebufferInfo, nullptr, &framebuffer); VkResult result = vkCreateFramebuffer(device->device, &framebufferInfo, nullptr, &framebuffer);
if (result != VK_SUCCESS) CheckVulkanError(result, "Could not create framebuffer");
throw std::runtime_error("Could not create framebuffer");
auto obj = std::make_unique<VulkanFramebuffer>(device, framebuffer); auto obj = std::make_unique<VulkanFramebuffer>(device, framebuffer);
if (debugName) if (debugName)
obj->SetDebugName(debugName); obj->SetDebugName(debugName);
@ -1179,8 +1037,7 @@ std::unique_ptr<VulkanPipeline> GraphicsPipelineBuilder::Create(VulkanDevice* de
{ {
VkPipeline pipeline = 0; VkPipeline pipeline = 0;
VkResult result = vkCreateGraphicsPipelines(device->device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline); VkResult result = vkCreateGraphicsPipelines(device->device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline);
if (result != VK_SUCCESS) CheckVulkanError(result, "Could not create graphics pipeline");
throw std::runtime_error("Could not create graphics pipeline");
auto obj = std::make_unique<VulkanPipeline>(device, pipeline); auto obj = std::make_unique<VulkanPipeline>(device, pipeline);
if (debugName) if (debugName)
obj->SetDebugName(debugName); obj->SetDebugName(debugName);
@ -1218,8 +1075,7 @@ std::unique_ptr<VulkanPipelineLayout> PipelineLayoutBuilder::Create(VulkanDevice
{ {
VkPipelineLayout pipelineLayout; VkPipelineLayout pipelineLayout;
VkResult result = vkCreatePipelineLayout(device->device, &pipelineLayoutInfo, nullptr, &pipelineLayout); VkResult result = vkCreatePipelineLayout(device->device, &pipelineLayoutInfo, nullptr, &pipelineLayout);
if (result != VK_SUCCESS) CheckVulkanError(result, "Could not create pipeline layout");
throw std::runtime_error("Could not create pipeline layout");
auto obj = std::make_unique<VulkanPipelineLayout>(device, pipelineLayout); auto obj = std::make_unique<VulkanPipelineLayout>(device, pipelineLayout);
if (debugName) if (debugName)
obj->SetDebugName(debugName); obj->SetDebugName(debugName);
@ -1323,8 +1179,7 @@ std::unique_ptr<VulkanRenderPass> RenderPassBuilder::Create(VulkanDevice* device
{ {
VkRenderPass renderPass = 0; VkRenderPass renderPass = 0;
VkResult result = vkCreateRenderPass(device->device, &renderPassInfo, nullptr, &renderPass); VkResult result = vkCreateRenderPass(device->device, &renderPassInfo, nullptr, &renderPass);
if (result != VK_SUCCESS) CheckVulkanError(result, "Could not create render pass");
throw std::runtime_error("Could not create render pass");
auto obj = std::make_unique<VulkanRenderPass>(device, renderPass); auto obj = std::make_unique<VulkanRenderPass>(device, renderPass);
if (debugName) if (debugName)
obj->SetDebugName(debugName); obj->SetDebugName(debugName);
@ -1466,9 +1321,8 @@ QueueSubmit& QueueSubmit::AddSignal(VulkanSemaphore* semaphore)
void QueueSubmit::Execute(VulkanDevice* device, VkQueue queue, VulkanFence* fence) void QueueSubmit::Execute(VulkanDevice* device, VkQueue queue, VulkanFence* fence)
{ {
VkResult result = vkQueueSubmit(device->graphicsQueue, 1, &submitInfo, fence ? fence->fence : VK_NULL_HANDLE); VkResult result = vkQueueSubmit(device->GraphicsQueue, 1, &submitInfo, fence ? fence->fence : VK_NULL_HANDLE);
if (result != VK_SUCCESS) CheckVulkanError(result, "Could not submit command buffer");
throw std::runtime_error("Could not submit command buffer");
} }
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
@ -1501,28 +1355,6 @@ WriteDescriptors& WriteDescriptors::AddBuffer(VulkanDescriptorSet* descriptorSet
return *this; return *this;
} }
WriteDescriptors& WriteDescriptors::AddSampledImage(VulkanDescriptorSet* descriptorSet, int binding, VulkanImageView* view, VkImageLayout imageLayout)
{
VkDescriptorImageInfo imageInfo = {};
imageInfo.imageView = view->view;
imageInfo.imageLayout = imageLayout;
auto extra = std::make_unique<WriteExtra>();
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_SAMPLED_IMAGE;
descriptorWrite.descriptorCount = 1;
descriptorWrite.pImageInfo = &extra->imageInfo;
writes.push_back(descriptorWrite);
writeExtras.push_back(std::move(extra));
return *this;
}
WriteDescriptors& WriteDescriptors::AddStorageImage(VulkanDescriptorSet* descriptorSet, int binding, VulkanImageView* view, VkImageLayout imageLayout) WriteDescriptors& WriteDescriptors::AddStorageImage(VulkanDescriptorSet* descriptorSet, int binding, VulkanImageView* view, VkImageLayout imageLayout)
{ {
VkDescriptorImageInfo imageInfo = {}; VkDescriptorImageInfo imageInfo = {};
@ -1546,10 +1378,15 @@ WriteDescriptors& WriteDescriptors::AddStorageImage(VulkanDescriptorSet* descrip
} }
WriteDescriptors& WriteDescriptors::AddCombinedImageSampler(VulkanDescriptorSet* descriptorSet, int binding, VulkanImageView* view, VulkanSampler* sampler, VkImageLayout imageLayout) WriteDescriptors& WriteDescriptors::AddCombinedImageSampler(VulkanDescriptorSet* descriptorSet, int binding, VulkanImageView* view, VulkanSampler* sampler, VkImageLayout imageLayout)
{
return AddCombinedImageSampler(descriptorSet, binding, 0, view, sampler, imageLayout);
}
WriteDescriptors& WriteDescriptors::AddCombinedImageSampler(VulkanDescriptorSet* descriptorSet, int binding, int arrayIndex, VulkanImageView* view, VulkanSampler* sampler, VkImageLayout imageLayout)
{ {
VkDescriptorImageInfo imageInfo = {}; VkDescriptorImageInfo imageInfo = {};
imageInfo.imageView = view->view; imageInfo.imageView = view->view;
imageInfo.sampler = sampler ? sampler->sampler : nullptr; imageInfo.sampler = sampler->sampler;
imageInfo.imageLayout = imageLayout; imageInfo.imageLayout = imageLayout;
auto extra = std::make_unique<WriteExtra>(); auto extra = std::make_unique<WriteExtra>();
@ -1559,7 +1396,7 @@ WriteDescriptors& WriteDescriptors::AddCombinedImageSampler(VulkanDescriptorSet*
descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrite.dstSet = descriptorSet->set; descriptorWrite.dstSet = descriptorSet->set;
descriptorWrite.dstBinding = binding; descriptorWrite.dstBinding = binding;
descriptorWrite.dstArrayElement = 0; descriptorWrite.dstArrayElement = arrayIndex;
descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptorWrite.descriptorCount = 1; descriptorWrite.descriptorCount = 1;
descriptorWrite.pImageInfo = &extra->imageInfo; descriptorWrite.pImageInfo = &extra->imageInfo;
@ -1591,5 +1428,6 @@ WriteDescriptors& WriteDescriptors::AddAccelerationStructure(VulkanDescriptorSet
void WriteDescriptors::Execute(VulkanDevice* device) void WriteDescriptors::Execute(VulkanDevice* device)
{ {
vkUpdateDescriptorSets(device->device, (uint32_t)writes.size(), writes.data(), 0, nullptr); if (!writes.empty())
vkUpdateDescriptorSets(device->device, (uint32_t)writes.size(), writes.data(), 0, nullptr);
} }

View file

@ -1,7 +1,6 @@
#pragma once #pragma once
#include "vulkanobjects.h" #include "vulkanobjects.h"
#include "framework/zstring.h"
#include <cassert> #include <cassert>
class ImageBuilder class ImageBuilder
@ -55,6 +54,7 @@ public:
SamplerBuilder& MagFilter(VkFilter magFilter); SamplerBuilder& MagFilter(VkFilter magFilter);
SamplerBuilder& MipmapMode(VkSamplerMipmapMode mode); SamplerBuilder& MipmapMode(VkSamplerMipmapMode mode);
SamplerBuilder& Anisotropy(float maxAnisotropy); SamplerBuilder& Anisotropy(float maxAnisotropy);
SamplerBuilder& MipLodBias(float bias);
SamplerBuilder& MaxLod(float value); SamplerBuilder& MaxLod(float value);
SamplerBuilder& DebugName(const char* name) { debugName = name; return *this; } SamplerBuilder& DebugName(const char* name) { debugName = name; return *this; }
@ -88,20 +88,17 @@ class ShaderBuilder
public: public:
ShaderBuilder(); ShaderBuilder();
ShaderBuilder& VertexShader(const FString &code); static void Init();
ShaderBuilder& FragmentShader(const FString &code); static void Deinit();
ShaderBuilder& RayGenShader(const FString& code);
ShaderBuilder& IntersectShader(const FString& code); ShaderBuilder& VertexShader(const std::string &code);
ShaderBuilder& AnyHitShader(const FString& code); ShaderBuilder& FragmentShader(const std::string&code);
ShaderBuilder& ClosestHitShader(const FString& code);
ShaderBuilder& MissShader(const FString& code);
ShaderBuilder& CallableShader(const FString& code);
ShaderBuilder& DebugName(const char* name) { debugName = name; return *this; } ShaderBuilder& DebugName(const char* name) { debugName = name; return *this; }
std::unique_ptr<VulkanShader> Create(const char *shadername, VulkanDevice *device); std::unique_ptr<VulkanShader> Create(const char *shadername, VulkanDevice *device);
private: private:
FString code; std::string code;
int stage = 0; int stage = 0;
const char* debugName = nullptr; const char* debugName = nullptr;
}; };
@ -123,29 +120,6 @@ private:
const char* debugName = nullptr; const char* debugName = nullptr;
}; };
class RayTracingPipelineBuilder
{
public:
RayTracingPipelineBuilder();
RayTracingPipelineBuilder& Layout(VulkanPipelineLayout* layout);
RayTracingPipelineBuilder& MaxPipelineRayRecursionDepth(int depth);
RayTracingPipelineBuilder& AddShader(VkShaderStageFlagBits stage, VulkanShader* shader);
RayTracingPipelineBuilder& AddRayGenGroup(int rayGenShader);
RayTracingPipelineBuilder& AddMissGroup(int missShader);
RayTracingPipelineBuilder& AddTrianglesHitGroup(int closestHitShader, int anyHitShader = VK_SHADER_UNUSED_KHR);
RayTracingPipelineBuilder& AddProceduralHitGroup(int intersectionShader, int closestHitShader, int anyHitShader);
RayTracingPipelineBuilder& DebugName(const char* name) { debugName = name; return *this; }
std::unique_ptr<VulkanPipeline> Create(VulkanDevice* device);
private:
VkRayTracingPipelineCreateInfoKHR pipelineInfo = {};
std::vector<VkPipelineShaderStageCreateInfo> stages;
std::vector<VkRayTracingShaderGroupCreateInfoKHR> groups;
const char* debugName = nullptr;
};
class ComputePipelineBuilder class ComputePipelineBuilder
{ {
public: public:
@ -168,14 +142,17 @@ class DescriptorSetLayoutBuilder
public: public:
DescriptorSetLayoutBuilder(); DescriptorSetLayoutBuilder();
DescriptorSetLayoutBuilder& AddBinding(int binding, VkDescriptorType type, int arrayCount, VkShaderStageFlags stageFlags); DescriptorSetLayoutBuilder& Flags(VkDescriptorSetLayoutCreateFlags flags);
DescriptorSetLayoutBuilder& AddBinding(int binding, VkDescriptorType type, int arrayCount, VkShaderStageFlags stageFlags, VkDescriptorBindingFlags flags = 0);
DescriptorSetLayoutBuilder& DebugName(const char* name) { debugName = name; return *this; } DescriptorSetLayoutBuilder& DebugName(const char* name) { debugName = name; return *this; }
std::unique_ptr<VulkanDescriptorSetLayout> Create(VulkanDevice *device); std::unique_ptr<VulkanDescriptorSetLayout> Create(VulkanDevice *device);
private: private:
VkDescriptorSetLayoutCreateInfo layoutInfo = {}; VkDescriptorSetLayoutCreateInfo layoutInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
TArray<VkDescriptorSetLayoutBinding> bindings; VkDescriptorSetLayoutBindingFlagsCreateInfoEXT bindingFlagsInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO_EXT };
std::vector<VkDescriptorSetLayoutBinding> bindings;
std::vector<VkDescriptorBindingFlags> bindingFlags;
const char* debugName = nullptr; const char* debugName = nullptr;
}; };
@ -184,6 +161,7 @@ class DescriptorPoolBuilder
public: public:
DescriptorPoolBuilder(); DescriptorPoolBuilder();
DescriptorPoolBuilder& Flags(VkDescriptorPoolCreateFlags flags);
DescriptorPoolBuilder& MaxSets(int value); DescriptorPoolBuilder& MaxSets(int value);
DescriptorPoolBuilder& AddPoolSize(VkDescriptorType type, int count); DescriptorPoolBuilder& AddPoolSize(VkDescriptorType type, int count);
DescriptorPoolBuilder& DebugName(const char* name) { debugName = name; return *this; } DescriptorPoolBuilder& DebugName(const char* name) { debugName = name; return *this; }
@ -388,9 +366,9 @@ class WriteDescriptors
public: public:
WriteDescriptors& AddBuffer(VulkanDescriptorSet *descriptorSet, int binding, VkDescriptorType type, VulkanBuffer *buffer); WriteDescriptors& AddBuffer(VulkanDescriptorSet *descriptorSet, int binding, VkDescriptorType type, VulkanBuffer *buffer);
WriteDescriptors& AddBuffer(VulkanDescriptorSet *descriptorSet, int binding, VkDescriptorType type, VulkanBuffer *buffer, size_t offset, size_t range); WriteDescriptors& AddBuffer(VulkanDescriptorSet *descriptorSet, int binding, VkDescriptorType type, VulkanBuffer *buffer, size_t offset, size_t range);
WriteDescriptors& AddSampledImage(VulkanDescriptorSet* descriptorSet, int binding, VulkanImageView* view, VkImageLayout imageLayout);
WriteDescriptors& AddStorageImage(VulkanDescriptorSet *descriptorSet, int binding, VulkanImageView *view, VkImageLayout imageLayout); WriteDescriptors& AddStorageImage(VulkanDescriptorSet *descriptorSet, int binding, VulkanImageView *view, VkImageLayout imageLayout);
WriteDescriptors& AddCombinedImageSampler(VulkanDescriptorSet *descriptorSet, int binding, VulkanImageView *view, VulkanSampler *sampler, VkImageLayout imageLayout); WriteDescriptors& AddCombinedImageSampler(VulkanDescriptorSet *descriptorSet, int binding, VulkanImageView *view, VulkanSampler *sampler, VkImageLayout imageLayout);
WriteDescriptors& AddCombinedImageSampler(VulkanDescriptorSet* descriptorSet, int binding, int arrayIndex, VulkanImageView* view, VulkanSampler* sampler, VkImageLayout imageLayout);
WriteDescriptors& AddAccelerationStructure(VulkanDescriptorSet* descriptorSet, int binding, VulkanAccelerationStructure* accelStruct); WriteDescriptors& AddAccelerationStructure(VulkanDescriptorSet* descriptorSet, int binding, VulkanAccelerationStructure* accelStruct);
void Execute(VulkanDevice *device); void Execute(VulkanDevice *device);

View file

@ -0,0 +1,145 @@
#include "vulkancompatibledevice.h"
#include "vulkansurface.h"
std::vector<VulkanCompatibleDevice> VulkanCompatibleDevice::FindDevices(const std::shared_ptr<VulkanInstance>& instance, const std::shared_ptr<VulkanSurface>& surface)
{
std::vector<std::string> RequiredDeviceExtensions =
{
VK_KHR_SWAPCHAIN_EXTENSION_NAME
};
std::vector<std::string> OptionalDeviceExtensions =
{
VK_EXT_HDR_METADATA_EXTENSION_NAME,
VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME,
VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME,
VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME,
VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME,
VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME,
VK_KHR_RAY_QUERY_EXTENSION_NAME,
VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME,
#if defined(VK_USE_PLATFORM_WIN32_KHR)
VK_EXT_FULL_SCREEN_EXCLUSIVE_EXTENSION_NAME
#endif
};
std::vector<VulkanCompatibleDevice> supportedDevices;
for (size_t idx = 0; idx < instance->PhysicalDevices.size(); idx++)
{
const auto& info = instance->PhysicalDevices[idx];
// Check if all required extensions are there
std::set<std::string> requiredExtensionSearch(RequiredDeviceExtensions.begin(), RequiredDeviceExtensions.end());
for (const auto& ext : info.Extensions)
requiredExtensionSearch.erase(ext.extensionName);
if (!requiredExtensionSearch.empty())
continue;
// Check if all required features are there
if (info.Features.Features.samplerAnisotropy != VK_TRUE ||
info.Features.Features.fragmentStoresAndAtomics != VK_TRUE)
continue;
VulkanCompatibleDevice dev;
dev.Device = &instance->PhysicalDevices[idx];
dev.EnabledDeviceExtensions = RequiredDeviceExtensions;
// Enable optional extensions we are interested in, if they are available on this device
for (const auto& ext : dev.Device->Extensions)
{
for (const auto& opt : OptionalDeviceExtensions)
{
if (ext.extensionName == opt)
{
dev.EnabledDeviceExtensions.push_back(opt);
}
}
}
// Enable optional features we are interested in, if they are available on this device
auto& enabledFeatures = dev.EnabledFeatures;
auto& deviceFeatures = dev.Device->Features;
enabledFeatures.Features.samplerAnisotropy = deviceFeatures.Features.samplerAnisotropy;
enabledFeatures.Features.fragmentStoresAndAtomics = deviceFeatures.Features.fragmentStoresAndAtomics;
enabledFeatures.Features.depthClamp = deviceFeatures.Features.depthClamp;
enabledFeatures.Features.shaderClipDistance = deviceFeatures.Features.shaderClipDistance;
enabledFeatures.BufferDeviceAddress.bufferDeviceAddress = deviceFeatures.BufferDeviceAddress.bufferDeviceAddress;
enabledFeatures.AccelerationStructure.accelerationStructure = deviceFeatures.AccelerationStructure.accelerationStructure;
enabledFeatures.RayQuery.rayQuery = deviceFeatures.RayQuery.rayQuery;
enabledFeatures.DescriptorIndexing.runtimeDescriptorArray = deviceFeatures.DescriptorIndexing.runtimeDescriptorArray;
enabledFeatures.DescriptorIndexing.descriptorBindingPartiallyBound = deviceFeatures.DescriptorIndexing.descriptorBindingPartiallyBound;
enabledFeatures.DescriptorIndexing.descriptorBindingSampledImageUpdateAfterBind = deviceFeatures.DescriptorIndexing.descriptorBindingSampledImageUpdateAfterBind;
enabledFeatures.DescriptorIndexing.descriptorBindingVariableDescriptorCount = deviceFeatures.DescriptorIndexing.descriptorBindingVariableDescriptorCount;
// Figure out which queue can present
if (surface)
{
for (int i = 0; i < (int)info.QueueFamilies.size(); i++)
{
VkBool32 presentSupport = false;
VkResult result = vkGetPhysicalDeviceSurfaceSupportKHR(info.Device, i, surface->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;
dev.GraphicsTimeQueries = queueFamily.timestampValidBits != 0;
break;
}
}
// Only use device if we found the required graphics and present queues
if (dev.GraphicsFamily != -1 && (!surface || dev.PresentFamily != -1))
{
supportedDevices.push_back(dev);
}
}
// 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;
});
return supportedDevices;
}
VulkanCompatibleDevice VulkanCompatibleDevice::SelectDevice(const std::shared_ptr<VulkanInstance>& instance, const std::shared_ptr<VulkanSurface>& surface, int vk_device)
{
if (instance->PhysicalDevices.empty())
VulkanError("No Vulkan devices found. The graphics card may have no vulkan support or the driver may be too old.");
std::vector<VulkanCompatibleDevice> supportedDevices = FindDevices(instance, surface);
if (supportedDevices.empty())
VulkanError("No Vulkan device found supports the minimum requirements of this application");
size_t selected = vk_device;
if (selected >= supportedDevices.size())
selected = 0;
return supportedDevices[selected];
}

View file

@ -0,0 +1,22 @@
#pragma once
#include "VulkanInstance.h"
class VulkanSurface;
class VulkanCompatibleDevice
{
public:
VulkanPhysicalDevice* Device = nullptr;
int GraphicsFamily = -1;
int PresentFamily = -1;
bool GraphicsTimeQueries = false;
std::vector<std::string> EnabledDeviceExtensions;
VulkanDeviceFeatures EnabledFeatures;
static std::vector<VulkanCompatibleDevice> FindDevices(const std::shared_ptr<VulkanInstance>& instance, const std::shared_ptr<VulkanSurface>& surface);
static VulkanCompatibleDevice SelectDevice(const std::shared_ptr<VulkanInstance>& instance, const std::shared_ptr<VulkanSurface>& surface, int vk_device);
};

View file

@ -1,194 +1,69 @@
#include "vulkandevice.h" #include "vulkandevice.h"
#include "vulkanobjects.h" #include "vulkanobjects.h"
#include "stacktrace.h" #include "vulkancompatibledevice.h"
#include <algorithm> #include <algorithm>
#include <set> #include <set>
#include <string> #include <string>
#include <mutex>
VulkanDevice::VulkanDevice(int vk_device, bool vk_debug) : vk_device(vk_device), vk_debug(vk_debug) VulkanDevice::VulkanDevice(std::shared_ptr<VulkanInstance> instance, std::shared_ptr<VulkanSurface> surface, const VulkanCompatibleDevice& selectedDevice) : Instance(instance), Surface(surface)
{ {
#ifdef WIN32 PhysicalDevice = *selectedDevice.Device;
if (HMODULE mod = GetModuleHandle(TEXT("renderdoc.dll"))) EnabledDeviceExtensions = selectedDevice.EnabledDeviceExtensions;
{ EnabledFeatures = selectedDevice.EnabledFeatures;
pRENDERDOC_GetAPI RENDERDOC_GetAPI = (pRENDERDOC_GetAPI)GetProcAddress(mod, "RENDERDOC_GetAPI");
int ret = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_4_2, (void**)&renderdoc); GraphicsFamily = selectedDevice.GraphicsFamily;
if (ret != 1) PresentFamily = selectedDevice.PresentFamily;
renderdoc = nullptr; GraphicsTimeQueries = selectedDevice.GraphicsTimeQueries;
}
#endif
try try
{ {
ShInitialize(); CreateDevice();
initVolk(); CreateAllocator();
createInstance();
selectPhysicalDevice();
selectFeatures();
createDevice();
createAllocator();
} }
catch (...) catch (...)
{ {
releaseResources(); ReleaseResources();
throw; throw;
} }
} }
VulkanDevice::~VulkanDevice() VulkanDevice::~VulkanDevice()
{ {
releaseResources(); ReleaseResources();
} }
void VulkanDevice::selectFeatures() bool VulkanDevice::SupportsDeviceExtension(const char* ext) const
{ {
enabledDeviceFeatures.samplerAnisotropy = physicalDevice.features.samplerAnisotropy; return std::find(EnabledDeviceExtensions.begin(), EnabledDeviceExtensions.end(), ext) != EnabledDeviceExtensions.end();
enabledDeviceFeatures.fragmentStoresAndAtomics = physicalDevice.features.fragmentStoresAndAtomics;
enabledDeviceFeatures.depthClamp = physicalDevice.features.depthClamp;
enabledDeviceFeatures.shaderClipDistance = physicalDevice.features.shaderClipDistance;
} }
bool VulkanDevice::checkRequiredFeatures(const VkPhysicalDeviceFeatures &f) void VulkanDevice::CreateAllocator()
{
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 = {}; VmaAllocatorCreateInfo allocinfo = {};
allocinfo.vulkanApiVersion = ApiVersion; allocinfo.vulkanApiVersion = Instance->ApiVersion;
if (supportsDeviceExtension(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME) && supportsDeviceExtension(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME)) 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.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT;
allocinfo.flags |= VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT; if (SupportsDeviceExtension(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME))
allocinfo.physicalDevice = physicalDevice.device; allocinfo.flags |= VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT;
allocinfo.physicalDevice = PhysicalDevice.Device;
allocinfo.device = device; allocinfo.device = device;
allocinfo.instance = Instance->Instance;
allocinfo.preferredLargeHeapBlockSize = 64 * 1024 * 1024; allocinfo.preferredLargeHeapBlockSize = 64 * 1024 * 1024;
allocinfo.instance = instance;
if (vmaCreateAllocator(&allocinfo, &allocator) != VK_SUCCESS) if (vmaCreateAllocator(&allocinfo, &allocator) != VK_SUCCESS)
throw std::runtime_error("Unable to create allocator"); VulkanError("Unable to create allocator");
} }
void VulkanDevice::createDevice() void VulkanDevice::CreateDevice()
{ {
float queuePriority = 1.0f; float queuePriority = 1.0f;
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos; std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
std::set<int> neededFamilies; std::set<int> neededFamilies;
neededFamilies.insert(graphicsFamily); if (GraphicsFamily != -1)
if (presentFamily != -1) neededFamilies.insert(GraphicsFamily);
neededFamilies.insert(presentFamily); if (PresentFamily != -1)
neededFamilies.insert(PresentFamily);
for (int index : neededFamilies) for (int index : neededFamilies)
{ {
@ -200,359 +75,85 @@ void VulkanDevice::createDevice()
queueCreateInfos.push_back(queueCreateInfo); queueCreateInfos.push_back(queueCreateInfo);
} }
VkPhysicalDeviceRayQueryFeaturesKHR rayqueryFeatures = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_QUERY_FEATURES_KHR }; std::vector<const char*> extensionNames;
rayqueryFeatures.rayQuery = true; extensionNames.reserve(EnabledDeviceExtensions.size());
for (const auto& name : EnabledDeviceExtensions)
extensionNames.push_back(name.c_str());
VkPhysicalDeviceRayTracingPipelineFeaturesKHR raytracingFeatures = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR }; VkDeviceCreateInfo deviceCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO };
raytracingFeatures.rayTracingPipeline = true;
raytracingFeatures.rayTraversalPrimitiveCulling = true;
raytracingFeatures.pNext = &rayqueryFeatures;
VkPhysicalDeviceAccelerationStructureFeaturesKHR deviceAccelFeatures = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR };
deviceAccelFeatures.accelerationStructure = true;
deviceAccelFeatures.pNext = &raytracingFeatures;
VkPhysicalDeviceBufferDeviceAddressFeatures deviceAddressFeatures = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES };
deviceAddressFeatures.bufferDeviceAddress = true;
deviceAddressFeatures.pNext = &deviceAccelFeatures;
VkPhysicalDeviceFeatures2 deviceFeatures2 = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2 };
deviceFeatures2.features = enabledDeviceFeatures;
deviceFeatures2.pNext = &deviceAddressFeatures;
VkDeviceCreateInfo deviceCreateInfo = {};
deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
deviceCreateInfo.pNext = &deviceFeatures2;
deviceCreateInfo.queueCreateInfoCount = (uint32_t)queueCreateInfos.size(); deviceCreateInfo.queueCreateInfoCount = (uint32_t)queueCreateInfos.size();
deviceCreateInfo.pQueueCreateInfos = queueCreateInfos.data(); deviceCreateInfo.pQueueCreateInfos = queueCreateInfos.data();
//deviceCreateInfo.pEnabledFeatures = &enabledDeviceFeatures; deviceCreateInfo.enabledExtensionCount = (uint32_t)extensionNames.size();
deviceCreateInfo.enabledExtensionCount = (uint32_t)enabledDeviceExtensions.size(); deviceCreateInfo.ppEnabledExtensionNames = extensionNames.data();
deviceCreateInfo.ppEnabledExtensionNames = enabledDeviceExtensions.data();
deviceCreateInfo.enabledLayerCount = 0; deviceCreateInfo.enabledLayerCount = 0;
VkResult result = vkCreateDevice(physicalDevice.device, &deviceCreateInfo, nullptr, &device); VkPhysicalDeviceFeatures2 deviceFeatures2 = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2 };
if (result != VK_SUCCESS) deviceFeatures2.features = EnabledFeatures.Features;
throw std::runtime_error("Could not create vulkan device");
void** next = const_cast<void**>(&deviceCreateInfo.pNext);
if (SupportsDeviceExtension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME))
{
*next = &deviceFeatures2;
next = &deviceFeatures2.pNext;
}
else // vulkan 1.0 specified features in a different way
{
deviceCreateInfo.pEnabledFeatures = &deviceFeatures2.features;
}
if (SupportsDeviceExtension(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME))
{
*next = &EnabledFeatures.BufferDeviceAddress;
next = &EnabledFeatures.BufferDeviceAddress.pNext;
}
if (SupportsDeviceExtension(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME))
{
*next = &EnabledFeatures.AccelerationStructure;
next = &EnabledFeatures.AccelerationStructure.pNext;
}
if (SupportsDeviceExtension(VK_KHR_RAY_QUERY_EXTENSION_NAME))
{
*next = &EnabledFeatures.RayQuery;
next = &EnabledFeatures.RayQuery.pNext;
}
if (SupportsDeviceExtension(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME))
{
*next = &EnabledFeatures.DescriptorIndexing;
next = &EnabledFeatures.DescriptorIndexing.pNext;
}
VkResult result = vkCreateDevice(PhysicalDevice.Device, &deviceCreateInfo, nullptr, &device);
CheckVulkanError(result, "Could not create vulkan device");
volkLoadDevice(device); volkLoadDevice(device);
vkGetDeviceQueue(device, graphicsFamily, 0, &graphicsQueue); if (GraphicsFamily != -1)
if (presentFamily != -1) vkGetDeviceQueue(device, GraphicsFamily, 0, &GraphicsQueue);
vkGetDeviceQueue(device, presentFamily, 0, &presentQueue); if (PresentFamily != -1)
vkGetDeviceQueue(device, PresentFamily, 0, &PresentQueue);
} }
/* void VulkanDevice::ReleaseResources()
#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_KHRONOS_validation";
bool wantDebugLayer = vk_debug;
bool debugLayerFound = false;
if (wantDebugLayer)
{
for (const VkLayerProperties& layer : availableLayers)
{
if (layer.layerName == debugLayer)
{
enabledValidationLayers.push_back(layer.layerName);
enabledExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
debugLayerFound = true;
break;
}
}
}
// 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 = "ZDRay";
appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.pEngineName = "ZDRay";
appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.apiVersion = ApiVersion;
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";
}
printf("\n[%s] %s\n", typestr, msg.c_str());
std::string callstack = CaptureStackTraceText(0);
if (!callstack.empty())
printf("%s\n", callstack.c_str());
}
}
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 = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2 };
VkPhysicalDeviceRayTracingPipelinePropertiesKHR rayprops = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_PROPERTIES_KHR };
props.pNext = &rayprops;
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) if (device)
vkDeviceWaitIdle(device); vkDeviceWaitIdle(device);
if (allocator) if (allocator)
vmaDestroyAllocator(allocator); vmaDestroyAllocator(allocator);
allocator = VK_NULL_HANDLE;
if (device) if (device)
vkDestroyDevice(device, nullptr); vkDestroyDevice(device, nullptr);
device = VK_NULL_HANDLE; device = nullptr;
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;
ShFinalize();
} }
uint32_t VulkanDevice::findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) void VulkanDevice::SetObjectName(const char* name, uint64_t handle, VkObjectType type)
{ {
for (uint32_t i = 0; i < physicalDevice.memoryProperties.memoryTypeCount; i++) if (!DebugLayerActive) return;
{
if ((typeFilter & (1 << i)) && (physicalDevice.memoryProperties.memoryTypes[i].propertyFlags & properties) == properties)
return i;
}
throw std::runtime_error("failed to find suitable memory type!"); 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);
} }

View file

@ -1,136 +1,52 @@
#pragma once #pragma once
#include "vulkaninstance.h"
#include <functional> #include <functional>
#include <mutex>
#ifdef WIN32 #include <vector>
#define VK_USE_PLATFORM_WIN32_KHR #include <algorithm>
#endif #include <memory>
#include "volk/volk.h"
#include "vk_mem_alloc/vk_mem_alloc.h"
#include "renderdoc_app.h"
#ifdef WIN32
#undef min
#undef max
#endif
#include "ShaderCompiler/glslang/Public/ShaderLang.h"
#include "ShaderCompiler/spirv/GlslangToSpv.h"
class VulkanSwapChain; class VulkanSwapChain;
class VulkanSemaphore; class VulkanSemaphore;
class VulkanFence; class VulkanFence;
class VulkanPhysicalDevice;
class VulkanPhysicalDevice class VulkanSurface;
{ class VulkanCompatibleDevice;
public:
VkPhysicalDevice device = VK_NULL_HANDLE;
std::vector<VkExtensionProperties> extensions;
std::vector<VkQueueFamilyProperties> queueFamilies;
VkPhysicalDeviceProperties properties = {};
VkPhysicalDeviceRayTracingPipelinePropertiesKHR rayTracingProperties = {};
VkPhysicalDeviceFeatures features = {};
VkPhysicalDeviceMemoryProperties memoryProperties = {};
};
class VulkanCompatibleDevice
{
public:
VulkanPhysicalDevice *device = nullptr;
int graphicsFamily = -1;
int presentFamily = -1;
};
class VulkanDevice class VulkanDevice
{ {
public: public:
VulkanDevice(int vk_device = 0, bool vk_debug = false); VulkanDevice(std::shared_ptr<VulkanInstance> instance, std::shared_ptr<VulkanSurface> surface, const VulkanCompatibleDevice& selectedDevice);
~VulkanDevice(); ~VulkanDevice();
void setDebugObjectName(const char *name, uint64_t handle, VkObjectType type) std::vector<std::string> EnabledDeviceExtensions;
{ VulkanDeviceFeatures EnabledFeatures;
if (!debugLayerActive) return;
VkDebugUtilsObjectNameInfoEXT info = {}; VulkanPhysicalDevice PhysicalDevice;
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); std::shared_ptr<VulkanInstance> Instance;
std::shared_ptr<VulkanSurface> Surface;
// Instance setup
std::vector<VkLayerProperties> availableLayers;
std::vector<VkExtensionProperties> extensions;
std::vector<const char *> enabledExtensions;
std::vector<const char *> optionalExtensions = { };
std::vector<const char*> enabledValidationLayers;
// Device setup
VkPhysicalDeviceFeatures enabledDeviceFeatures = {};
std::vector<const char *> enabledDeviceExtensions = {
//VK_KHR_SWAPCHAIN_EXTENSION_NAME,
VK_KHR_RAY_QUERY_EXTENSION_NAME,
VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME,
VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME,
VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME
};
std::vector<const char *> optionalDeviceExtensions = {
//VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME,
//VK_EXT_HDR_METADATA_EXTENSION_NAME,
VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME,
VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME
};
VulkanPhysicalDevice physicalDevice;
bool debugLayerActive = false;
VkInstance instance = VK_NULL_HANDLE;
VkSurfaceKHR surface = VK_NULL_HANDLE;
VkDevice device = VK_NULL_HANDLE; VkDevice device = VK_NULL_HANDLE;
VmaAllocator allocator = VK_NULL_HANDLE; VmaAllocator allocator = VK_NULL_HANDLE;
VkQueue graphicsQueue = VK_NULL_HANDLE; VkQueue GraphicsQueue = VK_NULL_HANDLE;
VkQueue presentQueue = VK_NULL_HANDLE; VkQueue PresentQueue = VK_NULL_HANDLE;
int graphicsFamily = -1; int GraphicsFamily = -1;
int presentFamily = -1; int PresentFamily = -1;
bool GraphicsTimeQueries = false;
// Physical device info bool SupportsDeviceExtension(const char* ext) const;
std::vector<VulkanPhysicalDevice> availableDevices;
std::vector<VulkanCompatibleDevice> supportedDevices;
uint32_t ApiVersion = VK_API_VERSION_1_2; void SetObjectName(const char* name, uint64_t handle, VkObjectType type);
RENDERDOC_API_1_4_2* renderdoc = nullptr;
static void initVolk();
private: private:
int vk_device; bool DebugLayerActive = false;
bool vk_debug;
void createInstance(); void CreateDevice();
//void createSurface(); void CreateAllocator();
void selectPhysicalDevice(); void ReleaseResources();
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);
}; };

View file

@ -0,0 +1,378 @@
#include "vulkaninstance.h"
#include "vulkanbuilders.h"
#include <mutex>
VulkanInstance::VulkanInstance(bool wantDebugLayer) : WantDebugLayer(wantDebugLayer)
{
try
{
ShaderBuilder::Init();
InitVolk();
CreateInstance();
}
catch (...)
{
ReleaseResources();
throw;
}
}
VulkanInstance::~VulkanInstance()
{
ReleaseResources();
}
void VulkanInstance::ReleaseResources()
{
if (debugMessenger)
vkDestroyDebugUtilsMessengerEXT(Instance, debugMessenger, nullptr);
debugMessenger = VK_NULL_HANDLE;
if (Instance)
vkDestroyInstance(Instance, nullptr);
Instance = nullptr;
}
void VulkanInstance::InitVolk()
{
if (volkInitialize() != VK_SUCCESS)
{
VulkanError("Unable to find Vulkan");
}
auto iver = volkGetInstanceVersion();
if (iver == 0)
{
VulkanError("Vulkan not supported");
}
}
void VulkanInstance::CreateInstance()
{
AvailableLayers = GetAvailableLayers();
AvailableExtensions = GetExtensions();
EnabledExtensions = RequiredExtensions;
std::string debugLayer = "VK_LAYER_KHRONOS_validation";
bool debugLayerFound = false;
if (WantDebugLayer)
{
for (const VkLayerProperties& layer : AvailableLayers)
{
if (layer.layerName == debugLayer)
{
EnabledValidationLayers.push_back(layer.layerName);
EnabledExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
debugLayerFound = true;
break;
}
}
}
// Enable optional instance extensions we are interested in
for (const auto& ext : AvailableExtensions)
{
for (const auto& opt : OptionalExtensions)
{
if (strcmp(ext.extensionName, opt) == 0)
{
EnabledExtensions.push_back(opt);
}
}
}
// Try get the highest vulkan version we can get
VkResult result = VK_ERROR_INITIALIZATION_FAILED;
for (uint32_t apiVersion : ApiVersionsToTry)
{
VkApplicationInfo appInfo = {};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pApplicationName = "VulkanDrv";
appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.pEngineName = "VulkanDrv";
appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.apiVersion = apiVersion;
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();
result = vkCreateInstance(&createInfo, nullptr, &Instance);
if (result >= VK_SUCCESS)
{
ApiVersion = apiVersion;
break;
}
}
CheckVulkanError(result, "Could not create vulkan instance");
volkLoadInstance(Instance);
if (debugLayerFound)
{
VkDebugUtilsMessengerCreateInfoEXT dbgCreateInfo = {};
dbgCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
dbgCreateInfo.messageSeverity =
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
dbgCreateInfo.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;
dbgCreateInfo.pfnUserCallback = DebugCallback;
dbgCreateInfo.pUserData = this;
result = vkCreateDebugUtilsMessengerEXT(Instance, &dbgCreateInfo, nullptr, &debugMessenger);
CheckVulkanError(result, "vkCreateDebugUtilsMessengerEXT failed");
DebugLayerActive = true;
}
PhysicalDevices = GetPhysicalDevices(Instance, ApiVersion);
}
std::vector<VulkanPhysicalDevice> VulkanInstance::GetPhysicalDevices(VkInstance instance, uint32_t apiVersion)
{
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 {};
CheckVulkanError(result, "vkEnumeratePhysicalDevices failed");
if (deviceCount == 0)
return {};
std::vector<VkPhysicalDevice> devices(deviceCount);
result = vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
CheckVulkanError(result, "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];
vkGetPhysicalDeviceMemoryProperties(dev.Device, &dev.MemoryProperties);
vkGetPhysicalDeviceProperties(dev.Device, &dev.Properties);
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());
auto checkForExtension = [&](const char* name)
{
for (const auto& ext : dev.Extensions)
{
if (strcmp(ext.extensionName, name) == 0)
return true;
}
return false;
};
if (apiVersion != VK_API_VERSION_1_0)
{
VkPhysicalDeviceFeatures2 deviceFeatures2 = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2 };
void** next = const_cast<void**>(&deviceFeatures2.pNext);
if (checkForExtension(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME))
{
*next = &dev.Features.BufferDeviceAddress;
next = &dev.Features.BufferDeviceAddress.pNext;
}
if (checkForExtension(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME))
{
*next = &dev.Features.AccelerationStructure;
next = &dev.Features.AccelerationStructure.pNext;
}
if (checkForExtension(VK_KHR_RAY_QUERY_EXTENSION_NAME))
{
*next = &dev.Features.RayQuery;
next = &dev.Features.RayQuery.pNext;
}
if (checkForExtension(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME))
{
*next = &dev.Features.DescriptorIndexing;
next = &dev.Features.DescriptorIndexing.pNext;
}
vkGetPhysicalDeviceFeatures2(dev.Device, &deviceFeatures2);
dev.Features.Features = deviceFeatures2.features;
dev.Features.BufferDeviceAddress.pNext = nullptr;
dev.Features.AccelerationStructure.pNext = nullptr;
dev.Features.RayQuery.pNext = nullptr;
dev.Features.DescriptorIndexing.pNext = nullptr;
}
else
{
vkGetPhysicalDeviceFeatures(dev.Device, &dev.Features.Features);
}
}
return devinfo;
}
std::vector<VkLayerProperties> VulkanInstance::GetAvailableLayers()
{
uint32_t layerCount;
VkResult result = vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
std::vector<VkLayerProperties> availableLayers(layerCount);
result = vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
return availableLayers;
}
std::vector<VkExtensionProperties> VulkanInstance::GetExtensions()
{
uint32_t extensionCount = 0;
VkResult result = vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);
std::vector<VkExtensionProperties> extensions(extensionCount);
result = vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data());
return extensions;
}
VkBool32 VulkanInstance::DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* callbackData, void* userData)
{
VulkanInstance* instance = (VulkanInstance*)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;
// Attempt to parse the string because the default formatting is totally unreadable and half of what it writes is totally useless!
auto parts = SplitString(msg, " | ");
if (parts.size() == 3)
{
msg = parts[2];
size_t pos = msg.find(" The Vulkan spec states:");
if (pos != std::string::npos)
msg = msg.substr(0, pos);
if (callbackData->objectCount > 0)
{
msg += " (";
for (uint32_t i = 0; i < callbackData->objectCount; i++)
{
if (i > 0)
msg += ", ";
if (callbackData->pObjects[i].pObjectName)
msg += callbackData->pObjects[i].pObjectName;
else
msg += "<noname>";
}
msg += ")";
}
}
bool found = seenMessages.find(msg) != seenMessages.end();
if (!found)
{
if (totalMessages < 20)
{
totalMessages++;
seenMessages.insert(msg);
const char* typestr;
bool showcallstack = false;
if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT)
{
typestr = "vulkan error";
showcallstack = true;
}
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";
}
VulkanPrintLog(typestr, msg);
}
}
return VK_FALSE;
}
std::vector<std::string> VulkanInstance::SplitString(const std::string& s, const std::string& seperator)
{
std::vector<std::string> output;
std::string::size_type prev_pos = 0, pos = 0;
while ((pos = s.find(seperator, pos)) != std::string::npos)
{
std::string substring(s.substr(prev_pos, pos - prev_pos));
output.push_back(substring);
pos += seperator.length();
prev_pos = pos;
}
output.push_back(s.substr(prev_pos, pos - prev_pos)); // Last word
return output;
}
std::string VkResultToString(VkResult result)
{
switch (result)
{
case VK_SUCCESS: return "success";
case VK_NOT_READY: return "not ready";
case VK_TIMEOUT: return "timeout";
case VK_EVENT_SET: return "event set";
case VK_EVENT_RESET: return "event reset";
case VK_INCOMPLETE: return "incomplete";
case VK_ERROR_OUT_OF_HOST_MEMORY: return "out of host memory";
case VK_ERROR_OUT_OF_DEVICE_MEMORY: return "out of device memory";
case VK_ERROR_INITIALIZATION_FAILED: return "initialization failed";
case VK_ERROR_DEVICE_LOST: return "device lost";
case VK_ERROR_MEMORY_MAP_FAILED: return "memory map failed";
case VK_ERROR_LAYER_NOT_PRESENT: return "layer not present";
case VK_ERROR_EXTENSION_NOT_PRESENT: return "extension not present";
case VK_ERROR_FEATURE_NOT_PRESENT: return "feature not present";
case VK_ERROR_INCOMPATIBLE_DRIVER: return "incompatible driver";
case VK_ERROR_TOO_MANY_OBJECTS: return "too many objects";
case VK_ERROR_FORMAT_NOT_SUPPORTED: return "format not supported";
case VK_ERROR_FRAGMENTED_POOL: return "fragmented pool";
case VK_ERROR_OUT_OF_POOL_MEMORY: return "out of pool memory";
case VK_ERROR_INVALID_EXTERNAL_HANDLE: return "invalid external handle";
case VK_ERROR_SURFACE_LOST_KHR: return "surface lost";
case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR: return "native window in use";
case VK_SUBOPTIMAL_KHR: return "suboptimal";
case VK_ERROR_OUT_OF_DATE_KHR: return "out of date";
case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR: return "incompatible display";
case VK_ERROR_VALIDATION_FAILED_EXT: return "validation failed";
case VK_ERROR_INVALID_SHADER_NV: return "invalid shader";
case VK_ERROR_FRAGMENTATION_EXT: return "fragmentation";
case VK_ERROR_NOT_PERMITTED_EXT: return "not permitted";
case VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT: return "full screen exclusive mode lost";
case VK_THREAD_IDLE_KHR: return "thread idle";
case VK_THREAD_DONE_KHR: return "thread done";
case VK_OPERATION_DEFERRED_KHR: return "operation deferred";
case VK_OPERATION_NOT_DEFERRED_KHR: return "operation not deferred";
case VK_PIPELINE_COMPILE_REQUIRED_EXT: return "pipeline compile required";
default: break;
}
return "vkResult " + std::to_string((int)result);
}

View file

@ -0,0 +1,106 @@
#pragma once
#if defined(_WIN32)
#define VK_USE_PLATFORM_WIN32_KHR
#elif defined(__APPLE__)
#define VK_USE_PLATFORM_MACOS_MVK
#define VK_USE_PLATFORM_METAL_EXT
#else
#define VK_USE_PLATFORM_XLIB_KHR
#endif
#include "volk/volk.h"
#include "vk_mem_alloc/vk_mem_alloc.h"
#undef min
#undef max
#include "ShaderCompiler/glslang/Public/ShaderLang.h"
#include "ShaderCompiler/spirv/GlslangToSpv.h"
#include <functional>
#include <memory>
class VulkanDeviceFeatures
{
public:
VkPhysicalDeviceFeatures Features = {};
VkPhysicalDeviceBufferDeviceAddressFeatures BufferDeviceAddress = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES };
VkPhysicalDeviceAccelerationStructureFeaturesKHR AccelerationStructure = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR };
VkPhysicalDeviceRayQueryFeaturesKHR RayQuery = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_QUERY_FEATURES_KHR };
VkPhysicalDeviceDescriptorIndexingFeatures DescriptorIndexing = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT };
};
class VulkanPhysicalDevice
{
public:
VkPhysicalDevice Device = VK_NULL_HANDLE;
std::vector<VkExtensionProperties> Extensions;
std::vector<VkQueueFamilyProperties> QueueFamilies;
VkPhysicalDeviceProperties Properties = {};
VkPhysicalDeviceMemoryProperties MemoryProperties = {};
VulkanDeviceFeatures Features;
};
class VulkanInstance
{
public:
VulkanInstance(bool wantDebugLayer);
~VulkanInstance();
std::vector<const char*> RequiredExtensions =
{
VK_KHR_SURFACE_EXTENSION_NAME,
#if defined(VK_USE_PLATFORM_WIN32_KHR)
VK_KHR_WIN32_SURFACE_EXTENSION_NAME
#elif defined(VK_USE_PLATFORM_MACOS_MVK)
VK_MVK_MACOS_SURFACE_EXTENSION_NAME
#elif defined(VK_USE_PLATFORM_XLIB_KHR)
VK_KHR_XLIB_SURFACE_EXTENSION_NAME
#endif
};
std::vector<const char*> OptionalExtensions =
{
VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME,
VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME,
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME
};
std::vector<uint32_t> ApiVersionsToTry = { VK_API_VERSION_1_2, VK_API_VERSION_1_1, VK_API_VERSION_1_0 };
std::vector<VkLayerProperties> AvailableLayers;
std::vector<VkExtensionProperties> AvailableExtensions;
std::vector<const char*> EnabledValidationLayers;
std::vector<const char*> EnabledExtensions;
std::vector<VulkanPhysicalDevice> PhysicalDevices;
uint32_t ApiVersion = {};
VkInstance Instance = VK_NULL_HANDLE;
bool DebugLayerActive = false;
private:
bool WantDebugLayer = false;
VkDebugUtilsMessengerEXT debugMessenger = VK_NULL_HANDLE;
void CreateInstance();
void ReleaseResources();
static void InitVolk();
static std::vector<VkLayerProperties> GetAvailableLayers();
static std::vector<VkExtensionProperties> GetExtensions();
static std::vector<VulkanPhysicalDevice> GetPhysicalDevices(VkInstance instance, uint32_t apiVersion);
static VKAPI_ATTR VkBool32 VKAPI_CALL DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData);
static std::vector<std::string> SplitString(const std::string& s, const std::string& seperator);
};
std::string VkResultToString(VkResult result);
void VulkanPrintLog(const char* typestr, const std::string& msg);
void VulkanError(const char* text);
inline void CheckVulkanError(VkResult result, const char* text)
{
if (result >= VK_SUCCESS) return;
VulkanError((text + std::string(": ") + VkResultToString(result)).c_str());
}

View file

@ -2,9 +2,6 @@
#include "vulkandevice.h" #include "vulkandevice.h"
#include <memory>
#include <stdexcept>
class VulkanCommandPool; class VulkanCommandPool;
class VulkanDescriptorPool; class VulkanDescriptorPool;
class VulkanCommandBuffer; class VulkanCommandBuffer;
@ -15,7 +12,7 @@ public:
VulkanSemaphore(VulkanDevice *device); VulkanSemaphore(VulkanDevice *device);
~VulkanSemaphore(); ~VulkanSemaphore();
void SetDebugName(const char *name) { device->setDebugObjectName(name, (uint64_t)semaphore, VK_OBJECT_TYPE_SEMAPHORE); } void SetDebugName(const char *name) { device->SetObjectName(name, (uint64_t)semaphore, VK_OBJECT_TYPE_SEMAPHORE); }
VulkanDevice *device = nullptr; VulkanDevice *device = nullptr;
VkSemaphore semaphore = VK_NULL_HANDLE; VkSemaphore semaphore = VK_NULL_HANDLE;
@ -31,7 +28,7 @@ public:
VulkanFence(VulkanDevice *device); VulkanFence(VulkanDevice *device);
~VulkanFence(); ~VulkanFence();
void SetDebugName(const char *name) { device->setDebugObjectName(name, (uint64_t)fence, VK_OBJECT_TYPE_FENCE); } void SetDebugName(const char *name) { device->SetObjectName(name, (uint64_t)fence, VK_OBJECT_TYPE_FENCE); }
VulkanDevice *device = nullptr; VulkanDevice *device = nullptr;
VkFence fence = VK_NULL_HANDLE; VkFence fence = VK_NULL_HANDLE;
@ -54,7 +51,12 @@ public:
return vkGetBufferDeviceAddress(device->device, &info); return vkGetBufferDeviceAddress(device->device, &info);
} }
void SetDebugName(const char *name) { device->setDebugObjectName(name, (uint64_t)buffer, VK_OBJECT_TYPE_BUFFER); } #ifdef _DEBUG
void SetDebugName(const char* name) { debugName = name; device->SetObjectName(name, (uint64_t)buffer, VK_OBJECT_TYPE_BUFFER); }
std::string debugName;
#else
void SetDebugName(const char* name) { device->SetObjectName(name, (uint64_t)buffer, VK_OBJECT_TYPE_BUFFER); }
#endif
VulkanDevice *device = nullptr; VulkanDevice *device = nullptr;
@ -76,7 +78,7 @@ public:
VulkanFramebuffer(VulkanDevice *device, VkFramebuffer framebuffer); VulkanFramebuffer(VulkanDevice *device, VkFramebuffer framebuffer);
~VulkanFramebuffer(); ~VulkanFramebuffer();
void SetDebugName(const char *name) { device->setDebugObjectName(name, (uint64_t)framebuffer, VK_OBJECT_TYPE_FRAMEBUFFER); } void SetDebugName(const char *name) { device->SetObjectName(name, (uint64_t)framebuffer, VK_OBJECT_TYPE_FRAMEBUFFER); }
VulkanDevice *device; VulkanDevice *device;
VkFramebuffer framebuffer; VkFramebuffer framebuffer;
@ -92,7 +94,7 @@ public:
VulkanImage(VulkanDevice *device, VkImage image, VmaAllocation allocation, int width, int height, int mipLevels, int layerCount); VulkanImage(VulkanDevice *device, VkImage image, VmaAllocation allocation, int width, int height, int mipLevels, int layerCount);
~VulkanImage(); ~VulkanImage();
void SetDebugName(const char *name) { device->setDebugObjectName(name, (uint64_t)image, VK_OBJECT_TYPE_IMAGE); } void SetDebugName(const char *name) { device->SetObjectName(name, (uint64_t)image, VK_OBJECT_TYPE_IMAGE); }
VkImage image = VK_NULL_HANDLE; VkImage image = VK_NULL_HANDLE;
int width = 0; int width = 0;
@ -117,7 +119,7 @@ public:
VulkanImageView(VulkanDevice *device, VkImageView view); VulkanImageView(VulkanDevice *device, VkImageView view);
~VulkanImageView(); ~VulkanImageView();
void SetDebugName(const char *name) { device->setDebugObjectName(name, (uint64_t)view, VK_OBJECT_TYPE_IMAGE_VIEW); } void SetDebugName(const char *name) { device->SetObjectName(name, (uint64_t)view, VK_OBJECT_TYPE_IMAGE_VIEW); }
VkImageView view = VK_NULL_HANDLE; VkImageView view = VK_NULL_HANDLE;
@ -134,7 +136,7 @@ public:
VulkanSampler(VulkanDevice *device, VkSampler sampler); VulkanSampler(VulkanDevice *device, VkSampler sampler);
~VulkanSampler(); ~VulkanSampler();
void SetDebugName(const char *name) { device->setDebugObjectName(name, (uint64_t)sampler, VK_OBJECT_TYPE_SAMPLER); } void SetDebugName(const char *name) { device->SetObjectName(name, (uint64_t)sampler, VK_OBJECT_TYPE_SAMPLER); }
VkSampler sampler = VK_NULL_HANDLE; VkSampler sampler = VK_NULL_HANDLE;
@ -151,7 +153,7 @@ public:
VulkanShader(VulkanDevice *device, VkShaderModule module); VulkanShader(VulkanDevice *device, VkShaderModule module);
~VulkanShader(); ~VulkanShader();
void SetDebugName(const char *name) { device->setDebugObjectName(name, (uint64_t)module, VK_OBJECT_TYPE_SHADER_MODULE); } void SetDebugName(const char *name) { device->SetObjectName(name, (uint64_t)module, VK_OBJECT_TYPE_SHADER_MODULE); }
VkShaderModule module = VK_NULL_HANDLE; VkShaderModule module = VK_NULL_HANDLE;
@ -168,7 +170,7 @@ public:
VulkanDescriptorSetLayout(VulkanDevice *device, VkDescriptorSetLayout layout); VulkanDescriptorSetLayout(VulkanDevice *device, VkDescriptorSetLayout layout);
~VulkanDescriptorSetLayout(); ~VulkanDescriptorSetLayout();
void SetDebugName(const char *name) { device->setDebugObjectName(name, (uint64_t)layout, VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT); } void SetDebugName(const char *name) { device->SetObjectName(name, (uint64_t)layout, VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT); }
VulkanDevice *device; VulkanDevice *device;
VkDescriptorSetLayout layout; VkDescriptorSetLayout layout;
@ -184,7 +186,12 @@ public:
VulkanDescriptorSet(VulkanDevice *device, VulkanDescriptorPool *pool, VkDescriptorSet set); VulkanDescriptorSet(VulkanDevice *device, VulkanDescriptorPool *pool, VkDescriptorSet set);
~VulkanDescriptorSet(); ~VulkanDescriptorSet();
void SetDebugName(const char *name) { device->setDebugObjectName(name, (uint64_t)set, VK_OBJECT_TYPE_DESCRIPTOR_SET); } #ifdef _DEBUG
void SetDebugName(const char* name) { debugName = name; device->SetObjectName(name, (uint64_t)set, VK_OBJECT_TYPE_DESCRIPTOR_SET); }
std::string debugName;
#else
void SetDebugName(const char* name) { device->SetObjectName(name, (uint64_t)set, VK_OBJECT_TYPE_DESCRIPTOR_SET); }
#endif
VulkanDevice *device; VulkanDevice *device;
VulkanDescriptorPool *pool; VulkanDescriptorPool *pool;
@ -201,14 +208,26 @@ public:
VulkanDescriptorPool(VulkanDevice *device, VkDescriptorPool pool); VulkanDescriptorPool(VulkanDevice *device, VkDescriptorPool pool);
~VulkanDescriptorPool(); ~VulkanDescriptorPool();
void SetDebugName(const char *name) { device->setDebugObjectName(name, (uint64_t)pool, VK_OBJECT_TYPE_DESCRIPTOR_POOL); } #ifdef _DEBUG
void SetDebugName(const char* name) { debugName = name; device->SetObjectName(name, (uint64_t)pool, VK_OBJECT_TYPE_DESCRIPTOR_POOL); }
std::string debugName;
#else
void SetDebugName(const char* name) { device->SetObjectName(name, (uint64_t)pool, VK_OBJECT_TYPE_DESCRIPTOR_POOL); }
#endif
std::unique_ptr<VulkanDescriptorSet> tryAllocate(VulkanDescriptorSetLayout *layout);
std::unique_ptr<VulkanDescriptorSet> tryAllocate(VulkanDescriptorSetLayout* layout, uint32_t bindlessCount);
std::unique_ptr<VulkanDescriptorSet> allocate(VulkanDescriptorSetLayout *layout); std::unique_ptr<VulkanDescriptorSet> allocate(VulkanDescriptorSetLayout *layout);
std::unique_ptr<VulkanDescriptorSet> allocate(VulkanDescriptorSetLayout* layout, uint32_t bindlessCount);
VulkanDevice *device; VulkanDevice *device;
VkDescriptorPool pool; VkDescriptorPool pool;
private: private:
enum class AllocType { TryAllocate, AlwaysAllocate };
std::unique_ptr<VulkanDescriptorSet> allocate(VulkanDescriptorSetLayout* layout, AllocType allocType);
std::unique_ptr<VulkanDescriptorSet> allocate(VulkanDescriptorSetLayout* layout, uint32_t bindlessCount, AllocType allocType);
VulkanDescriptorPool(const VulkanDescriptorPool &) = delete; VulkanDescriptorPool(const VulkanDescriptorPool &) = delete;
VulkanDescriptorPool &operator=(const VulkanDescriptorPool &) = delete; VulkanDescriptorPool &operator=(const VulkanDescriptorPool &) = delete;
}; };
@ -219,7 +238,7 @@ public:
VulkanQueryPool(VulkanDevice *device, VkQueryPool pool); VulkanQueryPool(VulkanDevice *device, VkQueryPool pool);
~VulkanQueryPool(); ~VulkanQueryPool();
void SetDebugName(const char *name) { device->setDebugObjectName(name, (uint64_t)pool, VK_OBJECT_TYPE_QUERY_POOL); } void SetDebugName(const char *name) { device->SetObjectName(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); bool getResults(uint32_t firstQuery, uint32_t queryCount, size_t dataSize, void *data, VkDeviceSize stride, VkQueryResultFlags flags);
@ -244,7 +263,7 @@ public:
return vkGetAccelerationStructureDeviceAddressKHR(device->device, &addressInfo); return vkGetAccelerationStructureDeviceAddressKHR(device->device, &addressInfo);
} }
void SetDebugName(const char* name) { device->setDebugObjectName(name, (uint64_t)accelstruct, VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_KHR); } void SetDebugName(const char* name) { device->SetObjectName(name, (uint64_t)accelstruct, VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_KHR); }
VulkanDevice* device; VulkanDevice* device;
VkAccelerationStructureKHR accelstruct; VkAccelerationStructureKHR accelstruct;
@ -257,14 +276,13 @@ private:
class VulkanPipeline class VulkanPipeline
{ {
public: public:
VulkanPipeline(VulkanDevice *device, VkPipeline pipeline, std::vector<uint8_t> shaderGroupHandles = {}); VulkanPipeline(VulkanDevice *device, VkPipeline pipeline);
~VulkanPipeline(); ~VulkanPipeline();
void SetDebugName(const char *name) { device->setDebugObjectName(name, (uint64_t)pipeline, VK_OBJECT_TYPE_PIPELINE); } void SetDebugName(const char *name) { device->SetObjectName(name, (uint64_t)pipeline, VK_OBJECT_TYPE_PIPELINE); }
VulkanDevice *device; VulkanDevice *device;
VkPipeline pipeline; VkPipeline pipeline;
std::vector<uint8_t> shaderGroupHandles;
private: private:
VulkanPipeline(const VulkanPipeline &) = delete; VulkanPipeline(const VulkanPipeline &) = delete;
@ -277,7 +295,7 @@ public:
VulkanPipelineLayout(VulkanDevice *device, VkPipelineLayout layout); VulkanPipelineLayout(VulkanDevice *device, VkPipelineLayout layout);
~VulkanPipelineLayout(); ~VulkanPipelineLayout();
void SetDebugName(const char *name) { device->setDebugObjectName(name, (uint64_t)layout, VK_OBJECT_TYPE_PIPELINE_LAYOUT); } void SetDebugName(const char *name) { device->SetObjectName(name, (uint64_t)layout, VK_OBJECT_TYPE_PIPELINE_LAYOUT); }
VulkanDevice *device; VulkanDevice *device;
VkPipelineLayout layout; VkPipelineLayout layout;
@ -293,7 +311,7 @@ public:
VulkanRenderPass(VulkanDevice *device, VkRenderPass renderPass); VulkanRenderPass(VulkanDevice *device, VkRenderPass renderPass);
~VulkanRenderPass(); ~VulkanRenderPass();
void SetDebugName(const char *name) { device->setDebugObjectName(name, (uint64_t)renderPass, VK_OBJECT_TYPE_RENDER_PASS); } void SetDebugName(const char *name) { device->SetObjectName(name, (uint64_t)renderPass, VK_OBJECT_TYPE_RENDER_PASS); }
VulkanDevice *device; VulkanDevice *device;
VkRenderPass renderPass; VkRenderPass renderPass;
@ -308,9 +326,9 @@ class RenderPassBegin
public: public:
RenderPassBegin(); RenderPassBegin();
RenderPassBegin& RenderPass(VulkanRenderPass *renderpass); RenderPassBegin& RenderPass(VulkanRenderPass* renderpass);
RenderPassBegin& RenderArea(int x, int y, int width, int height); RenderPassBegin& RenderArea(int x, int y, int width, int height);
RenderPassBegin& Framebuffer(VulkanFramebuffer *framebuffer); RenderPassBegin& Framebuffer(VulkanFramebuffer* framebuffer);
RenderPassBegin& AddClearColor(float r, float g, float b, float a); RenderPassBegin& AddClearColor(float r, float g, float b, float a);
RenderPassBegin& AddClearDepth(float value); RenderPassBegin& AddClearDepth(float value);
RenderPassBegin& AddClearStencil(int value); RenderPassBegin& AddClearStencil(int value);
@ -384,6 +402,7 @@ public:
void copyQueryPoolResults(VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, VkBuffer 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(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 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 beginRenderPass(const VkRenderPassBeginInfo* pRenderPassBegin, VkSubpassContents contents);
void nextSubpass(VkSubpassContents contents); void nextSubpass(VkSubpassContents contents);
void endRenderPass(); void endRenderPass();
@ -410,7 +429,7 @@ public:
VulkanCommandPool(VulkanDevice *device, int queueFamilyIndex); VulkanCommandPool(VulkanDevice *device, int queueFamilyIndex);
~VulkanCommandPool(); ~VulkanCommandPool();
void SetDebugName(const char *name) { device->setDebugObjectName(name, (uint64_t)pool, VK_OBJECT_TYPE_COMMAND_POOL); } void SetDebugName(const char *name) { device->SetObjectName(name, (uint64_t)pool, VK_OBJECT_TYPE_COMMAND_POOL); }
std::unique_ptr<VulkanCommandBuffer> createBuffer(); std::unique_ptr<VulkanCommandBuffer> createBuffer();
@ -432,8 +451,7 @@ inline VulkanSemaphore::VulkanSemaphore(VulkanDevice *device) : device(device)
VkSemaphoreCreateInfo semaphoreInfo = {}; VkSemaphoreCreateInfo semaphoreInfo = {};
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
VkResult result = vkCreateSemaphore(device->device, &semaphoreInfo, nullptr, &semaphore); VkResult result = vkCreateSemaphore(device->device, &semaphoreInfo, nullptr, &semaphore);
if (result != VK_SUCCESS) CheckVulkanError(result, "Could not create semaphore");
throw std::runtime_error("Failed to create semaphore!");
} }
inline VulkanSemaphore::~VulkanSemaphore() inline VulkanSemaphore::~VulkanSemaphore()
@ -448,8 +466,7 @@ inline VulkanFence::VulkanFence(VulkanDevice *device) : device(device)
VkFenceCreateInfo fenceInfo = {}; VkFenceCreateInfo fenceInfo = {};
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
VkResult result = vkCreateFence(device->device, &fenceInfo, nullptr, &fence); VkResult result = vkCreateFence(device->device, &fenceInfo, nullptr, &fence);
if (result != VK_SUCCESS) CheckVulkanError(result, "Could not create fence!");
throw std::runtime_error("Failed to create fence!");
} }
inline VulkanFence::~VulkanFence() inline VulkanFence::~VulkanFence()
@ -490,8 +507,7 @@ inline VulkanCommandPool::VulkanCommandPool(VulkanDevice *device, int queueFamil
poolInfo.flags = 0; poolInfo.flags = 0;
VkResult result = vkCreateCommandPool(device->device, &poolInfo, nullptr, &pool); VkResult result = vkCreateCommandPool(device->device, &poolInfo, nullptr, &pool);
if (result != VK_SUCCESS) CheckVulkanError(result, "Could not create command pool");
throw std::runtime_error("Could not create command pool");
} }
inline VulkanCommandPool::~VulkanCommandPool() inline VulkanCommandPool::~VulkanCommandPool()
@ -511,7 +527,7 @@ inline RenderPassBegin::RenderPassBegin()
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
} }
inline RenderPassBegin& RenderPassBegin::RenderPass(VulkanRenderPass *renderPass) inline RenderPassBegin& RenderPassBegin::RenderPass(VulkanRenderPass* renderPass)
{ {
renderPassInfo.renderPass = renderPass->renderPass; renderPassInfo.renderPass = renderPass->renderPass;
return *this; return *this;
@ -526,7 +542,7 @@ inline RenderPassBegin& RenderPassBegin::RenderArea(int x, int y, int width, int
return *this; return *this;
} }
inline RenderPassBegin& RenderPassBegin::Framebuffer(VulkanFramebuffer *framebuffer) inline RenderPassBegin& RenderPassBegin::Framebuffer(VulkanFramebuffer* framebuffer)
{ {
renderPassInfo.framebuffer = framebuffer->framebuffer; renderPassInfo.framebuffer = framebuffer->framebuffer;
return *this; return *this;
@ -593,8 +609,7 @@ inline VulkanCommandBuffer::VulkanCommandBuffer(VulkanCommandPool *pool) : pool(
allocInfo.commandBufferCount = 1; allocInfo.commandBufferCount = 1;
VkResult result = vkAllocateCommandBuffers(pool->device->device, &allocInfo, &buffer); VkResult result = vkAllocateCommandBuffers(pool->device->device, &allocInfo, &buffer);
if (result != VK_SUCCESS) CheckVulkanError(result, "Could not create command buffer");
throw std::runtime_error("Could not create command buffer");
} }
inline VulkanCommandBuffer::~VulkanCommandBuffer() inline VulkanCommandBuffer::~VulkanCommandBuffer()
@ -606,19 +621,17 @@ inline void VulkanCommandBuffer::begin()
{ {
VkCommandBufferBeginInfo beginInfo = {}; VkCommandBufferBeginInfo beginInfo = {};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT; beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
beginInfo.pInheritanceInfo = nullptr; beginInfo.pInheritanceInfo = nullptr;
VkResult result = vkBeginCommandBuffer(buffer, &beginInfo); VkResult result = vkBeginCommandBuffer(buffer, &beginInfo);
if (result != VK_SUCCESS) CheckVulkanError(result, "Could not begin recording command buffer");
throw std::runtime_error("Failed to begin recording command buffer!");
} }
inline void VulkanCommandBuffer::end() inline void VulkanCommandBuffer::end()
{ {
VkResult result = vkEndCommandBuffer(buffer); VkResult result = vkEndCommandBuffer(buffer);
if (result != VK_SUCCESS) CheckVulkanError(result, "Could not end command buffer recording");
throw std::runtime_error("Failed to record command buffer!");
} }
inline void VulkanCommandBuffer::debugFullPipelineBarrier() inline void VulkanCommandBuffer::debugFullPipelineBarrier()
@ -910,6 +923,11 @@ inline void VulkanCommandBuffer::pushConstants(VkPipelineLayout layout, VkShader
vkCmdPushConstants(buffer, layout, stageFlags, offset, size, 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) inline void VulkanCommandBuffer::beginRenderPass(const VkRenderPassBeginInfo* pRenderPassBegin, VkSubpassContents contents)
{ {
vkCmdBeginRenderPass(buffer, pRenderPassBegin, contents); vkCmdBeginRenderPass(buffer, pRenderPassBegin, contents);
@ -947,7 +965,7 @@ inline void VulkanCommandBuffer::writeAccelerationStructuresProperties(uint32_t
inline void VulkanCommandBuffer::SetDebugName(const char *name) inline void VulkanCommandBuffer::SetDebugName(const char *name)
{ {
pool->device->setDebugObjectName(name, (uint64_t)buffer, VK_OBJECT_TYPE_COMMAND_BUFFER); pool->device->SetObjectName(name, (uint64_t)buffer, VK_OBJECT_TYPE_COMMAND_BUFFER);
} }
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
@ -994,22 +1012,62 @@ inline VulkanDescriptorPool::~VulkanDescriptorPool()
vkDestroyDescriptorPool(device->device, pool, nullptr); vkDestroyDescriptorPool(device->device, pool, nullptr);
} }
inline std::unique_ptr<VulkanDescriptorSet> VulkanDescriptorPool::allocate(VulkanDescriptorSetLayout *layout) inline std::unique_ptr<VulkanDescriptorSet> VulkanDescriptorPool::allocate(VulkanDescriptorSetLayout* layout, AllocType allocType)
{ {
VkDescriptorSetAllocateInfo allocInfo = {}; VkDescriptorSetAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
allocInfo.descriptorPool = pool; allocInfo.descriptorPool = pool;
allocInfo.descriptorSetCount = 1; allocInfo.descriptorSetCount = 1;
allocInfo.pSetLayouts = &layout->layout; allocInfo.pSetLayouts = &layout->layout;
VkDescriptorSet descriptorSet; VkDescriptorSet descriptorSet;
VkResult result = vkAllocateDescriptorSets(device->device, &allocInfo, &descriptorSet); VkResult result = vkAllocateDescriptorSets(device->device, &allocInfo, &descriptorSet);
if (result != VK_SUCCESS) if (allocType == AllocType::TryAllocate && result != VK_SUCCESS)
throw std::runtime_error("Could not allocate descriptor sets"); return nullptr;
else
CheckVulkanError(result, "Could not allocate descriptor sets");
return std::make_unique<VulkanDescriptorSet>(device, this, descriptorSet); return std::make_unique<VulkanDescriptorSet>(device, this, descriptorSet);
} }
inline std::unique_ptr<VulkanDescriptorSet> VulkanDescriptorPool::allocate(VulkanDescriptorSetLayout* layout, uint32_t bindlessCount, AllocType allocType)
{
VkDescriptorSetAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
VkDescriptorSetVariableDescriptorCountAllocateInfoEXT countInfo{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO_EXT };
allocInfo.descriptorPool = pool;
allocInfo.descriptorSetCount = 1;
allocInfo.pSetLayouts = &layout->layout;
allocInfo.pNext = &countInfo;
countInfo.descriptorSetCount = 1;
countInfo.pDescriptorCounts = &bindlessCount;
VkDescriptorSet descriptorSet;
VkResult result = vkAllocateDescriptorSets(device->device, &allocInfo, &descriptorSet);
if (allocType == AllocType::TryAllocate && result != VK_SUCCESS)
return nullptr;
else
CheckVulkanError(result, "Could not allocate descriptor sets");
return std::make_unique<VulkanDescriptorSet>(device, this, descriptorSet);
}
inline std::unique_ptr<VulkanDescriptorSet> VulkanDescriptorPool::tryAllocate(VulkanDescriptorSetLayout *layout)
{
return allocate(layout, AllocType::TryAllocate);
}
inline std::unique_ptr<VulkanDescriptorSet> VulkanDescriptorPool::allocate(VulkanDescriptorSetLayout *layout)
{
return allocate(layout, AllocType::AlwaysAllocate);
}
inline std::unique_ptr<VulkanDescriptorSet> VulkanDescriptorPool::tryAllocate(VulkanDescriptorSetLayout* layout, uint32_t bindlessCount)
{
return allocate(layout, bindlessCount, AllocType::TryAllocate);
}
inline std::unique_ptr<VulkanDescriptorSet> VulkanDescriptorPool::allocate(VulkanDescriptorSetLayout* layout, uint32_t bindlessCount)
{
return allocate(layout, bindlessCount, AllocType::AlwaysAllocate);
}
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
inline VulkanQueryPool::VulkanQueryPool(VulkanDevice *device, VkQueryPool pool) : device(device), pool(pool) inline VulkanQueryPool::VulkanQueryPool(VulkanDevice *device, VkQueryPool pool) : device(device), pool(pool)
@ -1024,8 +1082,7 @@ inline VulkanQueryPool::~VulkanQueryPool()
inline bool VulkanQueryPool::getResults(uint32_t firstQuery, uint32_t queryCount, size_t dataSize, void *data, VkDeviceSize stride, VkQueryResultFlags flags) 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); VkResult result = vkGetQueryPoolResults(device->device, pool, firstQuery, queryCount, dataSize, data, stride, flags);
if (result != VK_SUCCESS && result != VK_NOT_READY) CheckVulkanError(result, "vkGetQueryPoolResults failed");
throw std::runtime_error("vkGetQueryPoolResults failed");
return result == VK_SUCCESS; return result == VK_SUCCESS;
} }
@ -1099,7 +1156,7 @@ inline VulkanAccelerationStructure::~VulkanAccelerationStructure()
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
inline VulkanPipeline::VulkanPipeline(VulkanDevice *device, VkPipeline pipeline, std::vector<uint8_t> shaderGroupHandles) : device(device), pipeline(pipeline), shaderGroupHandles(shaderGroupHandles) inline VulkanPipeline::VulkanPipeline(VulkanDevice *device, VkPipeline pipeline) : device(device), pipeline(pipeline)
{ {
} }

View file

@ -0,0 +1,53 @@
#include "vulkansurface.h"
#include "vulkaninstance.h"
#ifdef VK_USE_PLATFORM_WIN32_KHR
VulkanSurface::VulkanSurface(std::shared_ptr<VulkanInstance> instance, HWND window) : Instance(std::move(instance)), Window(window)
{
VkWin32SurfaceCreateInfoKHR createInfo = { VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR };
createInfo.hwnd = window;
createInfo.hinstance = GetModuleHandle(nullptr);
VkResult result = vkCreateWin32SurfaceKHR(Instance->Instance, &createInfo, nullptr, &Surface);
if (result != VK_SUCCESS)
VulkanError("Could not create vulkan surface");
}
VulkanSurface::~VulkanSurface()
{
vkDestroySurfaceKHR(Instance->Instance, Surface, nullptr);
}
#elif defined(VK_USE_PLATFORM_XLIB_KHR)
VulkanSurface::VulkanSurface(std::shared_ptr<VulkanInstance> instance, Display* disp, Window wind) : Instance(std::move(instance)), disp(disp), wind(wind)
{
VkXlibSurfaceCreateInfoKHR createInfo = { VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR };
createInfo.dpy = disp;
createInfo.window = wind;
VkResult result = vkCreateXlibSurfaceKHR(Instance->Instance, &createInfo, nullptr, &Surface);
if (result != VK_SUCCESS)
VulkanError("Could not create vulkan surface");
}
VulkanSurface::~VulkanSurface()
{
vkDestroySurfaceKHR(Instance->Instance, Surface, nullptr);
}
#else
VulkanSurface::VulkanSurface(std::shared_ptr<VulkanInstance> instance) : Instance(std::move(instance))
{
VulkanError("VulkanSurface not implemented on this platform");
}
VulkanSurface::~VulkanSurface()
{
vkDestroySurfaceKHR(Instance->Instance, Surface, nullptr);
}
#endif

View file

@ -0,0 +1,46 @@
#pragma once
#include "VulkanInstance.h"
#ifdef VK_USE_PLATFORM_WIN32_KHR
class VulkanSurface
{
public:
VulkanSurface(std::shared_ptr<VulkanInstance> instance, HWND window);
~VulkanSurface();
std::shared_ptr<VulkanInstance> Instance;
VkSurfaceKHR Surface = VK_NULL_HANDLE;
HWND Window = 0;
};
#elif defined(VK_USE_PLATFORM_XLIB_KHR)
#include <X11/Xlib.h>
class VulkanSurface
{
public:
VulkanSurface(std::shared_ptr<VulkanInstance> instance, Display* disp, Window wind);
~VulkanSurface();
std::shared_ptr<VulkanInstance> Instance;
VkSurfaceKHR Surface = VK_NULL_HANDLE;
Display* disp = nullptr;
Window wind;
};
#else
class VulkanSurface
{
public:
VulkanSurface(std::shared_ptr<VulkanInstance> instance);
~VulkanSurface();
std::shared_ptr<VulkanInstance> Instance;
VkSurfaceKHR Surface = VK_NULL_HANDLE;
};
#endif

View file

@ -1,3 +1,13 @@
#if defined(_WIN32)
#define VK_USE_PLATFORM_WIN32_KHR
#elif defined(__APPLE__)
#define VK_USE_PLATFORM_MACOS_MVK
#define VK_USE_PLATFORM_METAL_EXT
#else
#define VK_USE_PLATFORM_XLIB_KHR
#endif
/* This file is part of volk library; see volk.h for version/license details */ /* This file is part of volk library; see volk.h for version/license details */
/* clang-format off */ /* clang-format off */
#include "volk.h" #include "volk.h"