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/vulkanbuilders.cpp
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.h
src/lightmap/surfaceclip.cpp

View file

@ -7,6 +7,8 @@
#include "framework/templates.h"
#include "framework/halffloat.h"
#include "vulkanbuilders.h"
#include "vulkancompatibledevice.h"
#include "stacktrace.h"
#include <map>
#include <vector>
#include <algorithm>
@ -21,10 +23,22 @@
extern bool VKDebug;
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()
{
device = std::make_unique<VulkanDevice>(0, VKDebug);
useRayQuery = !NoRtx;// && device->physicalDevice.rayQueryProperties.supportsRayQuery;
auto instance = std::make_shared<VulkanInstance>(VKDebug);
device = std::make_unique<VulkanDevice>(instance, nullptr, VulkanCompatibleDevice::SelectDevice(instance, nullptr, 0));
useRayQuery = !NoRtx && device->PhysicalDevice.Features.RayQuery.rayQuery;
PrintVulkanInfo();
}
@ -38,9 +52,6 @@ void GPURaytracer::Raytrace(LevelMesh* level)
printf("Building Vulkan acceleration structures\n");
if (device->renderdoc)
device->renderdoc->StartFrameCapture(0, 0);
CreateVulkanObjects();
printf("Ray tracing in progress...\n");
@ -79,9 +90,6 @@ void GPURaytracer::Raytrace(LevelMesh* level)
DownloadAtlasImage(pageIndex);
}
if (device->renderdoc)
device->renderdoc->EndFrameCapture(0, 0);
printf("Ray trace complete\n");
}
@ -332,7 +340,7 @@ vec2 GPURaytracer::ToUV(const vec3& vert, const Surface* targetSurface)
void GPURaytracer::CreateVulkanObjects()
{
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();
@ -402,7 +410,7 @@ void GPURaytracer::FinishCommands()
QueueSubmit()
.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());
if (result != VK_SUCCESS)
@ -623,8 +631,8 @@ void GPURaytracer::CreateTopLevelAccelerationStructure()
void GPURaytracer::CreateShaders()
{
FString prefix = "#version 460\r\n#line 1\r\n";
FString traceprefix = "#version 460\r\n";
std::string prefix = "#version 460\r\n#line 1\r\n";
std::string traceprefix = "#version 460\r\n";
if (useRayQuery)
{
traceprefix += "#extension GL_EXT_ray_query : require\r\n";
@ -878,7 +886,7 @@ LightmapImage GPURaytracer::CreateImage(int width, int height)
void GPURaytracer::CreateUniformBuffer()
{
VkDeviceSize align = device->physicalDevice.properties.limits.minUniformBufferOffsetAlignment;
VkDeviceSize align = device->PhysicalDevice.Properties.limits.minUniformBufferOffsetAlignment;
uniformStructStride = (sizeof(Uniforms) + align - 1) / align * align;
uniformBuffer = BufferBuilder()
@ -932,7 +940,7 @@ std::vector<CollisionNode> GPURaytracer::CreateCollisionNodes()
void GPURaytracer::PrintVulkanInfo()
{
const auto& props = device->physicalDevice.properties;
const auto& props = device->PhysicalDevice.Properties;
std::string deviceType;
switch (props.deviceType)

View file

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

View file

@ -109,77 +109,45 @@ static const TBuiltInResource DefaultTBuiltInResource = {
}
};
void ShaderBuilder::Init()
{
ShInitialize();
}
void ShaderBuilder::Deinit()
{
ShFinalize();
}
ShaderBuilder::ShaderBuilder()
{
}
ShaderBuilder& ShaderBuilder::VertexShader(const FString &c)
ShaderBuilder& ShaderBuilder::VertexShader(const std::string& c)
{
code = c;
stage = EShLanguage::EShLangVertex;
return *this;
}
ShaderBuilder& ShaderBuilder::FragmentShader(const FString &c)
ShaderBuilder& ShaderBuilder::FragmentShader(const std::string& c)
{
code = c;
stage = EShLanguage::EShLangFragment;
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)
{
EShLanguage stage = (EShLanguage)this->stage;
const char *sources[] = { code.GetChars() };
const char *sources[] = { code.c_str() };
TBuiltInResource resources = DefaultTBuiltInResource;
glslang::TShader shader(stage);
shader.setStrings(sources, 1);
shader.setEnvInput(glslang::EShSourceGlsl, stage, glslang::EShClientVulkan, 100);
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.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);
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;
@ -200,13 +168,13 @@ std::unique_ptr<VulkanShader> ShaderBuilder::Create(const char *shadername, Vulk
bool linkSuccess = program.link(EShMsgDefault);
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);
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;
@ -226,9 +194,7 @@ std::unique_ptr<VulkanShader> ShaderBuilder::Create(const char *shadername, Vulk
VkShaderModule shaderModule;
VkResult result = vkCreateShaderModule(device->device, &createInfo, nullptr, &shaderModule);
if (result != VK_SUCCESS)
{
throw std::runtime_error("Could not create vulkan shader module");
}
auto obj = std::make_unique<VulkanShader>(device, shaderModule);
if (debugName)
@ -297,7 +263,7 @@ ImageBuilder& ImageBuilder::MemoryType(VkMemoryPropertyFlags requiredFlags, VkMe
bool ImageBuilder::IsFormatSupported(VulkanDevice* device, VkFormatFeatureFlags bufferFeatures)
{
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 (imageInfo.extent.width > properties.maxExtent.width) return false;
if (imageInfo.extent.height > properties.maxExtent.height) return false;
@ -308,7 +274,7 @@ bool ImageBuilder::IsFormatSupported(VulkanDevice* device, VkFormatFeatureFlags
if (bufferFeatures != 0)
{
VkFormatProperties formatProperties = { };
vkGetPhysicalDeviceFormatProperties(device->physicalDevice.device, imageInfo.format, &formatProperties);
vkGetPhysicalDeviceFormatProperties(device->PhysicalDevice.Device, imageInfo.format, &formatProperties);
if ((formatProperties.bufferFeatures & bufferFeatures) != bufferFeatures)
return false;
}
@ -321,8 +287,7 @@ std::unique_ptr<VulkanImage> ImageBuilder::Create(VulkanDevice* device, VkDevice
VmaAllocation allocation;
VkResult result = vmaCreateImage(device->allocator, &imageInfo, &allocInfo, &image, &allocation, nullptr);
if (result != VK_SUCCESS)
throw std::runtime_error("Could not create vulkan image");
CheckVulkanError(result, "Could not create vulkan image");
if (allocatedBytes != nullptr)
{
@ -385,8 +350,7 @@ std::unique_ptr<VulkanImageView> ImageViewBuilder::Create(VulkanDevice* device)
{
VkImageView view;
VkResult result = vkCreateImageView(device->device, &viewInfo, nullptr, &view);
if (result != VK_SUCCESS)
throw std::runtime_error("Could not create texture image view");
CheckVulkanError(result, "Could not create texture image view");
auto obj = std::make_unique<VulkanImageView>(device, view);
if (debugName)
@ -457,6 +421,12 @@ SamplerBuilder& SamplerBuilder::Anisotropy(float maxAnisotropy)
return *this;
}
SamplerBuilder& SamplerBuilder::MipLodBias(float bias)
{
samplerInfo.mipLodBias = bias;
return *this;
}
SamplerBuilder& SamplerBuilder::MaxLod(float value)
{
samplerInfo.maxLod = value;
@ -467,8 +437,7 @@ std::unique_ptr<VulkanSampler> SamplerBuilder::Create(VulkanDevice* device)
{
VkSampler sampler;
VkResult result = vkCreateSampler(device->device, &samplerInfo, nullptr, &sampler);
if (result != VK_SUCCESS)
throw std::runtime_error("Could not create texture sampler");
CheckVulkanError(result, "Could not create texture sampler");
auto obj = std::make_unique<VulkanSampler>(device, sampler);
if (debugName)
obj->SetDebugName(debugName);
@ -511,8 +480,7 @@ std::unique_ptr<VulkanBuffer> BufferBuilder::Create(VulkanDevice* device)
VmaAllocation allocation;
VkResult result = vmaCreateBuffer(device->allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
if (result != VK_SUCCESS)
throw std::runtime_error("Could not allocate memory for vulkan buffer");
CheckVulkanError(result, "Could not allocate memory for vulkan buffer");
auto obj = std::make_unique<VulkanBuffer>(device, buffer, allocation, bufferInfo.size);
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()
{
pipelineInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
@ -723,10 +566,15 @@ std::unique_ptr<VulkanPipeline> ComputePipelineBuilder::Create(VulkanDevice* dev
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 = { };
binding.binding = index;
@ -734,10 +582,18 @@ DescriptorSetLayoutBuilder& DescriptorSetLayoutBuilder::AddBinding(int index, Vk
binding.descriptorCount = arrayCount;
binding.stageFlags = stageFlags;
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;
}
@ -745,8 +601,7 @@ std::unique_ptr<VulkanDescriptorSetLayout> DescriptorSetLayoutBuilder::Create(Vu
{
VkDescriptorSetLayout layout;
VkResult result = vkCreateDescriptorSetLayout(device->device, &layoutInfo, nullptr, &layout);
if (result != VK_SUCCESS)
throw std::runtime_error("Could not create descriptor set layout");
CheckVulkanError(result, "Could not create descriptor set layout");
auto obj = std::make_unique<VulkanDescriptorSetLayout>(device, layout);
if (debugName)
obj->SetDebugName(debugName);
@ -762,6 +617,12 @@ DescriptorPoolBuilder::DescriptorPoolBuilder()
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)
{
poolInfo.maxSets = value;
@ -784,8 +645,7 @@ std::unique_ptr<VulkanDescriptorPool> DescriptorPoolBuilder::Create(VulkanDevice
{
VkDescriptorPool descriptorPool;
VkResult result = vkCreateDescriptorPool(device->device, &poolInfo, nullptr, &descriptorPool);
if (result != VK_SUCCESS)
throw std::runtime_error("Could not create descriptor pool");
CheckVulkanError(result, "Could not create descriptor pool");
auto obj = std::make_unique<VulkanDescriptorPool>(device, descriptorPool);
if (debugName)
obj->SetDebugName(debugName);
@ -811,8 +671,7 @@ std::unique_ptr<VulkanQueryPool> QueryPoolBuilder::Create(VulkanDevice* device)
{
VkQueryPool queryPool;
VkResult result = vkCreateQueryPool(device->device, &poolInfo, nullptr, &queryPool);
if (result != VK_SUCCESS)
throw std::runtime_error("Could not create query pool");
CheckVulkanError(result, "Could not create query pool");
auto obj = std::make_unique<VulkanQueryPool>(device, queryPool);
if (debugName)
obj->SetDebugName(debugName);
@ -862,8 +721,7 @@ std::unique_ptr<VulkanFramebuffer> FramebufferBuilder::Create(VulkanDevice* devi
{
VkFramebuffer framebuffer = 0;
VkResult result = vkCreateFramebuffer(device->device, &framebufferInfo, nullptr, &framebuffer);
if (result != VK_SUCCESS)
throw std::runtime_error("Could not create framebuffer");
CheckVulkanError(result, "Could not create framebuffer");
auto obj = std::make_unique<VulkanFramebuffer>(device, framebuffer);
if (debugName)
obj->SetDebugName(debugName);
@ -1179,8 +1037,7 @@ std::unique_ptr<VulkanPipeline> GraphicsPipelineBuilder::Create(VulkanDevice* de
{
VkPipeline pipeline = 0;
VkResult result = vkCreateGraphicsPipelines(device->device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline);
if (result != VK_SUCCESS)
throw std::runtime_error("Could not create graphics pipeline");
CheckVulkanError(result, "Could not create graphics pipeline");
auto obj = std::make_unique<VulkanPipeline>(device, pipeline);
if (debugName)
obj->SetDebugName(debugName);
@ -1218,8 +1075,7 @@ std::unique_ptr<VulkanPipelineLayout> PipelineLayoutBuilder::Create(VulkanDevice
{
VkPipelineLayout pipelineLayout;
VkResult result = vkCreatePipelineLayout(device->device, &pipelineLayoutInfo, nullptr, &pipelineLayout);
if (result != VK_SUCCESS)
throw std::runtime_error("Could not create pipeline layout");
CheckVulkanError(result, "Could not create pipeline layout");
auto obj = std::make_unique<VulkanPipelineLayout>(device, pipelineLayout);
if (debugName)
obj->SetDebugName(debugName);
@ -1323,8 +1179,7 @@ std::unique_ptr<VulkanRenderPass> RenderPassBuilder::Create(VulkanDevice* device
{
VkRenderPass renderPass = 0;
VkResult result = vkCreateRenderPass(device->device, &renderPassInfo, nullptr, &renderPass);
if (result != VK_SUCCESS)
throw std::runtime_error("Could not create render pass");
CheckVulkanError(result, "Could not create render pass");
auto obj = std::make_unique<VulkanRenderPass>(device, renderPass);
if (debugName)
obj->SetDebugName(debugName);
@ -1466,9 +1321,8 @@ QueueSubmit& QueueSubmit::AddSignal(VulkanSemaphore* semaphore)
void QueueSubmit::Execute(VulkanDevice* device, VkQueue queue, VulkanFence* fence)
{
VkResult result = vkQueueSubmit(device->graphicsQueue, 1, &submitInfo, fence ? fence->fence : VK_NULL_HANDLE);
if (result != VK_SUCCESS)
throw std::runtime_error("Could not submit command buffer");
VkResult result = vkQueueSubmit(device->GraphicsQueue, 1, &submitInfo, fence ? fence->fence : VK_NULL_HANDLE);
CheckVulkanError(result, "Could not submit command buffer");
}
/////////////////////////////////////////////////////////////////////////////
@ -1501,28 +1355,6 @@ WriteDescriptors& WriteDescriptors::AddBuffer(VulkanDescriptorSet* descriptorSet
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)
{
VkDescriptorImageInfo imageInfo = {};
@ -1546,10 +1378,15 @@ WriteDescriptors& WriteDescriptors::AddStorageImage(VulkanDescriptorSet* descrip
}
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 = {};
imageInfo.imageView = view->view;
imageInfo.sampler = sampler ? sampler->sampler : nullptr;
imageInfo.sampler = sampler->sampler;
imageInfo.imageLayout = imageLayout;
auto extra = std::make_unique<WriteExtra>();
@ -1559,7 +1396,7 @@ WriteDescriptors& WriteDescriptors::AddCombinedImageSampler(VulkanDescriptorSet*
descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrite.dstSet = descriptorSet->set;
descriptorWrite.dstBinding = binding;
descriptorWrite.dstArrayElement = 0;
descriptorWrite.dstArrayElement = arrayIndex;
descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptorWrite.descriptorCount = 1;
descriptorWrite.pImageInfo = &extra->imageInfo;
@ -1591,5 +1428,6 @@ WriteDescriptors& WriteDescriptors::AddAccelerationStructure(VulkanDescriptorSet
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
#include "vulkanobjects.h"
#include "framework/zstring.h"
#include <cassert>
class ImageBuilder
@ -55,6 +54,7 @@ public:
SamplerBuilder& MagFilter(VkFilter magFilter);
SamplerBuilder& MipmapMode(VkSamplerMipmapMode mode);
SamplerBuilder& Anisotropy(float maxAnisotropy);
SamplerBuilder& MipLodBias(float bias);
SamplerBuilder& MaxLod(float value);
SamplerBuilder& DebugName(const char* name) { debugName = name; return *this; }
@ -88,20 +88,17 @@ class ShaderBuilder
public:
ShaderBuilder();
ShaderBuilder& VertexShader(const FString &code);
ShaderBuilder& FragmentShader(const FString &code);
ShaderBuilder& RayGenShader(const FString& code);
ShaderBuilder& IntersectShader(const FString& code);
ShaderBuilder& AnyHitShader(const FString& code);
ShaderBuilder& ClosestHitShader(const FString& code);
ShaderBuilder& MissShader(const FString& code);
ShaderBuilder& CallableShader(const FString& code);
static void Init();
static void Deinit();
ShaderBuilder& VertexShader(const std::string &code);
ShaderBuilder& FragmentShader(const std::string&code);
ShaderBuilder& DebugName(const char* name) { debugName = name; return *this; }
std::unique_ptr<VulkanShader> Create(const char *shadername, VulkanDevice *device);
private:
FString code;
std::string code;
int stage = 0;
const char* debugName = nullptr;
};
@ -123,29 +120,6 @@ private:
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
{
public:
@ -168,14 +142,17 @@ class DescriptorSetLayoutBuilder
public:
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; }
std::unique_ptr<VulkanDescriptorSetLayout> Create(VulkanDevice *device);
private:
VkDescriptorSetLayoutCreateInfo layoutInfo = {};
TArray<VkDescriptorSetLayoutBinding> bindings;
VkDescriptorSetLayoutCreateInfo layoutInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
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;
};
@ -184,6 +161,7 @@ class DescriptorPoolBuilder
public:
DescriptorPoolBuilder();
DescriptorPoolBuilder& Flags(VkDescriptorPoolCreateFlags flags);
DescriptorPoolBuilder& MaxSets(int value);
DescriptorPoolBuilder& AddPoolSize(VkDescriptorType type, int count);
DescriptorPoolBuilder& DebugName(const char* name) { debugName = name; return *this; }
@ -388,9 +366,9 @@ class WriteDescriptors
public:
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& AddSampledImage(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, int arrayIndex, VulkanImageView* view, VulkanSampler* sampler, VkImageLayout imageLayout);
WriteDescriptors& AddAccelerationStructure(VulkanDescriptorSet* descriptorSet, int binding, VulkanAccelerationStructure* accelStruct);
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 "vulkanobjects.h"
#include "stacktrace.h"
#include "vulkancompatibledevice.h"
#include <algorithm>
#include <set>
#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
if (HMODULE mod = GetModuleHandle(TEXT("renderdoc.dll")))
{
pRENDERDOC_GetAPI RENDERDOC_GetAPI = (pRENDERDOC_GetAPI)GetProcAddress(mod, "RENDERDOC_GetAPI");
int ret = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_4_2, (void**)&renderdoc);
if (ret != 1)
renderdoc = nullptr;
}
#endif
PhysicalDevice = *selectedDevice.Device;
EnabledDeviceExtensions = selectedDevice.EnabledDeviceExtensions;
EnabledFeatures = selectedDevice.EnabledFeatures;
GraphicsFamily = selectedDevice.GraphicsFamily;
PresentFamily = selectedDevice.PresentFamily;
GraphicsTimeQueries = selectedDevice.GraphicsTimeQueries;
try
{
ShInitialize();
initVolk();
createInstance();
selectPhysicalDevice();
selectFeatures();
createDevice();
createAllocator();
CreateDevice();
CreateAllocator();
}
catch (...)
{
releaseResources();
ReleaseResources();
throw;
}
}
VulkanDevice::~VulkanDevice()
{
releaseResources();
ReleaseResources();
}
void VulkanDevice::selectFeatures()
bool VulkanDevice::SupportsDeviceExtension(const char* ext) const
{
enabledDeviceFeatures.samplerAnisotropy = physicalDevice.features.samplerAnisotropy;
enabledDeviceFeatures.fragmentStoresAndAtomics = physicalDevice.features.fragmentStoresAndAtomics;
enabledDeviceFeatures.depthClamp = physicalDevice.features.depthClamp;
enabledDeviceFeatures.shaderClipDistance = physicalDevice.features.shaderClipDistance;
return std::find(EnabledDeviceExtensions.begin(), EnabledDeviceExtensions.end(), ext) != EnabledDeviceExtensions.end();
}
bool VulkanDevice::checkRequiredFeatures(const VkPhysicalDeviceFeatures &f)
{
return
f.samplerAnisotropy == VK_TRUE &&
f.fragmentStoresAndAtomics == VK_TRUE &&
f.depthClamp == VK_TRUE;
}
void VulkanDevice::selectPhysicalDevice()
{
availableDevices = getPhysicalDevices(instance);
if (availableDevices.empty())
throw std::runtime_error("No Vulkan devices found. Either the graphics card has no vulkan support or the driver is too old.");
// createSurface();
supportedDevices.clear();
for (size_t idx = 0; idx < availableDevices.size(); idx++)
{
const auto &info = availableDevices[idx];
if (!checkRequiredFeatures(info.features))
continue;
std::set<std::string> requiredExtensionSearch(enabledDeviceExtensions.begin(), enabledDeviceExtensions.end());
for (const auto &ext : info.extensions)
requiredExtensionSearch.erase(ext.extensionName);
if (!requiredExtensionSearch.empty())
continue;
VulkanCompatibleDevice dev;
dev.device = &availableDevices[idx];
bool window = false; // static_cast<bool>(createSurfaceWindow);
// Figure out what can present
if (window)
{
for (int i = 0; i < (int)info.queueFamilies.size(); i++)
{
VkBool32 presentSupport = false;
VkResult result = vkGetPhysicalDeviceSurfaceSupportKHR(info.device, i, surface, &presentSupport);
if (result == VK_SUCCESS && info.queueFamilies[i].queueCount > 0 && presentSupport)
{
dev.presentFamily = i;
break;
}
}
}
// The vulkan spec states that graphics and compute queues can always do transfer.
// Furthermore the spec states that graphics queues always can do compute.
// Last, the spec makes it OPTIONAL whether the VK_QUEUE_TRANSFER_BIT is set for such queues, but they MUST support transfer.
//
// In short: pick the first graphics queue family for everything.
for (int i = 0; i < (int)info.queueFamilies.size(); i++)
{
const auto &queueFamily = info.queueFamilies[i];
if (queueFamily.queueCount > 0 && (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT))
{
dev.graphicsFamily = i;
break;
}
}
if (dev.graphicsFamily != -1 && (!window || dev.presentFamily != -1))
{
supportedDevices.push_back(dev);
}
}
if (supportedDevices.empty())
throw std::runtime_error("No Vulkan device supports the minimum requirements of this application");
// The device order returned by Vulkan can be anything. Prefer discrete > integrated > virtual gpu > cpu > other
std::stable_sort(supportedDevices.begin(), supportedDevices.end(), [&](const auto &a, const auto b) {
// Sort by GPU type first. This will ensure the "best" device is most likely to map to vk_device 0
static const int typeSort[] = { 4, 1, 0, 2, 3 };
int sortA = a.device->properties.deviceType < 5 ? typeSort[a.device->properties.deviceType] : (int)a.device->properties.deviceType;
int sortB = b.device->properties.deviceType < 5 ? typeSort[b.device->properties.deviceType] : (int)b.device->properties.deviceType;
if (sortA != sortB)
return sortA < sortB;
// Then sort by the device's unique ID so that vk_device uses a consistent order
int sortUUID = memcmp(a.device->properties.pipelineCacheUUID, b.device->properties.pipelineCacheUUID, VK_UUID_SIZE);
return sortUUID < 0;
});
size_t selected = vk_device;
if (selected >= supportedDevices.size())
selected = 0;
// Enable optional extensions we are interested in, if they are available on this device
for (const auto &ext : supportedDevices[selected].device->extensions)
{
for (const auto &opt : optionalDeviceExtensions)
{
if (strcmp(ext.extensionName, opt) == 0)
{
enabledDeviceExtensions.push_back(opt);
}
}
}
physicalDevice = *supportedDevices[selected].device;
graphicsFamily = supportedDevices[selected].graphicsFamily;
presentFamily = supportedDevices[selected].presentFamily;
}
bool VulkanDevice::supportsDeviceExtension(const char *ext) const
{
return std::find(enabledDeviceExtensions.begin(), enabledDeviceExtensions.end(), ext) != enabledDeviceExtensions.end();
}
void VulkanDevice::createAllocator()
void VulkanDevice::CreateAllocator()
{
VmaAllocatorCreateInfo allocinfo = {};
allocinfo.vulkanApiVersion = ApiVersion;
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_BUFFER_DEVICE_ADDRESS_BIT;
allocinfo.physicalDevice = physicalDevice.device;
allocinfo.vulkanApiVersion = Instance->ApiVersion;
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;
if (SupportsDeviceExtension(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME))
allocinfo.flags |= VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT;
allocinfo.physicalDevice = PhysicalDevice.Device;
allocinfo.device = device;
allocinfo.instance = Instance->Instance;
allocinfo.preferredLargeHeapBlockSize = 64 * 1024 * 1024;
allocinfo.instance = instance;
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;
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
std::set<int> neededFamilies;
neededFamilies.insert(graphicsFamily);
if (presentFamily != -1)
neededFamilies.insert(presentFamily);
if (GraphicsFamily != -1)
neededFamilies.insert(GraphicsFamily);
if (PresentFamily != -1)
neededFamilies.insert(PresentFamily);
for (int index : neededFamilies)
{
@ -200,359 +75,85 @@ void VulkanDevice::createDevice()
queueCreateInfos.push_back(queueCreateInfo);
}
VkPhysicalDeviceRayQueryFeaturesKHR rayqueryFeatures = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_QUERY_FEATURES_KHR };
rayqueryFeatures.rayQuery = true;
std::vector<const char*> extensionNames;
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 };
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;
VkDeviceCreateInfo deviceCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO };
deviceCreateInfo.queueCreateInfoCount = (uint32_t)queueCreateInfos.size();
deviceCreateInfo.pQueueCreateInfos = queueCreateInfos.data();
//deviceCreateInfo.pEnabledFeatures = &enabledDeviceFeatures;
deviceCreateInfo.enabledExtensionCount = (uint32_t)enabledDeviceExtensions.size();
deviceCreateInfo.ppEnabledExtensionNames = enabledDeviceExtensions.data();
deviceCreateInfo.enabledExtensionCount = (uint32_t)extensionNames.size();
deviceCreateInfo.ppEnabledExtensionNames = extensionNames.data();
deviceCreateInfo.enabledLayerCount = 0;
VkResult result = vkCreateDevice(physicalDevice.device, &deviceCreateInfo, nullptr, &device);
if (result != VK_SUCCESS)
throw std::runtime_error("Could not create vulkan device");
VkPhysicalDeviceFeatures2 deviceFeatures2 = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2 };
deviceFeatures2.features = EnabledFeatures.Features;
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);
vkGetDeviceQueue(device, graphicsFamily, 0, &graphicsQueue);
if (presentFamily != -1)
vkGetDeviceQueue(device, presentFamily, 0, &presentQueue);
if (GraphicsFamily != -1)
vkGetDeviceQueue(device, GraphicsFamily, 0, &GraphicsQueue);
if (PresentFamily != -1)
vkGetDeviceQueue(device, PresentFamily, 0, &PresentQueue);
}
/*
#ifdef WIN32
void VulkanDevice::createSurface()
{
VkWin32SurfaceCreateInfoKHR windowCreateInfo = {};
windowCreateInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
windowCreateInfo.hwnd = window;
windowCreateInfo.hinstance = GetModuleHandle(nullptr);
VkResult result = vkCreateWin32SurfaceKHR(instance, &windowCreateInfo, nullptr, &surface);
if (result != VK_SUCCESS)
throw std::runtime_error("Could not create vulkan surface");
}
#else
void VulkanDevice::createSurface()
{
for (const auto &info : availableDevices)
{
for (uint32_t i = 0; i < (uint32_t)info.queueFamilies.size(); i++)
{
const auto &queueFamily = info.queueFamilies[i];
if (queueFamily.queueCount > 0 && (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT))
{
uint32_t graphicsFamily = i;
auto handles = createSurfaceWindow(info.device, graphicsFamily);
VkXlibSurfaceCreateInfoKHR windowCreateInfo = {};
windowCreateInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
windowCreateInfo.dpy = handles.first;
windowCreateInfo.window = handles.second;
VkResult result = vkCreateXlibSurfaceKHR(instance, &windowCreateInfo, nullptr, &surface);
if (result != VK_SUCCESS)
throw std::runtime_error("Could not create vulkan surface");
return;
}
}
}
throw std::runtime_error("No vulkan device supports graphics!");
}
#endif
*/
void VulkanDevice::createInstance()
{
availableLayers = getAvailableLayers();
extensions = getExtensions();
enabledExtensions = getPlatformExtensions();
std::string debugLayer = "VK_LAYER_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()
void VulkanDevice::ReleaseResources()
{
if (device)
vkDeviceWaitIdle(device);
if (allocator)
vmaDestroyAllocator(allocator);
allocator = VK_NULL_HANDLE;
if (device)
vkDestroyDevice(device, nullptr);
device = VK_NULL_HANDLE;
if (surface)
vkDestroySurfaceKHR(instance, surface, nullptr);
surface = VK_NULL_HANDLE;
if (debugMessenger)
vkDestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
if (instance)
vkDestroyInstance(instance, nullptr);
instance = VK_NULL_HANDLE;
ShFinalize();
device = nullptr;
}
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 ((typeFilter & (1 << i)) && (physicalDevice.memoryProperties.memoryTypes[i].propertyFlags & properties) == properties)
return i;
}
if (!DebugLayerActive) return;
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
#include "vulkaninstance.h"
#include <functional>
#ifdef WIN32
#define VK_USE_PLATFORM_WIN32_KHR
#endif
#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"
#include <mutex>
#include <vector>
#include <algorithm>
#include <memory>
class VulkanSwapChain;
class VulkanSemaphore;
class VulkanFence;
class VulkanPhysicalDevice
{
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 VulkanPhysicalDevice;
class VulkanSurface;
class VulkanCompatibleDevice;
class VulkanDevice
{
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();
void setDebugObjectName(const char *name, uint64_t handle, VkObjectType type)
{
if (!debugLayerActive) return;
std::vector<std::string> EnabledDeviceExtensions;
VulkanDeviceFeatures EnabledFeatures;
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);
}
VulkanPhysicalDevice PhysicalDevice;
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;
VmaAllocator allocator = VK_NULL_HANDLE;
VkQueue graphicsQueue = VK_NULL_HANDLE;
VkQueue presentQueue = VK_NULL_HANDLE;
VkQueue GraphicsQueue = VK_NULL_HANDLE;
VkQueue PresentQueue = VK_NULL_HANDLE;
int graphicsFamily = -1;
int presentFamily = -1;
int GraphicsFamily = -1;
int PresentFamily = -1;
bool GraphicsTimeQueries = false;
// Physical device info
std::vector<VulkanPhysicalDevice> availableDevices;
std::vector<VulkanCompatibleDevice> supportedDevices;
bool SupportsDeviceExtension(const char* ext) const;
uint32_t ApiVersion = VK_API_VERSION_1_2;
RENDERDOC_API_1_4_2* renderdoc = nullptr;
static void initVolk();
void SetObjectName(const char* name, uint64_t handle, VkObjectType type);
private:
int vk_device;
bool vk_debug;
bool DebugLayerActive = false;
void createInstance();
//void createSurface();
void selectPhysicalDevice();
void selectFeatures();
void createDevice();
void createAllocator();
void releaseResources();
bool supportsDeviceExtension(const char *ext) const;
static bool checkRequiredFeatures(const VkPhysicalDeviceFeatures &f);
VkDebugUtilsMessengerEXT debugMessenger = VK_NULL_HANDLE;
static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData);
static std::vector<VkLayerProperties> getAvailableLayers();
static std::vector<VkExtensionProperties> getExtensions();
static std::vector<const char *> getPlatformExtensions();
static std::vector<VulkanPhysicalDevice> getPhysicalDevices(VkInstance instance);
void CreateDevice();
void CreateAllocator();
void ReleaseResources();
};

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 <memory>
#include <stdexcept>
class VulkanCommandPool;
class VulkanDescriptorPool;
class VulkanCommandBuffer;
@ -15,7 +12,7 @@ public:
VulkanSemaphore(VulkanDevice *device);
~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;
VkSemaphore semaphore = VK_NULL_HANDLE;
@ -31,7 +28,7 @@ public:
VulkanFence(VulkanDevice *device);
~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;
VkFence fence = VK_NULL_HANDLE;
@ -54,7 +51,12 @@ public:
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;
@ -76,7 +78,7 @@ public:
VulkanFramebuffer(VulkanDevice *device, VkFramebuffer framebuffer);
~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;
VkFramebuffer framebuffer;
@ -92,7 +94,7 @@ public:
VulkanImage(VulkanDevice *device, VkImage image, VmaAllocation allocation, int width, int height, int mipLevels, int layerCount);
~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;
int width = 0;
@ -117,7 +119,7 @@ public:
VulkanImageView(VulkanDevice *device, VkImageView view);
~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;
@ -134,7 +136,7 @@ public:
VulkanSampler(VulkanDevice *device, VkSampler sampler);
~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;
@ -151,7 +153,7 @@ public:
VulkanShader(VulkanDevice *device, VkShaderModule module);
~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;
@ -168,7 +170,7 @@ public:
VulkanDescriptorSetLayout(VulkanDevice *device, VkDescriptorSetLayout layout);
~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;
VkDescriptorSetLayout layout;
@ -184,7 +186,12 @@ public:
VulkanDescriptorSet(VulkanDevice *device, VulkanDescriptorPool *pool, VkDescriptorSet set);
~VulkanDescriptorSet();
void SetDebugName(const char *name) { device->setDebugObjectName(name, (uint64_t)set, VK_OBJECT_TYPE_DESCRIPTOR_SET); }
#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;
VulkanDescriptorPool *pool;
@ -201,14 +208,26 @@ public:
VulkanDescriptorPool(VulkanDevice *device, VkDescriptorPool pool);
~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, uint32_t bindlessCount);
VulkanDevice *device;
VkDescriptorPool pool;
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 &operator=(const VulkanDescriptorPool &) = delete;
};
@ -219,7 +238,7 @@ public:
VulkanQueryPool(VulkanDevice *device, VkQueryPool pool);
~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);
@ -244,7 +263,7 @@ public:
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;
VkAccelerationStructureKHR accelstruct;
@ -257,14 +276,13 @@ private:
class VulkanPipeline
{
public:
VulkanPipeline(VulkanDevice *device, VkPipeline pipeline, std::vector<uint8_t> shaderGroupHandles = {});
VulkanPipeline(VulkanDevice *device, VkPipeline pipeline);
~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;
VkPipeline pipeline;
std::vector<uint8_t> shaderGroupHandles;
private:
VulkanPipeline(const VulkanPipeline &) = delete;
@ -277,7 +295,7 @@ public:
VulkanPipelineLayout(VulkanDevice *device, VkPipelineLayout layout);
~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;
VkPipelineLayout layout;
@ -293,7 +311,7 @@ public:
VulkanRenderPass(VulkanDevice *device, VkRenderPass renderPass);
~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;
VkRenderPass renderPass;
@ -308,9 +326,9 @@ class RenderPassBegin
public:
RenderPassBegin();
RenderPassBegin& RenderPass(VulkanRenderPass *renderpass);
RenderPassBegin& RenderPass(VulkanRenderPass* renderpass);
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& AddClearDepth(float 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 pushConstants(VulkanPipelineLayout *layout, VkShaderStageFlags stageFlags, uint32_t offset, uint32_t size, const void* pValues);
void pushConstants(VkPipelineLayout layout, VkShaderStageFlags stageFlags, uint32_t offset, uint32_t size, const void* pValues);
void beginRenderPass(const RenderPassBegin &renderPassBegin, VkSubpassContents contents = VK_SUBPASS_CONTENTS_INLINE);
void beginRenderPass(const VkRenderPassBeginInfo* pRenderPassBegin, VkSubpassContents contents);
void nextSubpass(VkSubpassContents contents);
void endRenderPass();
@ -410,7 +429,7 @@ public:
VulkanCommandPool(VulkanDevice *device, int queueFamilyIndex);
~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();
@ -432,8 +451,7 @@ inline VulkanSemaphore::VulkanSemaphore(VulkanDevice *device) : device(device)
VkSemaphoreCreateInfo semaphoreInfo = {};
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
VkResult result = vkCreateSemaphore(device->device, &semaphoreInfo, nullptr, &semaphore);
if (result != VK_SUCCESS)
throw std::runtime_error("Failed to create semaphore!");
CheckVulkanError(result, "Could not create semaphore");
}
inline VulkanSemaphore::~VulkanSemaphore()
@ -448,8 +466,7 @@ inline VulkanFence::VulkanFence(VulkanDevice *device) : device(device)
VkFenceCreateInfo fenceInfo = {};
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
VkResult result = vkCreateFence(device->device, &fenceInfo, nullptr, &fence);
if (result != VK_SUCCESS)
throw std::runtime_error("Failed to create fence!");
CheckVulkanError(result, "Could not create fence!");
}
inline VulkanFence::~VulkanFence()
@ -490,8 +507,7 @@ inline VulkanCommandPool::VulkanCommandPool(VulkanDevice *device, int queueFamil
poolInfo.flags = 0;
VkResult result = vkCreateCommandPool(device->device, &poolInfo, nullptr, &pool);
if (result != VK_SUCCESS)
throw std::runtime_error("Could not create command pool");
CheckVulkanError(result, "Could not create command pool");
}
inline VulkanCommandPool::~VulkanCommandPool()
@ -511,7 +527,7 @@ inline RenderPassBegin::RenderPassBegin()
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;
return *this;
@ -526,7 +542,7 @@ inline RenderPassBegin& RenderPassBegin::RenderArea(int x, int y, int width, int
return *this;
}
inline RenderPassBegin& RenderPassBegin::Framebuffer(VulkanFramebuffer *framebuffer)
inline RenderPassBegin& RenderPassBegin::Framebuffer(VulkanFramebuffer* framebuffer)
{
renderPassInfo.framebuffer = framebuffer->framebuffer;
return *this;
@ -593,8 +609,7 @@ inline VulkanCommandBuffer::VulkanCommandBuffer(VulkanCommandPool *pool) : pool(
allocInfo.commandBufferCount = 1;
VkResult result = vkAllocateCommandBuffers(pool->device->device, &allocInfo, &buffer);
if (result != VK_SUCCESS)
throw std::runtime_error("Could not create command buffer");
CheckVulkanError(result, "Could not create command buffer");
}
inline VulkanCommandBuffer::~VulkanCommandBuffer()
@ -606,19 +621,17 @@ inline void VulkanCommandBuffer::begin()
{
VkCommandBufferBeginInfo beginInfo = {};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
beginInfo.pInheritanceInfo = nullptr;
VkResult result = vkBeginCommandBuffer(buffer, &beginInfo);
if (result != VK_SUCCESS)
throw std::runtime_error("Failed to begin recording command buffer!");
CheckVulkanError(result, "Could not begin recording command buffer");
}
inline void VulkanCommandBuffer::end()
{
VkResult result = vkEndCommandBuffer(buffer);
if (result != VK_SUCCESS)
throw std::runtime_error("Failed to record command buffer!");
CheckVulkanError(result, "Could not end command buffer recording");
}
inline void VulkanCommandBuffer::debugFullPipelineBarrier()
@ -910,6 +923,11 @@ inline void VulkanCommandBuffer::pushConstants(VkPipelineLayout layout, VkShader
vkCmdPushConstants(buffer, layout, stageFlags, offset, size, pValues);
}
inline void VulkanCommandBuffer::beginRenderPass(const RenderPassBegin &renderPassBegin, VkSubpassContents contents)
{
beginRenderPass(&renderPassBegin.renderPassInfo, contents);
}
inline void VulkanCommandBuffer::beginRenderPass(const VkRenderPassBeginInfo* pRenderPassBegin, VkSubpassContents contents)
{
vkCmdBeginRenderPass(buffer, pRenderPassBegin, contents);
@ -947,7 +965,7 @@ inline void VulkanCommandBuffer::writeAccelerationStructuresProperties(uint32_t
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);
}
inline std::unique_ptr<VulkanDescriptorSet> VulkanDescriptorPool::allocate(VulkanDescriptorSetLayout *layout)
inline std::unique_ptr<VulkanDescriptorSet> VulkanDescriptorPool::allocate(VulkanDescriptorSetLayout* layout, AllocType allocType)
{
VkDescriptorSetAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
VkDescriptorSetAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
allocInfo.descriptorPool = pool;
allocInfo.descriptorSetCount = 1;
allocInfo.pSetLayouts = &layout->layout;
VkDescriptorSet descriptorSet;
VkResult result = vkAllocateDescriptorSets(device->device, &allocInfo, &descriptorSet);
if (result != VK_SUCCESS)
throw std::runtime_error("Could not allocate descriptor sets");
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::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)
@ -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)
{
VkResult result = vkGetQueryPoolResults(device->device, pool, firstQuery, queryCount, dataSize, data, stride, flags);
if (result != VK_SUCCESS && result != VK_NOT_READY)
throw std::runtime_error("vkGetQueryPoolResults failed");
CheckVulkanError(result, "vkGetQueryPoolResults failed");
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 */
/* clang-format off */
#include "volk.h"