zdray/thirdparty/ZVulkan/src/vulkanbuilders.cpp
Magnus Norddahl 9ed4723e6e Fix misc bugs
2023-10-16 17:37:26 +02:00

2003 lines
67 KiB
C++

#include "vulkanbuilders.h"
#include "vulkansurface.h"
#include "vulkancompatibledevice.h"
#include "vulkanswapchain.h"
#include "glslang/glslang/Public/ShaderLang.h"
#include "glslang/spirv/GlslangToSpv.h"
static const TBuiltInResource DefaultTBuiltInResource = {
/* .MaxLights = */ 32,
/* .MaxClipPlanes = */ 6,
/* .MaxTextureUnits = */ 32,
/* .MaxTextureCoords = */ 32,
/* .MaxVertexAttribs = */ 64,
/* .MaxVertexUniformComponents = */ 4096,
/* .MaxVaryingFloats = */ 64,
/* .MaxVertexTextureImageUnits = */ 32,
/* .MaxCombinedTextureImageUnits = */ 80,
/* .MaxTextureImageUnits = */ 32,
/* .MaxFragmentUniformComponents = */ 4096,
/* .MaxDrawBuffers = */ 32,
/* .MaxVertexUniformVectors = */ 128,
/* .MaxVaryingVectors = */ 8,
/* .MaxFragmentUniformVectors = */ 16,
/* .MaxVertexOutputVectors = */ 16,
/* .MaxFragmentInputVectors = */ 15,
/* .MinProgramTexelOffset = */ -8,
/* .MaxProgramTexelOffset = */ 7,
/* .MaxClipDistances = */ 8,
/* .MaxComputeWorkGroupCountX = */ 65535,
/* .MaxComputeWorkGroupCountY = */ 65535,
/* .MaxComputeWorkGroupCountZ = */ 65535,
/* .MaxComputeWorkGroupSizeX = */ 1024,
/* .MaxComputeWorkGroupSizeY = */ 1024,
/* .MaxComputeWorkGroupSizeZ = */ 64,
/* .MaxComputeUniformComponents = */ 1024,
/* .MaxComputeTextureImageUnits = */ 16,
/* .MaxComputeImageUniforms = */ 8,
/* .MaxComputeAtomicCounters = */ 8,
/* .MaxComputeAtomicCounterBuffers = */ 1,
/* .MaxVaryingComponents = */ 60,
/* .MaxVertexOutputComponents = */ 64,
/* .MaxGeometryInputComponents = */ 64,
/* .MaxGeometryOutputComponents = */ 128,
/* .MaxFragmentInputComponents = */ 128,
/* .MaxImageUnits = */ 8,
/* .MaxCombinedImageUnitsAndFragmentOutputs = */ 8,
/* .MaxCombinedShaderOutputResources = */ 8,
/* .MaxImageSamples = */ 0,
/* .MaxVertexImageUniforms = */ 0,
/* .MaxTessControlImageUniforms = */ 0,
/* .MaxTessEvaluationImageUniforms = */ 0,
/* .MaxGeometryImageUniforms = */ 0,
/* .MaxFragmentImageUniforms = */ 8,
/* .MaxCombinedImageUniforms = */ 8,
/* .MaxGeometryTextureImageUnits = */ 16,
/* .MaxGeometryOutputVertices = */ 256,
/* .MaxGeometryTotalOutputComponents = */ 1024,
/* .MaxGeometryUniformComponents = */ 1024,
/* .MaxGeometryVaryingComponents = */ 64,
/* .MaxTessControlInputComponents = */ 128,
/* .MaxTessControlOutputComponents = */ 128,
/* .MaxTessControlTextureImageUnits = */ 16,
/* .MaxTessControlUniformComponents = */ 1024,
/* .MaxTessControlTotalOutputComponents = */ 4096,
/* .MaxTessEvaluationInputComponents = */ 128,
/* .MaxTessEvaluationOutputComponents = */ 128,
/* .MaxTessEvaluationTextureImageUnits = */ 16,
/* .MaxTessEvaluationUniformComponents = */ 1024,
/* .MaxTessPatchComponents = */ 120,
/* .MaxPatchVertices = */ 32,
/* .MaxTessGenLevel = */ 64,
/* .MaxViewports = */ 16,
/* .MaxVertexAtomicCounters = */ 0,
/* .MaxTessControlAtomicCounters = */ 0,
/* .MaxTessEvaluationAtomicCounters = */ 0,
/* .MaxGeometryAtomicCounters = */ 0,
/* .MaxFragmentAtomicCounters = */ 8,
/* .MaxCombinedAtomicCounters = */ 8,
/* .MaxAtomicCounterBindings = */ 1,
/* .MaxVertexAtomicCounterBuffers = */ 0,
/* .MaxTessControlAtomicCounterBuffers = */ 0,
/* .MaxTessEvaluationAtomicCounterBuffers = */ 0,
/* .MaxGeometryAtomicCounterBuffers = */ 0,
/* .MaxFragmentAtomicCounterBuffers = */ 1,
/* .MaxCombinedAtomicCounterBuffers = */ 1,
/* .MaxAtomicCounterBufferSize = */ 16384,
/* .MaxTransformFeedbackBuffers = */ 4,
/* .MaxTransformFeedbackInterleavedComponents = */ 64,
/* .MaxCullDistances = */ 8,
/* .MaxCombinedClipAndCullDistances = */ 8,
/* .MaxSamples = */ 4,
/* .maxMeshOutputVerticesNV = */ 256,
/* .maxMeshOutputPrimitivesNV = */ 512,
/* .maxMeshWorkGroupSizeX_NV = */ 32,
/* .maxMeshWorkGroupSizeY_NV = */ 1,
/* .maxMeshWorkGroupSizeZ_NV = */ 1,
/* .maxTaskWorkGroupSizeX_NV = */ 32,
/* .maxTaskWorkGroupSizeY_NV = */ 1,
/* .maxTaskWorkGroupSizeZ_NV = */ 1,
/* .maxMeshViewCountNV = */ 4,
/* .maxDualSourceDrawBuffersEXT = */ 1,
/* .limits = */ {
/* .nonInductiveForLoops = */ 1,
/* .whileLoops = */ 1,
/* .doWhileLoops = */ 1,
/* .generalUniformIndexing = */ 1,
/* .generalAttributeMatrixVectorIndexing = */ 1,
/* .generalVaryingIndexing = */ 1,
/* .generalSamplerIndexing = */ 1,
/* .generalVariableIndexing = */ 1,
/* .generalConstantMatrixVectorIndexing = */ 1,
}
};
void ShaderBuilder::Init()
{
ShInitialize();
}
void ShaderBuilder::Deinit()
{
ShFinalize();
}
ShaderBuilder::ShaderBuilder()
{
}
ShaderBuilder& ShaderBuilder::Type(ShaderType type)
{
switch (type)
{
case ShaderType::Vertex: stage = EShLanguage::EShLangVertex; break;
case ShaderType::TessControl: stage = EShLanguage::EShLangTessControl; break;
case ShaderType::TessEvaluation: stage = EShLanguage::EShLangTessEvaluation; break;
case ShaderType::Geometry: stage = EShLanguage::EShLangGeometry; break;
case ShaderType::Fragment: stage = EShLanguage::EShLangFragment; break;
case ShaderType::Compute: stage = EShLanguage::EShLangCompute; break;
}
return *this;
}
ShaderBuilder& ShaderBuilder::AddSource(const std::string& name, const std::string& code)
{
sources.push_back({ name, code });
return *this;
}
ShaderBuilder& ShaderBuilder::OnIncludeSystem(std::function<ShaderIncludeResult(std::string headerName, std::string includerName, size_t inclusionDepth)> onIncludeSystem)
{
this->onIncludeSystem = std::move(onIncludeSystem);
return *this;
}
ShaderBuilder& ShaderBuilder::OnIncludeLocal(std::function<ShaderIncludeResult(std::string headerName, std::string includerName, size_t inclusionDepth)> onIncludeLocal)
{
this->onIncludeLocal = std::move(onIncludeLocal);
return *this;
}
class ShaderBuilderIncluderImpl : public glslang::TShader::Includer
{
public:
ShaderBuilderIncluderImpl(ShaderBuilder* shaderBuilder) : shaderBuilder(shaderBuilder)
{
}
IncludeResult* includeSystem(const char* headerName, const char* includerName, size_t inclusionDepth) override
{
if (!shaderBuilder->onIncludeSystem)
{
return nullptr;
}
try
{
std::unique_ptr<ShaderIncludeResult> result;
try
{
result = std::make_unique<ShaderIncludeResult>(shaderBuilder->onIncludeSystem(headerName, includerName, inclusionDepth));
}
catch (const std::exception& e)
{
result = std::make_unique<ShaderIncludeResult>(e.what());
}
if (!result || (result->name.empty() && result->text.empty()))
{
return nullptr;
}
IncludeResult* outer = new IncludeResult(result->name, result->text.data(), result->text.size(), result.get());
result.release();
return outer;
}
catch (...)
{
return nullptr;
}
}
IncludeResult* includeLocal(const char* headerName, const char* includerName, size_t inclusionDepth) override
{
if (!shaderBuilder->onIncludeLocal)
{
return nullptr;
}
try
{
std::unique_ptr<ShaderIncludeResult> result;
try
{
result = std::make_unique<ShaderIncludeResult>(shaderBuilder->onIncludeLocal(headerName, includerName, inclusionDepth));
}
catch (const std::exception& e)
{
result = std::make_unique<ShaderIncludeResult>(e.what());
}
if (!result || (result->name.empty() && result->text.empty()))
{
return nullptr;
}
IncludeResult* outer = new IncludeResult(result->name, result->text.data(), result->text.size(), result.get());
result.release();
return outer;
}
catch (...)
{
return nullptr;
}
}
void releaseInclude(IncludeResult* result) override
{
if (result)
{
delete (ShaderIncludeResult*)result->userData;
delete result;
}
}
private:
ShaderBuilder* shaderBuilder = nullptr;
};
std::unique_ptr<VulkanShader> ShaderBuilder::Create(const char *shadername, VulkanDevice *device)
{
EShLanguage stage = (EShLanguage)this->stage;
std::vector<const char*> namesC, sourcesC;
std::vector<int> lengthsC;
for (const auto& s : sources)
{
namesC.push_back(s.first.c_str());
sourcesC.push_back(s.second.c_str());
lengthsC.push_back((int)s.second.size());
}
TBuiltInResource resources = DefaultTBuiltInResource;
glslang::TShader shader(stage);
shader.setStringsWithLengthsAndNames(sourcesC.data(), lengthsC.data(), namesC.data(), (int)sources.size());
shader.setEnvInput(glslang::EShSourceGlsl, stage, glslang::EShClientVulkan, 100);
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);
}
else
{
shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_0);
shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_0);
}
ShaderBuilderIncluderImpl includer(this);
bool compileSuccess = shader.parse(&resources, 110, false, EShMsgVulkanRules, includer);
if (!compileSuccess)
{
VulkanError((std::string("Shader compile failed: ") + shader.getInfoLog()).c_str());
}
glslang::TProgram program;
program.addShader(&shader);
bool linkSuccess = program.link(EShMsgDefault);
if (!linkSuccess)
{
VulkanError((std::string("Shader link failed: ") + program.getInfoLog()).c_str());
}
glslang::TIntermediate *intermediate = program.getIntermediate(stage);
if (!intermediate)
{
VulkanError("Internal shader compiler error");
}
glslang::SpvOptions spvOptions;
spvOptions.generateDebugInfo = false;
spvOptions.disableOptimizer = false;
spvOptions.optimizeSize = true;
std::vector<unsigned int> spirv;
spv::SpvBuildLogger logger;
glslang::GlslangToSpv(*intermediate, spirv, &logger, &spvOptions);
VkShaderModuleCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
createInfo.codeSize = spirv.size() * sizeof(unsigned int);
createInfo.pCode = spirv.data();
VkShaderModule shaderModule;
VkResult result = vkCreateShaderModule(device->device, &createInfo, nullptr, &shaderModule);
if (result != VK_SUCCESS)
VulkanError("Could not create vulkan shader module");
auto obj = std::make_unique<VulkanShader>(device, shaderModule);
if (debugName)
obj->SetDebugName(debugName);
return obj;
}
/////////////////////////////////////////////////////////////////////////////
CommandPoolBuilder::CommandPoolBuilder()
{
}
CommandPoolBuilder& CommandPoolBuilder::QueueFamily(int index)
{
queueFamilyIndex = index;
return *this;
}
std::unique_ptr<VulkanCommandPool> CommandPoolBuilder::Create(VulkanDevice* device)
{
auto obj = std::make_unique<VulkanCommandPool>(device, queueFamilyIndex != -1 ? queueFamilyIndex : device->GraphicsFamily);
if (debugName)
obj->SetDebugName(debugName);
return obj;
}
/////////////////////////////////////////////////////////////////////////////
SemaphoreBuilder::SemaphoreBuilder()
{
}
std::unique_ptr<VulkanSemaphore> SemaphoreBuilder::Create(VulkanDevice* device)
{
auto obj = std::make_unique<VulkanSemaphore>(device);
if (debugName)
obj->SetDebugName(debugName);
return obj;
}
/////////////////////////////////////////////////////////////////////////////
FenceBuilder::FenceBuilder()
{
}
std::unique_ptr<VulkanFence> FenceBuilder::Create(VulkanDevice* device)
{
auto obj = std::make_unique<VulkanFence>(device);
if (debugName)
obj->SetDebugName(debugName);
return obj;
}
/////////////////////////////////////////////////////////////////////////////
ImageBuilder::ImageBuilder()
{
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.imageType = VK_IMAGE_TYPE_2D;
imageInfo.extent.depth = 1;
imageInfo.arrayLayers = 1;
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // Note: must either be VK_IMAGE_LAYOUT_UNDEFINED or VK_IMAGE_LAYOUT_PREINITIALIZED
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageInfo.flags = 0;
}
ImageBuilder& ImageBuilder::Size(int width, int height, int mipLevels, int arrayLayers)
{
imageInfo.extent.width = width;
imageInfo.extent.height = height;
imageInfo.mipLevels = mipLevels;
imageInfo.arrayLayers = arrayLayers;
return *this;
}
ImageBuilder& ImageBuilder::Samples(VkSampleCountFlagBits samples)
{
imageInfo.samples = samples;
return *this;
}
ImageBuilder& ImageBuilder::Format(VkFormat format)
{
imageInfo.format = format;
return *this;
}
ImageBuilder& ImageBuilder::LinearTiling()
{
imageInfo.tiling = VK_IMAGE_TILING_LINEAR;
return *this;
}
ImageBuilder& ImageBuilder::Usage(VkImageUsageFlags usage, VmaMemoryUsage memoryUsage, VmaAllocationCreateFlags allocFlags)
{
imageInfo.usage = usage;
allocInfo.usage = memoryUsage;
allocInfo.flags = allocFlags;
return *this;
}
ImageBuilder& ImageBuilder::MemoryType(VkMemoryPropertyFlags requiredFlags, VkMemoryPropertyFlags preferredFlags, uint32_t memoryTypeBits)
{
allocInfo.requiredFlags = requiredFlags;
allocInfo.preferredFlags = preferredFlags;
allocInfo.memoryTypeBits = memoryTypeBits;
return *this;
}
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);
if (result != VK_SUCCESS) return false;
if (imageInfo.extent.width > properties.maxExtent.width) return false;
if (imageInfo.extent.height > properties.maxExtent.height) return false;
if (imageInfo.extent.depth > properties.maxExtent.depth) return false;
if (imageInfo.mipLevels > properties.maxMipLevels) return false;
if (imageInfo.arrayLayers > properties.maxArrayLayers) return false;
if ((imageInfo.samples & properties.sampleCounts) != imageInfo.samples) return false;
if (bufferFeatures != 0)
{
VkFormatProperties formatProperties = { };
vkGetPhysicalDeviceFormatProperties(device->PhysicalDevice.Device, imageInfo.format, &formatProperties);
if ((formatProperties.bufferFeatures & bufferFeatures) != bufferFeatures)
return false;
}
return true;
}
std::unique_ptr<VulkanImage> ImageBuilder::Create(VulkanDevice* device, VkDeviceSize* allocatedBytes)
{
VkImage image;
VmaAllocation allocation;
VkResult result = vmaCreateImage(device->allocator, &imageInfo, &allocInfo, &image, &allocation, nullptr);
CheckVulkanError(result, "Could not create vulkan image");
if (allocatedBytes != nullptr)
{
VmaAllocationInfo allocatedInfo;
vmaGetAllocationInfo(device->allocator, allocation, &allocatedInfo);
*allocatedBytes = allocatedInfo.size;
}
auto obj = std::make_unique<VulkanImage>(device, image, allocation, imageInfo.extent.width, imageInfo.extent.height, imageInfo.mipLevels, imageInfo.arrayLayers);
if (debugName)
obj->SetDebugName(debugName);
return obj;
}
std::unique_ptr<VulkanImage> ImageBuilder::TryCreate(VulkanDevice* device)
{
VkImage image;
VmaAllocation allocation;
VkResult result = vmaCreateImage(device->allocator, &imageInfo, &allocInfo, &image, &allocation, nullptr);
if (result != VK_SUCCESS)
return nullptr;
auto obj = std::make_unique<VulkanImage>(device, image, allocation, imageInfo.extent.width, imageInfo.extent.height, imageInfo.mipLevels, imageInfo.arrayLayers);
if (debugName)
obj->SetDebugName(debugName);
return obj;
}
/////////////////////////////////////////////////////////////////////////////
ImageViewBuilder::ImageViewBuilder()
{
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1;
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
}
ImageViewBuilder& ImageViewBuilder::Type(VkImageViewType type)
{
viewInfo.viewType = type;
return *this;
}
ImageViewBuilder& ImageViewBuilder::Image(VulkanImage* image, VkFormat format, VkImageAspectFlags aspectMask, int mipLevel, int arrayLayer, int levelCount, int layerCount)
{
viewInfo.image = image->image;
viewInfo.format = format;
viewInfo.subresourceRange.levelCount = levelCount == 0 ? image->mipLevels : levelCount;
viewInfo.subresourceRange.aspectMask = aspectMask;
viewInfo.subresourceRange.layerCount = layerCount == 0 ? image->layerCount : layerCount;
viewInfo.subresourceRange.baseMipLevel = mipLevel;
viewInfo.subresourceRange.baseArrayLayer = arrayLayer;
return *this;
}
std::unique_ptr<VulkanImageView> ImageViewBuilder::Create(VulkanDevice* device)
{
VkImageView view;
VkResult result = vkCreateImageView(device->device, &viewInfo, nullptr, &view);
CheckVulkanError(result, "Could not create texture image view");
auto obj = std::make_unique<VulkanImageView>(device, view);
if (debugName)
obj->SetDebugName(debugName);
return obj;
}
/////////////////////////////////////////////////////////////////////////////
SamplerBuilder::SamplerBuilder()
{
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
samplerInfo.magFilter = VK_FILTER_LINEAR;
samplerInfo.minFilter = VK_FILTER_LINEAR;
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
samplerInfo.anisotropyEnable = VK_FALSE;
samplerInfo.maxAnisotropy = 1.0f;
samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
samplerInfo.unnormalizedCoordinates = VK_FALSE;
samplerInfo.compareEnable = VK_FALSE;
samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
samplerInfo.mipLodBias = 0.0f;
samplerInfo.minLod = 0.0f;
samplerInfo.maxLod = 100.0f;
}
SamplerBuilder& SamplerBuilder::AddressMode(VkSamplerAddressMode addressMode)
{
samplerInfo.addressModeU = addressMode;
samplerInfo.addressModeV = addressMode;
samplerInfo.addressModeW = addressMode;
return *this;
}
SamplerBuilder& SamplerBuilder::AddressMode(VkSamplerAddressMode u, VkSamplerAddressMode v, VkSamplerAddressMode w)
{
samplerInfo.addressModeU = u;
samplerInfo.addressModeV = v;
samplerInfo.addressModeW = w;
return *this;
}
SamplerBuilder& SamplerBuilder::MinFilter(VkFilter minFilter)
{
samplerInfo.minFilter = minFilter;
return *this;
}
SamplerBuilder& SamplerBuilder::MagFilter(VkFilter magFilter)
{
samplerInfo.magFilter = magFilter;
return *this;
}
SamplerBuilder& SamplerBuilder::MipmapMode(VkSamplerMipmapMode mode)
{
samplerInfo.mipmapMode = mode;
return *this;
}
SamplerBuilder& SamplerBuilder::Anisotropy(float maxAnisotropy)
{
samplerInfo.anisotropyEnable = VK_TRUE;
samplerInfo.maxAnisotropy = maxAnisotropy;
return *this;
}
SamplerBuilder& SamplerBuilder::MipLodBias(float bias)
{
samplerInfo.mipLodBias = bias;
return *this;
}
SamplerBuilder& SamplerBuilder::MaxLod(float value)
{
samplerInfo.maxLod = value;
return *this;
}
std::unique_ptr<VulkanSampler> SamplerBuilder::Create(VulkanDevice* device)
{
VkSampler sampler;
VkResult result = vkCreateSampler(device->device, &samplerInfo, nullptr, &sampler);
CheckVulkanError(result, "Could not create texture sampler");
auto obj = std::make_unique<VulkanSampler>(device, sampler);
if (debugName)
obj->SetDebugName(debugName);
return obj;
}
/////////////////////////////////////////////////////////////////////////////
BufferBuilder::BufferBuilder()
{
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
}
BufferBuilder& BufferBuilder::Size(size_t size)
{
bufferInfo.size = std::max(size, (size_t)16);
return *this;
}
BufferBuilder& BufferBuilder::Usage(VkBufferUsageFlags bufferUsage, VmaMemoryUsage memoryUsage, VmaAllocationCreateFlags allocFlags)
{
bufferInfo.usage = bufferUsage;
allocInfo.usage = memoryUsage;
allocInfo.flags = allocFlags;
return *this;
}
BufferBuilder& BufferBuilder::MemoryType(VkMemoryPropertyFlags requiredFlags, VkMemoryPropertyFlags preferredFlags, uint32_t memoryTypeBits)
{
allocInfo.requiredFlags = requiredFlags;
allocInfo.preferredFlags = preferredFlags;
allocInfo.memoryTypeBits = memoryTypeBits;
return *this;
}
BufferBuilder& BufferBuilder::MinAlignment(VkDeviceSize memoryAlignment)
{
minAlignment = memoryAlignment;
return *this;
}
std::unique_ptr<VulkanBuffer> BufferBuilder::Create(VulkanDevice* device)
{
VkBuffer buffer;
VmaAllocation allocation;
if (minAlignment == 0)
{
VkResult result = vmaCreateBuffer(device->allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
CheckVulkanError(result, "Could not allocate memory for vulkan buffer");
}
else
{
VkResult result = vmaCreateBufferWithAlignment(device->allocator, &bufferInfo, &allocInfo, minAlignment, &buffer, &allocation, nullptr);
CheckVulkanError(result, "Could not allocate memory for vulkan buffer");
}
auto obj = std::make_unique<VulkanBuffer>(device, buffer, allocation, bufferInfo.size);
if (debugName)
obj->SetDebugName(debugName);
return obj;
}
/////////////////////////////////////////////////////////////////////////////
AccelerationStructureBuilder::AccelerationStructureBuilder()
{
}
AccelerationStructureBuilder& AccelerationStructureBuilder::Type(VkAccelerationStructureTypeKHR type)
{
createInfo.type = type;
return *this;
}
AccelerationStructureBuilder& AccelerationStructureBuilder::Buffer(VulkanBuffer* buffer, VkDeviceSize size)
{
createInfo.buffer = buffer->buffer;
createInfo.offset = 0;
createInfo.size = size;
return *this;
}
AccelerationStructureBuilder& AccelerationStructureBuilder::Buffer(VulkanBuffer* buffer, VkDeviceSize offset, VkDeviceSize size)
{
createInfo.buffer = buffer->buffer;
createInfo.offset = offset;
createInfo.size = size;
return *this;
}
std::unique_ptr<VulkanAccelerationStructure> AccelerationStructureBuilder::Create(VulkanDevice* device)
{
VkAccelerationStructureKHR hande = {};
VkResult result = vkCreateAccelerationStructureKHR(device->device, &createInfo, nullptr, &hande);
if (result != VK_SUCCESS)
VulkanError("vkCreateAccelerationStructureKHR failed");
auto obj = std::make_unique<VulkanAccelerationStructure>(device, hande);
if (debugName)
obj->SetDebugName(debugName);
return obj;
}
/////////////////////////////////////////////////////////////////////////////
ComputePipelineBuilder::ComputePipelineBuilder()
{
pipelineInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
stageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
}
ComputePipelineBuilder& ComputePipelineBuilder::Cache(VulkanPipelineCache* cache)
{
this->cache = cache;
return *this;
}
ComputePipelineBuilder& ComputePipelineBuilder::Layout(VulkanPipelineLayout* layout)
{
pipelineInfo.layout = layout->layout;
return *this;
}
ComputePipelineBuilder& ComputePipelineBuilder::ComputeShader(VulkanShader* shader)
{
stageInfo.stage = VK_SHADER_STAGE_COMPUTE_BIT;
stageInfo.module = shader->module;
stageInfo.pName = "main";
pipelineInfo.stage = stageInfo;
return *this;
}
std::unique_ptr<VulkanPipeline> ComputePipelineBuilder::Create(VulkanDevice* device)
{
VkPipeline pipeline;
vkCreateComputePipelines(device->device, cache ? cache->cache : VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline);
auto obj = std::make_unique<VulkanPipeline>(device, pipeline);
if (debugName)
obj->SetDebugName(debugName);
return obj;
}
/////////////////////////////////////////////////////////////////////////////
DescriptorSetLayoutBuilder::DescriptorSetLayoutBuilder()
{
}
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;
binding.descriptorType = type;
binding.descriptorCount = arrayCount;
binding.stageFlags = stageFlags;
binding.pImmutableSamplers = nullptr;
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;
return *this;
}
std::unique_ptr<VulkanDescriptorSetLayout> DescriptorSetLayoutBuilder::Create(VulkanDevice* device)
{
VkDescriptorSetLayout layout;
VkResult result = vkCreateDescriptorSetLayout(device->device, &layoutInfo, nullptr, &layout);
CheckVulkanError(result, "Could not create descriptor set layout");
auto obj = std::make_unique<VulkanDescriptorSetLayout>(device, layout);
if (debugName)
obj->SetDebugName(debugName);
return obj;
}
/////////////////////////////////////////////////////////////////////////////
DescriptorPoolBuilder::DescriptorPoolBuilder()
{
poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
poolInfo.maxSets = 1;
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;
return *this;
}
DescriptorPoolBuilder& DescriptorPoolBuilder::AddPoolSize(VkDescriptorType type, int count)
{
VkDescriptorPoolSize size;
size.type = type;
size.descriptorCount = count;
poolSizes.push_back(size);
poolInfo.poolSizeCount = (uint32_t)poolSizes.size();
poolInfo.pPoolSizes = poolSizes.data();
return *this;
}
std::unique_ptr<VulkanDescriptorPool> DescriptorPoolBuilder::Create(VulkanDevice* device)
{
VkDescriptorPool descriptorPool;
VkResult result = vkCreateDescriptorPool(device->device, &poolInfo, nullptr, &descriptorPool);
CheckVulkanError(result, "Could not create descriptor pool");
auto obj = std::make_unique<VulkanDescriptorPool>(device, descriptorPool);
if (debugName)
obj->SetDebugName(debugName);
return obj;
}
/////////////////////////////////////////////////////////////////////////////
QueryPoolBuilder::QueryPoolBuilder()
{
poolInfo.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
}
QueryPoolBuilder& QueryPoolBuilder::QueryType(VkQueryType type, int count, VkQueryPipelineStatisticFlags pipelineStatistics)
{
poolInfo.queryType = type;
poolInfo.queryCount = count;
poolInfo.pipelineStatistics = pipelineStatistics;
return *this;
}
std::unique_ptr<VulkanQueryPool> QueryPoolBuilder::Create(VulkanDevice* device)
{
VkQueryPool queryPool;
VkResult result = vkCreateQueryPool(device->device, &poolInfo, nullptr, &queryPool);
CheckVulkanError(result, "Could not create query pool");
auto obj = std::make_unique<VulkanQueryPool>(device, queryPool);
if (debugName)
obj->SetDebugName(debugName);
return obj;
}
/////////////////////////////////////////////////////////////////////////////
FramebufferBuilder::FramebufferBuilder()
{
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
}
FramebufferBuilder& FramebufferBuilder::RenderPass(VulkanRenderPass* renderPass)
{
framebufferInfo.renderPass = renderPass->renderPass;
return *this;
}
FramebufferBuilder& FramebufferBuilder::AddAttachment(VulkanImageView* view)
{
attachments.push_back(view->view);
framebufferInfo.attachmentCount = (uint32_t)attachments.size();
framebufferInfo.pAttachments = attachments.data();
return *this;
}
FramebufferBuilder& FramebufferBuilder::AddAttachment(VkImageView view)
{
attachments.push_back(view);
framebufferInfo.attachmentCount = (uint32_t)attachments.size();
framebufferInfo.pAttachments = attachments.data();
return *this;
}
FramebufferBuilder& FramebufferBuilder::Size(int width, int height, int layers)
{
framebufferInfo.width = width;
framebufferInfo.height = height;
framebufferInfo.layers = 1;
return *this;
}
std::unique_ptr<VulkanFramebuffer> FramebufferBuilder::Create(VulkanDevice* device)
{
VkFramebuffer framebuffer = 0;
VkResult result = vkCreateFramebuffer(device->device, &framebufferInfo, nullptr, &framebuffer);
CheckVulkanError(result, "Could not create framebuffer");
auto obj = std::make_unique<VulkanFramebuffer>(device, framebuffer);
if (debugName)
obj->SetDebugName(debugName);
return obj;
}
/////////////////////////////////////////////////////////////////////////////
GraphicsPipelineBuilder::GraphicsPipelineBuilder()
{
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineInfo.pVertexInputState = &vertexInputInfo;
pipelineInfo.pInputAssemblyState = &inputAssembly;
pipelineInfo.pViewportState = &viewportState;
pipelineInfo.pRasterizationState = &rasterizer;
pipelineInfo.pMultisampleState = &multisampling;
pipelineInfo.pDepthStencilState = &depthStencil;
pipelineInfo.pColorBlendState = &colorBlending;
pipelineInfo.pDynamicState = &dynamicState;
pipelineInfo.subpass = 0;
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
pipelineInfo.basePipelineIndex = -1;
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertexInputInfo.vertexBindingDescriptionCount = 0;
vertexInputInfo.pVertexBindingDescriptions = nullptr;
vertexInputInfo.vertexAttributeDescriptionCount = 0;
vertexInputInfo.pVertexAttributeDescriptions = nullptr;
inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
inputAssembly.primitiveRestartEnable = VK_FALSE;
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportState.viewportCount = 1;
viewportState.pViewports = &viewport;
viewportState.scissorCount = 1;
viewportState.pScissors = &scissor;
depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
depthStencil.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
depthStencil.depthBoundsTestEnable = VK_FALSE;
depthStencil.minDepthBounds = 0.0f;
depthStencil.maxDepthBounds = 1.0f;
depthStencil.stencilTestEnable = VK_FALSE;
depthStencil.front = {};
depthStencil.back = {};
rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizer.depthClampEnable = VK_FALSE;
rasterizer.rasterizerDiscardEnable = VK_FALSE;
rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
rasterizer.lineWidth = 1.0f;
rasterizer.cullMode = VK_CULL_MODE_NONE;
rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
rasterizer.depthBiasEnable = VK_FALSE;
rasterizer.depthBiasConstantFactor = 0.0f;
rasterizer.depthBiasClamp = 0.0f;
rasterizer.depthBiasSlopeFactor = 0.0f;
multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampling.sampleShadingEnable = VK_FALSE;
multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
multisampling.minSampleShading = 1.0f;
multisampling.pSampleMask = nullptr;
multisampling.alphaToCoverageEnable = VK_FALSE;
multisampling.alphaToOneEnable = VK_FALSE;
colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
colorBlendAttachment.blendEnable = VK_FALSE;
colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlending.logicOpEnable = VK_FALSE;
colorBlending.logicOp = VK_LOGIC_OP_COPY;
colorBlending.attachmentCount = 1;
colorBlending.pAttachments = &colorBlendAttachment;
colorBlending.blendConstants[0] = 0.0f;
colorBlending.blendConstants[1] = 0.0f;
colorBlending.blendConstants[2] = 0.0f;
colorBlending.blendConstants[3] = 0.0f;
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
}
GraphicsPipelineBuilder& GraphicsPipelineBuilder::RasterizationSamples(VkSampleCountFlagBits samples)
{
multisampling.rasterizationSamples = samples;
return *this;
}
GraphicsPipelineBuilder& GraphicsPipelineBuilder::Cache(VulkanPipelineCache* cache)
{
this->cache = cache;
return *this;
}
GraphicsPipelineBuilder& GraphicsPipelineBuilder::Subpass(int subpass)
{
pipelineInfo.subpass = subpass;
return *this;
}
GraphicsPipelineBuilder& GraphicsPipelineBuilder::Layout(VulkanPipelineLayout* layout)
{
pipelineInfo.layout = layout->layout;
return *this;
}
GraphicsPipelineBuilder& GraphicsPipelineBuilder::RenderPass(VulkanRenderPass* renderPass)
{
pipelineInfo.renderPass = renderPass->renderPass;
return *this;
}
GraphicsPipelineBuilder& GraphicsPipelineBuilder::Topology(VkPrimitiveTopology topology)
{
inputAssembly.topology = topology;
return *this;
}
GraphicsPipelineBuilder& GraphicsPipelineBuilder::Viewport(float x, float y, float width, float height, float minDepth, float maxDepth)
{
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = width;
viewport.height = height;
viewport.minDepth = minDepth;
viewport.maxDepth = maxDepth;
return *this;
}
GraphicsPipelineBuilder& GraphicsPipelineBuilder::Scissor(int x, int y, int width, int height)
{
scissor.offset.x = x;
scissor.offset.y = y;
scissor.extent.width = width;
scissor.extent.height = height;
return *this;
}
GraphicsPipelineBuilder& GraphicsPipelineBuilder::Cull(VkCullModeFlags cullMode, VkFrontFace frontFace)
{
rasterizer.cullMode = cullMode;
rasterizer.frontFace = frontFace;
return *this;
}
GraphicsPipelineBuilder& GraphicsPipelineBuilder::DepthStencilEnable(bool test, bool write, bool stencil)
{
depthStencil.depthTestEnable = test ? VK_TRUE : VK_FALSE;
depthStencil.depthWriteEnable = write ? VK_TRUE : VK_FALSE;
depthStencil.stencilTestEnable = stencil ? VK_TRUE : VK_FALSE;
return *this;
}
GraphicsPipelineBuilder& GraphicsPipelineBuilder::Stencil(VkStencilOp failOp, VkStencilOp passOp, VkStencilOp depthFailOp, VkCompareOp compareOp, uint32_t compareMask, uint32_t writeMask, uint32_t reference)
{
depthStencil.front.failOp = failOp;
depthStencil.front.passOp = passOp;
depthStencil.front.depthFailOp = depthFailOp;
depthStencil.front.compareOp = compareOp;
depthStencil.front.compareMask = compareMask;
depthStencil.front.writeMask = writeMask;
depthStencil.front.reference = reference;
depthStencil.back.failOp = failOp;
depthStencil.back.passOp = passOp;
depthStencil.back.depthFailOp = depthFailOp;
depthStencil.back.compareOp = compareOp;
depthStencil.back.compareMask = compareMask;
depthStencil.back.writeMask = writeMask;
depthStencil.back.reference = reference;
return *this;
}
GraphicsPipelineBuilder& GraphicsPipelineBuilder::DepthFunc(VkCompareOp func)
{
depthStencil.depthCompareOp = func;
return *this;
}
GraphicsPipelineBuilder& GraphicsPipelineBuilder::DepthClampEnable(bool value)
{
rasterizer.depthClampEnable = value ? VK_TRUE : VK_FALSE;
return *this;
}
GraphicsPipelineBuilder& GraphicsPipelineBuilder::DepthBias(bool enable, float biasConstantFactor, float biasClamp, float biasSlopeFactor)
{
rasterizer.depthBiasEnable = enable ? VK_TRUE : VK_FALSE;
rasterizer.depthBiasConstantFactor = biasConstantFactor;
rasterizer.depthBiasClamp = biasClamp;
rasterizer.depthBiasSlopeFactor = biasSlopeFactor;
return *this;
}
GraphicsPipelineBuilder& GraphicsPipelineBuilder::ColorWriteMask(VkColorComponentFlags mask)
{
colorBlendAttachment.colorWriteMask = mask;
return *this;
}
GraphicsPipelineBuilder& GraphicsPipelineBuilder::AdditiveBlendMode()
{
colorBlendAttachment.blendEnable = VK_TRUE;
colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
return *this;
}
GraphicsPipelineBuilder& GraphicsPipelineBuilder::AlphaBlendMode()
{
colorBlendAttachment.blendEnable = VK_TRUE;
colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
return *this;
}
GraphicsPipelineBuilder& GraphicsPipelineBuilder::BlendMode(VkBlendOp op, VkBlendFactor src, VkBlendFactor dst)
{
colorBlendAttachment.blendEnable = VK_TRUE;
colorBlendAttachment.srcColorBlendFactor = src;
colorBlendAttachment.dstColorBlendFactor = dst;
colorBlendAttachment.colorBlendOp = op;
colorBlendAttachment.srcAlphaBlendFactor = src;
colorBlendAttachment.dstAlphaBlendFactor = dst;
colorBlendAttachment.alphaBlendOp = op;
return *this;
}
GraphicsPipelineBuilder& GraphicsPipelineBuilder::SubpassColorAttachmentCount(int count)
{
colorBlendAttachments.resize(count, colorBlendAttachment);
colorBlending.pAttachments = colorBlendAttachments.data();
colorBlending.attachmentCount = (uint32_t)colorBlendAttachments.size();
return *this;
}
GraphicsPipelineBuilder& GraphicsPipelineBuilder::AddVertexShader(VulkanShader* shader)
{
VkPipelineShaderStageCreateInfo vertShaderStageInfo = {};
vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
vertShaderStageInfo.module = shader->module;
vertShaderStageInfo.pName = "main";
shaderStages.push_back(vertShaderStageInfo);
pipelineInfo.stageCount = (uint32_t)shaderStages.size();
pipelineInfo.pStages = shaderStages.data();
return *this;
}
GraphicsPipelineBuilder& GraphicsPipelineBuilder::AddFragmentShader(VulkanShader* shader)
{
VkPipelineShaderStageCreateInfo fragShaderStageInfo = {};
fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
fragShaderStageInfo.module = shader->module;
fragShaderStageInfo.pName = "main";
shaderStages.push_back(fragShaderStageInfo);
pipelineInfo.stageCount = (uint32_t)shaderStages.size();
pipelineInfo.pStages = shaderStages.data();
return *this;
}
GraphicsPipelineBuilder& GraphicsPipelineBuilder::AddVertexBufferBinding(int index, size_t stride)
{
VkVertexInputBindingDescription desc = {};
desc.binding = index;
desc.stride = (uint32_t)stride;
desc.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
vertexInputBindings.push_back(desc);
vertexInputInfo.vertexBindingDescriptionCount = (uint32_t)vertexInputBindings.size();
vertexInputInfo.pVertexBindingDescriptions = vertexInputBindings.data();
return *this;
}
GraphicsPipelineBuilder& GraphicsPipelineBuilder::AddVertexAttribute(int location, int binding, VkFormat format, size_t offset)
{
VkVertexInputAttributeDescription desc = { };
desc.location = location;
desc.binding = binding;
desc.format = format;
desc.offset = (uint32_t)offset;
vertexInputAttributes.push_back(desc);
vertexInputInfo.vertexAttributeDescriptionCount = (uint32_t)vertexInputAttributes.size();
vertexInputInfo.pVertexAttributeDescriptions = vertexInputAttributes.data();
return *this;
}
GraphicsPipelineBuilder& GraphicsPipelineBuilder::AddDynamicState(VkDynamicState state)
{
dynamicStates.push_back(state);
dynamicState.dynamicStateCount = (uint32_t)dynamicStates.size();
dynamicState.pDynamicStates = dynamicStates.data();
return *this;
}
std::unique_ptr<VulkanPipeline> GraphicsPipelineBuilder::Create(VulkanDevice* device)
{
VkPipeline pipeline = 0;
VkResult result = vkCreateGraphicsPipelines(device->device, cache ? cache->cache : VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline);
CheckVulkanError(result, "Could not create graphics pipeline");
auto obj = std::make_unique<VulkanPipeline>(device, pipeline);
if (debugName)
obj->SetDebugName(debugName);
return obj;
}
/////////////////////////////////////////////////////////////////////////////
PipelineLayoutBuilder::PipelineLayoutBuilder()
{
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
}
PipelineLayoutBuilder& PipelineLayoutBuilder::AddSetLayout(VulkanDescriptorSetLayout* setLayout)
{
setLayouts.push_back(setLayout->layout);
pipelineLayoutInfo.setLayoutCount = (uint32_t)setLayouts.size();
pipelineLayoutInfo.pSetLayouts = setLayouts.data();
return *this;
}
PipelineLayoutBuilder& PipelineLayoutBuilder::AddPushConstantRange(VkShaderStageFlags stageFlags, size_t offset, size_t size)
{
VkPushConstantRange range = { };
range.stageFlags = stageFlags;
range.offset = (uint32_t)offset;
range.size = (uint32_t)size;
pushConstantRanges.push_back(range);
pipelineLayoutInfo.pushConstantRangeCount = (uint32_t)pushConstantRanges.size();
pipelineLayoutInfo.pPushConstantRanges = pushConstantRanges.data();
return *this;
}
std::unique_ptr<VulkanPipelineLayout> PipelineLayoutBuilder::Create(VulkanDevice* device)
{
VkPipelineLayout pipelineLayout;
VkResult result = vkCreatePipelineLayout(device->device, &pipelineLayoutInfo, nullptr, &pipelineLayout);
CheckVulkanError(result, "Could not create pipeline layout");
auto obj = std::make_unique<VulkanPipelineLayout>(device, pipelineLayout);
if (debugName)
obj->SetDebugName(debugName);
return obj;
}
/////////////////////////////////////////////////////////////////////////////
PipelineCacheBuilder::PipelineCacheBuilder()
{
pipelineCacheInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
}
PipelineCacheBuilder& PipelineCacheBuilder::InitialData(const void* data, size_t size)
{
initData.resize(size);
memcpy(initData.data(), data, size);
return *this;
}
PipelineCacheBuilder& PipelineCacheBuilder::Flags(VkPipelineCacheCreateFlags flags)
{
pipelineCacheInfo.flags = flags;
return *this;
}
std::unique_ptr<VulkanPipelineCache> PipelineCacheBuilder::Create(VulkanDevice* device)
{
pipelineCacheInfo.pInitialData = nullptr;
pipelineCacheInfo.initialDataSize = 0;
// Check if the saved cache data is compatible with our device:
if (initData.size() >= sizeof(VkPipelineCacheHeaderVersionOne))
{
VkPipelineCacheHeaderVersionOne* header = (VkPipelineCacheHeaderVersionOne*)initData.data();
if (header->headerVersion == VK_PIPELINE_CACHE_HEADER_VERSION_ONE &&
header->vendorID == device->PhysicalDevice.Properties.Properties.vendorID &&
header->deviceID == device->PhysicalDevice.Properties.Properties.deviceID &&
memcmp(header->pipelineCacheUUID, device->PhysicalDevice.Properties.Properties.pipelineCacheUUID, VK_UUID_SIZE) == 0)
{
pipelineCacheInfo.pInitialData = initData.data();
pipelineCacheInfo.initialDataSize = initData.size();
}
}
VkPipelineCache pipelineCache;
VkResult result = vkCreatePipelineCache(device->device, &pipelineCacheInfo, nullptr, &pipelineCache);
CheckVulkanError(result, "Could not create pipeline cache");
auto obj = std::make_unique<VulkanPipelineCache>(device, pipelineCache);
if (debugName)
obj->SetDebugName(debugName);
return obj;
}
/////////////////////////////////////////////////////////////////////////////
RenderPassBuilder::RenderPassBuilder()
{
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
}
RenderPassBuilder& RenderPassBuilder::AddAttachment(VkFormat format, VkSampleCountFlagBits samples, VkAttachmentLoadOp load, VkAttachmentStoreOp store, VkImageLayout initialLayout, VkImageLayout finalLayout)
{
VkAttachmentDescription attachment = {};
attachment.format = format;
attachment.samples = samples;
attachment.loadOp = load;
attachment.storeOp = store;
attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachment.initialLayout = initialLayout;
attachment.finalLayout = finalLayout;
attachments.push_back(attachment);
renderPassInfo.pAttachments = attachments.data();
renderPassInfo.attachmentCount = (uint32_t)attachments.size();
return *this;
}
RenderPassBuilder& RenderPassBuilder::AddDepthStencilAttachment(VkFormat format, VkSampleCountFlagBits samples, VkAttachmentLoadOp load, VkAttachmentStoreOp store, VkAttachmentLoadOp stencilLoad, VkAttachmentStoreOp stencilStore, VkImageLayout initialLayout, VkImageLayout finalLayout)
{
VkAttachmentDescription attachment = {};
attachment.format = format;
attachment.samples = samples;
attachment.loadOp = load;
attachment.storeOp = store;
attachment.stencilLoadOp = stencilLoad;
attachment.stencilStoreOp = stencilStore;
attachment.initialLayout = initialLayout;
attachment.finalLayout = finalLayout;
attachments.push_back(attachment);
renderPassInfo.pAttachments = attachments.data();
renderPassInfo.attachmentCount = (uint32_t)attachments.size();
return *this;
}
RenderPassBuilder& RenderPassBuilder::AddExternalSubpassDependency(VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask)
{
VkSubpassDependency dependency = {};
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
dependency.dstSubpass = 0;
dependency.srcStageMask = srcStageMask;
dependency.srcAccessMask = srcAccessMask;
dependency.dstStageMask = dstStageMask;
dependency.dstAccessMask = dstAccessMask;
dependencies.push_back(dependency);
renderPassInfo.pDependencies = dependencies.data();
renderPassInfo.dependencyCount = (uint32_t)dependencies.size();
return *this;
}
RenderPassBuilder& RenderPassBuilder::AddSubpass()
{
VkSubpassDescription subpass = {};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpasses.push_back(subpass);
renderPassInfo.pSubpasses = subpasses.data();
renderPassInfo.subpassCount = (uint32_t)subpasses.size();
subpassData.push_back(std::make_unique<SubpassData>());
return *this;
}
RenderPassBuilder& RenderPassBuilder::AddSubpassColorAttachmentRef(uint32_t index, VkImageLayout layout)
{
VkAttachmentReference colorAttachmentRef = {};
colorAttachmentRef.attachment = index;
colorAttachmentRef.layout = layout;
subpassData.back()->colorRefs.push_back(colorAttachmentRef);
subpasses.back().pColorAttachments = subpassData.back()->colorRefs.data();
subpasses.back().colorAttachmentCount = (uint32_t)subpassData.back()->colorRefs.size();
return *this;
}
RenderPassBuilder& RenderPassBuilder::AddSubpassDepthStencilAttachmentRef(uint32_t index, VkImageLayout layout)
{
VkAttachmentReference& depthAttachmentRef = subpassData.back()->depthRef;
depthAttachmentRef.attachment = index;
depthAttachmentRef.layout = layout;
VkSubpassDescription& subpass = subpasses.back();
subpass.pDepthStencilAttachment = &depthAttachmentRef;
return *this;
}
std::unique_ptr<VulkanRenderPass> RenderPassBuilder::Create(VulkanDevice* device)
{
VkRenderPass renderPass = 0;
VkResult result = vkCreateRenderPass(device->device, &renderPassInfo, nullptr, &renderPass);
CheckVulkanError(result, "Could not create render pass");
auto obj = std::make_unique<VulkanRenderPass>(device, renderPass);
if (debugName)
obj->SetDebugName(debugName);
return obj;
}
/////////////////////////////////////////////////////////////////////////////
PipelineBarrier& PipelineBarrier::AddMemory(VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask)
{
VkMemoryBarrier barrier = { };
barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
barrier.srcAccessMask = srcAccessMask;
barrier.dstAccessMask = dstAccessMask;
memoryBarriers.push_back(barrier);
return *this;
}
PipelineBarrier& PipelineBarrier::AddBuffer(VulkanBuffer* buffer, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask)
{
return AddBuffer(buffer, 0, buffer->size, srcAccessMask, dstAccessMask);
}
PipelineBarrier& PipelineBarrier::AddBuffer(VulkanBuffer* buffer, VkDeviceSize offset, VkDeviceSize size, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask)
{
VkBufferMemoryBarrier barrier = { };
barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
barrier.srcAccessMask = srcAccessMask;
barrier.dstAccessMask = dstAccessMask;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.buffer = buffer->buffer;
barrier.offset = offset;
barrier.size = size;
bufferMemoryBarriers.push_back(barrier);
return *this;
}
PipelineBarrier& PipelineBarrier::AddImage(VulkanImage* image, VkImageLayout oldLayout, VkImageLayout newLayout, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask, VkImageAspectFlags aspectMask, int baseMipLevel, int levelCount, int baseArrayLayer, int layerCount)
{
return AddImage(image->image, oldLayout, newLayout, srcAccessMask, dstAccessMask, aspectMask, baseMipLevel, levelCount, baseArrayLayer, layerCount);
}
PipelineBarrier& PipelineBarrier::AddImage(VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask, VkImageAspectFlags aspectMask, int baseMipLevel, int levelCount, int baseArrayLayer, int layerCount)
{
VkImageMemoryBarrier barrier = { };
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.srcAccessMask = srcAccessMask;
barrier.dstAccessMask = dstAccessMask;
barrier.oldLayout = oldLayout;
barrier.newLayout = newLayout;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = image;
barrier.subresourceRange.aspectMask = aspectMask;
barrier.subresourceRange.baseMipLevel = baseMipLevel;
barrier.subresourceRange.levelCount = levelCount;
barrier.subresourceRange.baseArrayLayer = baseArrayLayer;
barrier.subresourceRange.layerCount = layerCount;
imageMemoryBarriers.push_back(barrier);
return *this;
}
PipelineBarrier& PipelineBarrier::AddQueueTransfer(int srcFamily, int dstFamily, VulkanBuffer* buffer, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask)
{
VkBufferMemoryBarrier barrier = { };
barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
barrier.srcAccessMask = srcAccessMask;
barrier.dstAccessMask = dstAccessMask;
barrier.srcQueueFamilyIndex = srcFamily;
barrier.dstQueueFamilyIndex = dstFamily;
barrier.buffer = buffer->buffer;
barrier.offset = 0;
barrier.size = buffer->size;
bufferMemoryBarriers.push_back(barrier);
return *this;
}
PipelineBarrier& PipelineBarrier::AddQueueTransfer(int srcFamily, int dstFamily, VulkanImage* image, VkImageLayout layout, VkImageAspectFlags aspectMask, int baseMipLevel, int levelCount)
{
VkImageMemoryBarrier barrier = { };
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.oldLayout = layout;
barrier.newLayout = layout;
barrier.srcQueueFamilyIndex = srcFamily;
barrier.dstQueueFamilyIndex = dstFamily;
barrier.image = image->image;
barrier.subresourceRange.aspectMask = aspectMask;
barrier.subresourceRange.baseMipLevel = baseMipLevel;
barrier.subresourceRange.levelCount = levelCount;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
imageMemoryBarriers.push_back(barrier);
return *this;
}
void PipelineBarrier::Execute(VulkanCommandBuffer* commandBuffer, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkDependencyFlags dependencyFlags)
{
commandBuffer->pipelineBarrier(
srcStageMask, dstStageMask, dependencyFlags,
(uint32_t)memoryBarriers.size(), memoryBarriers.data(),
(uint32_t)bufferMemoryBarriers.size(), bufferMemoryBarriers.data(),
(uint32_t)imageMemoryBarriers.size(), imageMemoryBarriers.data());
}
/////////////////////////////////////////////////////////////////////////////
QueueSubmit::QueueSubmit()
{
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
}
QueueSubmit& QueueSubmit::AddCommandBuffer(VulkanCommandBuffer* buffer)
{
commandBuffers.push_back(buffer->buffer);
submitInfo.pCommandBuffers = commandBuffers.data();
submitInfo.commandBufferCount = (uint32_t)commandBuffers.size();
return *this;
}
QueueSubmit& QueueSubmit::AddWait(VkPipelineStageFlags waitStageMask, VulkanSemaphore* semaphore)
{
waitStages.push_back(waitStageMask);
waitSemaphores.push_back(semaphore->semaphore);
submitInfo.pWaitDstStageMask = waitStages.data();
submitInfo.pWaitSemaphores = waitSemaphores.data();
submitInfo.waitSemaphoreCount = (uint32_t)waitSemaphores.size();
return *this;
}
QueueSubmit& QueueSubmit::AddSignal(VulkanSemaphore* semaphore)
{
signalSemaphores.push_back(semaphore->semaphore);
submitInfo.pSignalSemaphores = signalSemaphores.data();
submitInfo.signalSemaphoreCount = (uint32_t)signalSemaphores.size();
return *this;
}
void QueueSubmit::Execute(VulkanDevice* device, VkQueue queue, VulkanFence* fence)
{
VkResult result = vkQueueSubmit(device->GraphicsQueue, 1, &submitInfo, fence ? fence->fence : VK_NULL_HANDLE);
CheckVulkanError(result, "Could not submit command buffer");
}
/////////////////////////////////////////////////////////////////////////////
WriteDescriptors& WriteDescriptors::AddBuffer(VulkanDescriptorSet* descriptorSet, int binding, VkDescriptorType type, VulkanBuffer* buffer)
{
return AddBuffer(descriptorSet, binding, type, buffer, 0, buffer->size);
}
WriteDescriptors& WriteDescriptors::AddBuffer(VulkanDescriptorSet* descriptorSet, int binding, VkDescriptorType type, VulkanBuffer* buffer, size_t offset, size_t range)
{
VkDescriptorBufferInfo bufferInfo = {};
bufferInfo.buffer = buffer->buffer;
bufferInfo.offset = offset;
bufferInfo.range = range;
auto extra = std::make_unique<WriteExtra>();
extra->bufferInfo = bufferInfo;
VkWriteDescriptorSet descriptorWrite = {};
descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrite.dstSet = descriptorSet->set;
descriptorWrite.dstBinding = binding;
descriptorWrite.dstArrayElement = 0;
descriptorWrite.descriptorType = type;
descriptorWrite.descriptorCount = 1;
descriptorWrite.pBufferInfo = &extra->bufferInfo;
writes.push_back(descriptorWrite);
writeExtras.push_back(std::move(extra));
return *this;
}
WriteDescriptors& WriteDescriptors::AddStorageImage(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_STORAGE_IMAGE;
descriptorWrite.descriptorCount = 1;
descriptorWrite.pImageInfo = &extra->imageInfo;
writes.push_back(descriptorWrite);
writeExtras.push_back(std::move(extra));
return *this;
}
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;
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 = arrayIndex;
descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptorWrite.descriptorCount = 1;
descriptorWrite.pImageInfo = &extra->imageInfo;
writes.push_back(descriptorWrite);
writeExtras.push_back(std::move(extra));
return *this;
}
WriteDescriptors& WriteDescriptors::AddAccelerationStructure(VulkanDescriptorSet* descriptorSet, int binding, VulkanAccelerationStructure* accelStruct)
{
auto extra = std::make_unique<WriteExtra>();
extra->accelStruct = {};
extra->accelStruct.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR;
extra->accelStruct.accelerationStructureCount = 1;
extra->accelStruct.pAccelerationStructures = &accelStruct->accelstruct;
VkWriteDescriptorSet descriptorWrite = {};
descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrite.dstSet = descriptorSet->set;
descriptorWrite.dstBinding = binding;
descriptorWrite.dstArrayElement = 0;
descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR;
descriptorWrite.descriptorCount = 1;
descriptorWrite.pNext = &extra->accelStruct;
writes.push_back(descriptorWrite);
writeExtras.push_back(std::move(extra));
return *this;
}
void WriteDescriptors::Execute(VulkanDevice* device)
{
if (!writes.empty())
vkUpdateDescriptorSets(device->device, (uint32_t)writes.size(), writes.data(), 0, nullptr);
}
/////////////////////////////////////////////////////////////////////////////
VulkanInstanceBuilder::VulkanInstanceBuilder()
{
apiVersionsToTry = { VK_API_VERSION_1_2, VK_API_VERSION_1_1, VK_API_VERSION_1_0 };
OptionalExtension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
}
VulkanInstanceBuilder& VulkanInstanceBuilder::ApiVersionsToTry(const std::vector<uint32_t>& versions)
{
apiVersionsToTry = versions;
return *this;
}
VulkanInstanceBuilder& VulkanInstanceBuilder::RequireExtension(const std::string& extensionName)
{
requiredExtensions.insert(extensionName);
return *this;
}
VulkanInstanceBuilder& VulkanInstanceBuilder::RequireSurfaceExtensions(bool enable)
{
if (enable)
{
RequireExtension(VK_KHR_SURFACE_EXTENSION_NAME);
OptionalExtension(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME);
#if defined(VK_USE_PLATFORM_WIN32_KHR)
RequireExtension(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
#elif defined(VK_USE_PLATFORM_MACOS_MVK)
RequireExtension(VK_MVK_MACOS_SURFACE_EXTENSION_NAME);
#elif defined(VK_USE_PLATFORM_XLIB_KHR)
RequireExtension(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
#endif
OptionalExtension(VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME); // For HDR support
}
return *this;
}
VulkanInstanceBuilder& VulkanInstanceBuilder::OptionalExtension(const std::string& extensionName)
{
optionalExtensions.insert(extensionName);
return *this;
}
VulkanInstanceBuilder& VulkanInstanceBuilder::DebugLayer(bool enable)
{
debugLayer = enable;
return *this;
}
std::shared_ptr<VulkanInstance> VulkanInstanceBuilder::Create()
{
return std::make_shared<VulkanInstance>(apiVersionsToTry, requiredExtensions, optionalExtensions, debugLayer);
}
/////////////////////////////////////////////////////////////////////////////
#ifdef VK_USE_PLATFORM_WIN32_KHR
VulkanSurfaceBuilder::VulkanSurfaceBuilder()
{
}
VulkanSurfaceBuilder& VulkanSurfaceBuilder::Win32Window(HWND hwnd)
{
this->hwnd = hwnd;
return *this;
}
std::shared_ptr<VulkanSurface> VulkanSurfaceBuilder::Create(std::shared_ptr<VulkanInstance> instance)
{
return std::make_shared<VulkanSurface>(std::move(instance), hwnd);
}
#endif
/////////////////////////////////////////////////////////////////////////////
VulkanDeviceBuilder::VulkanDeviceBuilder()
{
OptionalExtension(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME);
OptionalExtension(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
}
VulkanDeviceBuilder& VulkanDeviceBuilder::RequireExtension(const std::string& extensionName)
{
requiredDeviceExtensions.insert(extensionName);
return *this;
}
VulkanDeviceBuilder& VulkanDeviceBuilder::OptionalExtension(const std::string& extensionName)
{
optionalDeviceExtensions.insert(extensionName);
return *this;
}
VulkanDeviceBuilder& VulkanDeviceBuilder::OptionalRayQuery()
{
OptionalExtension(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME);
OptionalExtension(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME);
OptionalExtension(VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME);
OptionalExtension(VK_KHR_RAY_QUERY_EXTENSION_NAME);
return *this;
}
VulkanDeviceBuilder& VulkanDeviceBuilder::OptionalDescriptorIndexing()
{
OptionalExtension(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME);
return *this;
}
VulkanDeviceBuilder& VulkanDeviceBuilder::Surface(std::shared_ptr<VulkanSurface> surface)
{
if (surface)
{
RequireExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
#if defined(VK_USE_PLATFORM_WIN32_KHR)
OptionalExtension(VK_EXT_FULL_SCREEN_EXCLUSIVE_EXTENSION_NAME);
#endif
}
this->surface = std::move(surface);
return *this;
}
VulkanDeviceBuilder& VulkanDeviceBuilder::SelectDevice(int index)
{
deviceIndex = index;
return *this;
}
std::vector<VulkanCompatibleDevice> VulkanDeviceBuilder::FindDevices(const std::shared_ptr<VulkanInstance>& instance)
{
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;
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 ||
info.Features.Features.multiDrawIndirect != 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)
{
if (optionalDeviceExtensions.find(ext.extensionName) != optionalDeviceExtensions.end())
{
dev.EnabledDeviceExtensions.insert(ext.extensionName);
}
}
// 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.Features.multiDrawIndirect = deviceFeatures.Features.multiDrawIndirect;
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;
enabledFeatures.DescriptorIndexing.shaderSampledImageArrayNonUniformIndexing = deviceFeatures.DescriptorIndexing.shaderSampledImageArrayNonUniformIndexing;
// 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
auto sortFunc = [&](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.Properties.deviceType < 5 ? typeSort[a.Device->Properties.Properties.deviceType] : (int)a.Device->Properties.Properties.deviceType;
int sortB = b.Device->Properties.Properties.deviceType < 5 ? typeSort[b.Device->Properties.Properties.deviceType] : (int)b.Device->Properties.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.Properties.pipelineCacheUUID, b.Device->Properties.Properties.pipelineCacheUUID, VK_UUID_SIZE);
return sortUUID < 0;
};
std::stable_sort(supportedDevices.begin(), supportedDevices.end(), sortFunc);
return supportedDevices;
}
std::shared_ptr<VulkanDevice> VulkanDeviceBuilder::Create(std::shared_ptr<VulkanInstance> instance)
{
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);
if (supportedDevices.empty())
VulkanError("No Vulkan device found supports the minimum requirements of this application");
size_t selected = deviceIndex;
if (selected >= supportedDevices.size())
selected = 0;
return std::make_shared<VulkanDevice>(instance, surface, supportedDevices[selected]);
}
/////////////////////////////////////////////////////////////////////////////
VulkanSwapChainBuilder::VulkanSwapChainBuilder()
{
}
std::shared_ptr<VulkanSwapChain> VulkanSwapChainBuilder::Create(VulkanDevice* device)
{
return std::make_shared<VulkanSwapChain>(device);
}
/////////////////////////////////////////////////////////////////////////////
BufferTransfer& BufferTransfer::AddBuffer(VulkanBuffer* buffer, size_t offset, const void* data, size_t size)
{
bufferCopies.push_back({ buffer, offset, data, size, nullptr, 0 });
return *this;
}
BufferTransfer& BufferTransfer::AddBuffer(VulkanBuffer* buffer, const void* data, size_t size)
{
bufferCopies.push_back({ buffer, 0, data, size, nullptr, 0 });
return *this;
}
BufferTransfer& BufferTransfer::AddBuffer(VulkanBuffer* buffer, const void* data0, size_t size0, const void* data1, size_t size1)
{
bufferCopies.push_back({ buffer, 0, data0, size0, data1, size1 });
return *this;
}
std::unique_ptr<VulkanBuffer> BufferTransfer::Execute(VulkanDevice* device, VulkanCommandBuffer* cmdbuffer)
{
size_t transferbuffersize = 0;
for (const auto& copy : bufferCopies)
transferbuffersize += copy.size0 + copy.size1;
if (transferbuffersize == 0)
return nullptr;
auto transferBuffer = BufferBuilder()
.Usage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY)
.Size(transferbuffersize)
.DebugName("BufferTransfer.transferBuffer")
.Create(device);
uint8_t* data = (uint8_t*)transferBuffer->Map(0, transferbuffersize);
size_t pos = 0;
for (const auto& copy : bufferCopies)
{
memcpy(data + pos, copy.data0, copy.size0);
pos += copy.size0;
memcpy(data + pos, copy.data1, copy.size1);
pos += copy.size1;
}
transferBuffer->Unmap();
pos = 0;
for (const auto& copy : bufferCopies)
{
if (copy.size0 > 0)
cmdbuffer->copyBuffer(transferBuffer.get(), copy.buffer, pos, copy.offset, copy.size0);
pos += copy.size0;
if (copy.size1 > 0)
cmdbuffer->copyBuffer(transferBuffer.get(), copy.buffer, pos, copy.offset + copy.size0, copy.size1);
pos += copy.size1;
}
return transferBuffer;
}