#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 onIncludeSystem) { this->onIncludeSystem = std::move(onIncludeSystem); return *this; } ShaderBuilder& ShaderBuilder::OnIncludeLocal(std::function 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 result; try { result = std::make_unique(shaderBuilder->onIncludeSystem(headerName, includerName, inclusionDepth)); } catch (const std::exception& e) { result = std::make_unique(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 result; try { result = std::make_unique(shaderBuilder->onIncludeLocal(headerName, includerName, inclusionDepth)); } catch (const std::exception& e) { result = std::make_unique(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 ShaderBuilder::Create(const char *shadername, VulkanDevice *device) { EShLanguage stage = (EShLanguage)this->stage; std::vector namesC, sourcesC; std::vector 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 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(device, shaderModule); if (debugName) obj->SetDebugName(debugName); return obj; } ///////////////////////////////////////////////////////////////////////////// CommandPoolBuilder::CommandPoolBuilder() { } CommandPoolBuilder& CommandPoolBuilder::QueueFamily(int index) { queueFamilyIndex = index; return *this; } std::unique_ptr CommandPoolBuilder::Create(VulkanDevice* device) { auto obj = std::make_unique(device, queueFamilyIndex != -1 ? queueFamilyIndex : device->GraphicsFamily); if (debugName) obj->SetDebugName(debugName); return obj; } ///////////////////////////////////////////////////////////////////////////// SemaphoreBuilder::SemaphoreBuilder() { } std::unique_ptr SemaphoreBuilder::Create(VulkanDevice* device) { auto obj = std::make_unique(device); if (debugName) obj->SetDebugName(debugName); return obj; } ///////////////////////////////////////////////////////////////////////////// FenceBuilder::FenceBuilder() { } std::unique_ptr FenceBuilder::Create(VulkanDevice* device) { auto obj = std::make_unique(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 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(device, image, allocation, imageInfo.extent.width, imageInfo.extent.height, imageInfo.mipLevels, imageInfo.arrayLayers); if (debugName) obj->SetDebugName(debugName); return obj; } std::unique_ptr 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(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 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(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 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(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 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(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 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(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 ComputePipelineBuilder::Create(VulkanDevice* device) { VkPipeline pipeline; vkCreateComputePipelines(device->device, cache ? cache->cache : VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline); auto obj = std::make_unique(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 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(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 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(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 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(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 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(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 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(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 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(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 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(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()); 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 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(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(); 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(); 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(); 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(); 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& 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 VulkanInstanceBuilder::Create() { return std::make_shared(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 VulkanSurfaceBuilder::Create(std::shared_ptr instance) { return std::make_shared(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 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 VulkanDeviceBuilder::FindDevices(const std::shared_ptr& instance) { std::vector 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 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 VulkanDeviceBuilder::Create(std::shared_ptr 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 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(instance, surface, supportedDevices[selected]); } ///////////////////////////////////////////////////////////////////////////// VulkanSwapChainBuilder::VulkanSwapChainBuilder() { } std::shared_ptr VulkanSwapChainBuilder::Create(VulkanDevice* device) { return std::make_shared(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 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; }