From c5961fa4143b192fd1e16b8af7f1096ae29a7238 Mon Sep 17 00:00:00 2001 From: Sergey Kosarevsky Date: Sun, 7 Apr 2024 22:30:15 -0700 Subject: [PATCH] VulkanBuffer is a `struct` --- lvk/vulkan/VulkanClasses.cpp | 478 ++++++++++++++++------------------- lvk/vulkan/VulkanClasses.h | 35 +-- 2 files changed, 230 insertions(+), 283 deletions(-) diff --git a/lvk/vulkan/VulkanClasses.cpp b/lvk/vulkan/VulkanClasses.cpp index d26b0e4547..afbfbed983 100644 --- a/lvk/vulkan/VulkanClasses.cpp +++ b/lvk/vulkan/VulkanClasses.cpp @@ -16,8 +16,8 @@ #include "VulkanClasses.h" #include "VulkanUtils.h" -#include #include +#include #include #ifndef VK_USE_PLATFORM_WIN32_KHR @@ -682,159 +682,13 @@ struct VulkanContextImpl final { } // namespace lvk -lvk::VulkanBuffer::VulkanBuffer(lvk::VulkanContext* ctx, - VkDevice device, - VkDeviceSize bufferSize, - VkBufferUsageFlags usageFlags, - VkMemoryPropertyFlags memFlags, - const char* debugName) : - ctx_(ctx), device_(device), bufferSize_(bufferSize), vkUsageFlags_(usageFlags), vkMemFlags_(memFlags) { - LVK_PROFILER_FUNCTION_COLOR(LVK_PROFILER_COLOR_CREATE); - - LVK_ASSERT(ctx); - LVK_ASSERT(bufferSize > 0); - - const VkBufferCreateInfo ci = { - .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .size = bufferSize, - .usage = usageFlags, - .sharingMode = VK_SHARING_MODE_EXCLUSIVE, - .queueFamilyIndexCount = 0, - .pQueueFamilyIndices = nullptr, - }; - - if (LVK_VULKAN_USE_VMA) { - // Initialize VmaAllocation Info - if (memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) { - vmaAllocInfo_.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; - vmaAllocInfo_.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT; - vmaAllocInfo_.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT; - } - - if (memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) { - // Check if coherent buffer is available. - VK_ASSERT(vkCreateBuffer(device_, &ci, nullptr, &vkBuffer_)); - VkMemoryRequirements requirements = {}; - vkGetBufferMemoryRequirements(device_, vkBuffer_, &requirements); - vkDestroyBuffer(device, vkBuffer_, nullptr); - vkBuffer_ = VK_NULL_HANDLE; - - if (requirements.memoryTypeBits & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) { - vmaAllocInfo_.requiredFlags |= VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; - isCoherentMemory_ = true; - } - } - - vmaAllocInfo_.usage = VMA_MEMORY_USAGE_AUTO; - - vmaCreateBuffer((VmaAllocator)ctx_->getVmaAllocator(), &ci, &vmaAllocInfo_, &vkBuffer_, &vmaAllocation_, nullptr); - - // handle memory-mapped buffers - if (memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) { - vmaMapMemory((VmaAllocator)ctx_->getVmaAllocator(), vmaAllocation_, &mappedPtr_); - } - } else { - // create buffer - VK_ASSERT(vkCreateBuffer(device_, &ci, nullptr, &vkBuffer_)); - - // back the buffer with some memory - { - VkMemoryRequirements requirements = {}; - vkGetBufferMemoryRequirements(device_, vkBuffer_, &requirements); - if (requirements.memoryTypeBits & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) { - isCoherentMemory_ = true; - } - - VK_ASSERT(lvk::allocateMemory(ctx_->getVkPhysicalDevice(), device_, &requirements, memFlags, &vkMemory_)); - VK_ASSERT(vkBindBufferMemory(device_, vkBuffer_, vkMemory_, 0)); - } - - // handle memory-mapped buffers - if (memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) { - VK_ASSERT(vkMapMemory(device_, vkMemory_, 0, bufferSize_, 0, &mappedPtr_)); - } - } - - LVK_ASSERT(vkBuffer_ != VK_NULL_HANDLE); - - // set debug name - VK_ASSERT(lvk::setDebugObjectName(device_, VK_OBJECT_TYPE_BUFFER, (uint64_t)vkBuffer_, debugName)); - - // handle shader access - if (usageFlags & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT) { - const VkBufferDeviceAddressInfo ai = { - .sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, - .buffer = vkBuffer_, - }; - vkDeviceAddress_ = vkGetBufferDeviceAddress(device_, &ai); - LVK_ASSERT(vkDeviceAddress_); - } -} - -lvk::VulkanBuffer::~VulkanBuffer() { - LVK_PROFILER_FUNCTION_COLOR(LVK_PROFILER_COLOR_DESTROY); - - if (!ctx_) { - return; - } - - if (LVK_VULKAN_USE_VMA) { - if (mappedPtr_) { - vmaUnmapMemory((VmaAllocator)ctx_->getVmaAllocator(), vmaAllocation_); - } - ctx_->deferredTask(std::packaged_task([vma = ctx_->getVmaAllocator(), buffer = vkBuffer_, allocation = vmaAllocation_]() { - vmaDestroyBuffer((VmaAllocator)vma, buffer, allocation); - })); - } else { - if (mappedPtr_) { - vkUnmapMemory(device_, vkMemory_); - } - ctx_->deferredTask(std::packaged_task([device = device_, buffer = vkBuffer_, memory = vkMemory_]() { - vkDestroyBuffer(device, buffer, nullptr); - vkFreeMemory(device, memory, nullptr); - })); - } -} - -lvk::VulkanBuffer::VulkanBuffer(VulkanBuffer&& other) : - ctx_(other.ctx_), - device_(other.device_), - vkBuffer_(other.vkBuffer_), - vkMemory_(other.vkMemory_), - vmaAllocInfo_(other.vmaAllocInfo_), - vmaAllocation_(other.vmaAllocation_), - vkDeviceAddress_(other.vkDeviceAddress_), - bufferSize_(other.bufferSize_), - vkUsageFlags_(other.vkUsageFlags_), - vkMemFlags_(other.vkMemFlags_), - mappedPtr_(other.mappedPtr_) { - other.ctx_ = nullptr; -} - -lvk::VulkanBuffer& lvk::VulkanBuffer::operator=(VulkanBuffer&& other) { - std::swap(ctx_, other.ctx_); - std::swap(device_, other.device_); - std::swap(vkBuffer_, other.vkBuffer_); - std::swap(vkMemory_, other.vkMemory_); - std::swap(vmaAllocInfo_, other.vmaAllocInfo_); - std::swap(vmaAllocation_, other.vmaAllocation_); - std::swap(vkDeviceAddress_, other.vkDeviceAddress_); - std::swap(bufferSize_, other.bufferSize_); - std::swap(vkUsageFlags_, other.vkUsageFlags_); - std::swap(vkMemFlags_, other.vkMemFlags_); - std::swap(mappedPtr_, other.mappedPtr_); - return *this; -} - -void lvk::VulkanBuffer::flushMappedMemory(VkDeviceSize offset, VkDeviceSize size) const { +void lvk::VulkanBuffer::flushMappedMemory(const VulkanContext& ctx, VkDeviceSize offset, VkDeviceSize size) const { if (!LVK_VERIFY(isMapped())) { return; } if (LVK_VULKAN_USE_VMA) { - vmaFlushAllocation((VmaAllocator)ctx_->getVmaAllocator(), vmaAllocation_, offset, size); + vmaFlushAllocation((VmaAllocator)ctx.getVmaAllocator(), vmaAllocation_, offset, size); } else { const VkMappedMemoryRange range = { .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, @@ -842,17 +696,17 @@ void lvk::VulkanBuffer::flushMappedMemory(VkDeviceSize offset, VkDeviceSize size .offset = offset, .size = size, }; - vkFlushMappedMemoryRanges(device_, 1, &range); + vkFlushMappedMemoryRanges(ctx.getVkDevice(), 1, &range); } } -void lvk::VulkanBuffer::invalidateMappedMemory(VkDeviceSize offset, VkDeviceSize size) const { +void lvk::VulkanBuffer::invalidateMappedMemory(const VulkanContext& ctx, VkDeviceSize offset, VkDeviceSize size) const { if (!LVK_VERIFY(isMapped())) { return; } if (LVK_VULKAN_USE_VMA) { - vmaInvalidateAllocation(static_cast(ctx_->getVmaAllocator()), vmaAllocation_, offset, size); + vmaInvalidateAllocation(static_cast(ctx.getVmaAllocator()), vmaAllocation_, offset, size); } else { const VkMappedMemoryRange range = { .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, @@ -860,11 +714,11 @@ void lvk::VulkanBuffer::invalidateMappedMemory(VkDeviceSize offset, VkDeviceSize .offset = offset, .size = size, }; - vkInvalidateMappedMemoryRanges(device_, 1, &range); + vkInvalidateMappedMemoryRanges(ctx.getVkDevice(), 1, &range); } } -void lvk::VulkanBuffer::getBufferSubData(size_t offset, size_t size, void* data) { +void lvk::VulkanBuffer::getBufferSubData(const VulkanContext& ctx, size_t offset, size_t size, void* data) { // only host-visible buffers can be downloaded this way LVK_ASSERT(mappedPtr_); @@ -875,14 +729,14 @@ void lvk::VulkanBuffer::getBufferSubData(size_t offset, size_t size, void* data) LVK_ASSERT(offset + size <= bufferSize_); if (!isCoherentMemory_) { - invalidateMappedMemory(offset, size); + invalidateMappedMemory(ctx, offset, size); } const uint8_t* src = static_cast(mappedPtr_) + offset; memcpy(data, src, size); } -void lvk::VulkanBuffer::bufferSubData(size_t offset, size_t size, const void* data) { +void lvk::VulkanBuffer::bufferSubData(const VulkanContext& ctx, size_t offset, size_t size, const void* data) { // only host-visible buffers can be uploaded this way LVK_ASSERT(mappedPtr_); @@ -899,7 +753,7 @@ void lvk::VulkanBuffer::bufferSubData(size_t offset, size_t size, const void* da } if (!isCoherentMemory_) { - flushMappedMemory(offset, size); + flushMappedMemory(ctx, offset, size); } } @@ -1333,8 +1187,7 @@ bool lvk::VulkanImage::isStencilFormat(VkFormat format) { (format == VK_FORMAT_D32_SFLOAT_S8_UINT); } -lvk::VulkanTexture::VulkanTexture(VulkanImage&& image, VkImageView imageView) : - image_(std::move(image)), imageView_(imageView) { +lvk::VulkanTexture::VulkanTexture(VulkanImage&& image, VkImageView imageView) : image_(std::move(image)), imageView_(imageView) { LVK_PROFILER_FUNCTION_COLOR(LVK_PROFILER_COLOR_CREATE); LVK_ASSERT(imageView_ != VK_NULL_HANDLE); @@ -1454,26 +1307,26 @@ lvk::VulkanSwapchain::VulkanSwapchain(VulkanContext& ctx, uint32_t width, uint32 const VkImageUsageFlags usageFlags = chooseUsageFlags(ctx.getVkPhysicalDevice(), ctx.vkSurface_, surfaceFormat_.format); const bool isCompositeAlphaOpaqueSupported = (ctx.deviceSurfaceCaps_.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR) != 0; const VkSwapchainCreateInfoKHR ci = { - .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, - .surface = ctx.vkSurface_, - .minImageCount = chooseSwapImageCount(ctx.deviceSurfaceCaps_), - .imageFormat = surfaceFormat_.format, - .imageColorSpace = surfaceFormat_.colorSpace, - .imageExtent = {.width = width, .height = height}, - .imageArrayLayers = 1, - .imageUsage = usageFlags, - .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, - .queueFamilyIndexCount = 1, - .pQueueFamilyIndices = &ctx.deviceQueues_.graphicsQueueFamilyIndex, + .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, + .surface = ctx.vkSurface_, + .minImageCount = chooseSwapImageCount(ctx.deviceSurfaceCaps_), + .imageFormat = surfaceFormat_.format, + .imageColorSpace = surfaceFormat_.colorSpace, + .imageExtent = {.width = width, .height = height}, + .imageArrayLayers = 1, + .imageUsage = usageFlags, + .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = 1, + .pQueueFamilyIndices = &ctx.deviceQueues_.graphicsQueueFamilyIndex, #if defined(ANDROID) - .preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR, + .preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR, #else - .preTransform = ctx.deviceSurfaceCaps_.currentTransform, + .preTransform = ctx.deviceSurfaceCaps_.currentTransform, #endif - .compositeAlpha = isCompositeAlphaOpaqueSupported ? VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR : VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR, - .presentMode = chooseSwapPresentMode(ctx.devicePresentModes_), - .clipped = VK_TRUE, - .oldSwapchain = VK_NULL_HANDLE, + .compositeAlpha = isCompositeAlphaOpaqueSupported ? VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR : VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR, + .presentMode = chooseSwapPresentMode(ctx.devicePresentModes_), + .clipped = VK_TRUE, + .oldSwapchain = VK_NULL_HANDLE, }; VK_ASSERT(vkCreateSwapchainKHR(device_, &ci, nullptr, &swapchain_)); @@ -2541,8 +2394,8 @@ void lvk::CommandBuffer::cmdBindDepthState(const DepthState& desc) { #if defined(ANDROID) // This is a workaround for the issue. - // On Android (Mali-G715-Immortalis MC11 v1.r38p1-01eac0.c1a71ccca2acf211eb87c5db5322f569) - // if depth-stencil texture is not set, call of vkCmdSetDepthCompareOp leads to disappearing of all content. + // On Android (Mali-G715-Immortalis MC11 v1.r38p1-01eac0.c1a71ccca2acf211eb87c5db5322f569) + // if depth-stencil texture is not set, call of vkCmdSetDepthCompareOp leads to disappearing of all content. if (!framebuffer_.depthStencil.texture) { return; } @@ -2711,7 +2564,7 @@ void lvk::VulkanStagingDevice::bufferSubData(VulkanBuffer& buffer, size_t dstOff LVK_PROFILER_FUNCTION(); if (buffer.isMapped()) { - buffer.bufferSubData(dstOffset, size, data); + buffer.bufferSubData(ctx_, dstOffset, size, data); return; } @@ -2723,7 +2576,7 @@ void lvk::VulkanStagingDevice::bufferSubData(VulkanBuffer& buffer, size_t dstOff const uint32_t chunkSize = std::min((uint32_t)size, desc.size_); // copy data into staging buffer - stagingBuffer->bufferSubData(desc.offset_, chunkSize, data); + stagingBuffer->bufferSubData(ctx_, desc.offset_, chunkSize, data); // do the transfer const VkBufferCopy copy = { @@ -2816,7 +2669,7 @@ void lvk::VulkanStagingDevice::imageData2D(VulkanImage& image, lvk::VulkanBuffer* stagingBuffer = ctx_.buffersPool_.get(stagingBuffer_); - stagingBuffer->bufferSubData(desc.offset_, storageSize, data); + stagingBuffer->bufferSubData(ctx_, desc.offset_, storageSize, data); uint32_t offset = 0; @@ -2909,7 +2762,7 @@ void lvk::VulkanStagingDevice::imageData3D(VulkanImage& image, lvk::VulkanBuffer* stagingBuffer = ctx_.buffersPool_.get(stagingBuffer_); // 1. Copy the pixel data into the host visible staging buffer - stagingBuffer->bufferSubData(desc.offset_, storageSize, data); + stagingBuffer->bufferSubData(ctx_, desc.offset_, storageSize, data); auto& wrapper = immediate_->acquire(); @@ -3015,7 +2868,7 @@ void lvk::VulkanStagingDevice::getImageData(VulkanImage& image, waitAndReset(); if (!stagingBuffer->isCoherentMemory_) { - stagingBuffer->invalidateMappedMemory(desc.offset_, desc.size_); + stagingBuffer->invalidateMappedMemory(ctx_, desc.offset_, desc.size_); } // 3. Copy data from staging buffer into data @@ -3770,8 +3623,8 @@ VkPipeline lvk::VulkanContext::getVkPipeline(ComputePipelineHandle handle) { if (cps->lastVkDescriptorSetLayout_ != vkDSL_) { deferredTask( std::packaged_task([device = vkDevice_, pipeline = cps->pipeline_]() { vkDestroyPipeline(device, pipeline, nullptr); })); - deferredTask( - std::packaged_task([device = vkDevice_, layout = cps->pipelineLayout_]() { vkDestroyPipelineLayout(device, layout, nullptr); })); + deferredTask(std::packaged_task( + [device = vkDevice_, layout = cps->pipelineLayout_]() { vkDestroyPipelineLayout(device, layout, nullptr); })); cps->pipeline_ = VK_NULL_HANDLE; cps->pipelineLayout_ = VK_NULL_HANDLE; cps->lastVkDescriptorSetLayout_ = vkDSL_; @@ -3936,7 +3789,29 @@ void lvk::VulkanContext::destroy(SamplerHandle handle) { } void lvk::VulkanContext::destroy(BufferHandle handle) { - // deferred deletion handled in VulkanBuffer + LVK_PROFILER_FUNCTION_COLOR(LVK_PROFILER_COLOR_DESTROY); + + lvk::VulkanBuffer* buf = buffersPool_.get(handle); + + if (buf) { + if (LVK_VULKAN_USE_VMA) { + if (buf->mappedPtr_) { + vmaUnmapMemory((VmaAllocator)getVmaAllocator(), buf->vmaAllocation_); + } + deferredTask(std::packaged_task([vma = getVmaAllocator(), buffer = buf->vkBuffer_, allocation = buf->vmaAllocation_]() { + vmaDestroyBuffer((VmaAllocator)vma, buffer, allocation); + })); + } else { + if (buf->mappedPtr_) { + vkUnmapMemory(vkDevice_, buf->vkMemory_); + } + deferredTask(std::packaged_task([device = vkDevice_, buffer = buf->vkBuffer_, memory = buf->vkMemory_]() { + vkDestroyBuffer(device, buffer, nullptr); + vkFreeMemory(device, memory, nullptr); + })); + } + } + buffersPool_.destroy(handle); } @@ -4021,7 +3896,7 @@ void lvk::VulkanContext::flushMappedMemory(BufferHandle handle, size_t offset, s LVK_ASSERT(buf); - buf->flushMappedMemory(offset, size); + buf->flushMappedMemory(*this, offset, size); } lvk::Result lvk::VulkanContext::download(lvk::TextureHandle handle, const TextureRangeDesc& range, void* outData) { @@ -4118,12 +3993,14 @@ void lvk::VulkanContext::generateMipmap(TextureHandle handle) const { const lvk::VulkanTexture* tex = texturesPool_.get(handle); - if (tex->image_.numLevels_ > 1) { - LVK_ASSERT(tex->image_.vkImageLayout_ != VK_IMAGE_LAYOUT_UNDEFINED); - const auto& wrapper = immediate_->acquire(); - tex->image_.generateMipmap(wrapper.cmdBuf_); - immediate_->submit(wrapper); + if (tex->image_.numLevels_ <= 1) { + return; } + + LVK_ASSERT(tex->image_.vkImageLayout_ != VK_IMAGE_LAYOUT_UNDEFINED); + const auto& wrapper = immediate_->acquire(); + tex->image_.generateMipmap(wrapper.cmdBuf_); + immediate_->submit(wrapper); } lvk::Format lvk::VulkanContext::getFormat(TextureHandle handle) const { @@ -4137,7 +4014,7 @@ lvk::Format lvk::VulkanContext::getFormat(TextureHandle handle) const { lvk::Holder lvk::VulkanContext::createShaderModule(const ShaderModuleDesc& desc, Result* outResult) { Result result; ShaderModuleState sm = desc.dataSize ? createShaderModuleFromSPIRV(desc.data, desc.dataSize, desc.debugName, &result) // binary - : createShaderModuleFromGLSL(desc.stage, desc.data, desc.debugName, &result); // text + : createShaderModuleFromGLSL(desc.stage, desc.data, desc.debugName, &result); // text if (!result.isOk()) { Result::setResult(outResult, result); @@ -4195,9 +4072,9 @@ lvk::ShaderModuleState lvk::VulkanContext::createShaderModuleFromSPIRV(const voi } lvk::ShaderModuleState lvk::VulkanContext::createShaderModuleFromGLSL(ShaderStage stage, - const char* source, - const char* debugName, - Result* outResult) const { + const char* source, + const char* debugName, + Result* outResult) const { const VkShaderStageFlagBits vkStage = shaderStageToVkShaderStage(stage); LVK_ASSERT(vkStage != VK_SHADER_STAGE_FLAG_BITS_MAX_ENUM); LVK_ASSERT(source); @@ -4373,39 +4250,39 @@ void lvk::VulkanContext::createInstance() { VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_RESERVE_BINDING_SLOT_EXT, }; #endif // ANDROID - + #if defined(__APPLE__) // Shader validation doesn't work in MoltenVK for SPIR-V 1.6 under Vulkan 1.3: // "Invalid SPIR-V binary version 1.6 for target environment SPIR-V 1.5 (under Vulkan 1.2 semantics)." const VkValidationFeatureDisableEXT validationFeaturesDisabled[] = { - VK_VALIDATION_FEATURE_DISABLE_SHADERS_EXT, - VK_VALIDATION_FEATURE_DISABLE_SHADER_VALIDATION_CACHE_EXT, + VK_VALIDATION_FEATURE_DISABLE_SHADERS_EXT, + VK_VALIDATION_FEATURE_DISABLE_SHADER_VALIDATION_CACHE_EXT, }; #endif // __APPLE__ const VkValidationFeaturesEXT features = { - .sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT, - .pNext = nullptr, + .sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT, + .pNext = nullptr, #if !defined(ANDROID) - .enabledValidationFeatureCount = config_.enableValidation ? (uint32_t)LVK_ARRAY_NUM_ELEMENTS(validationFeaturesEnabled) : 0u, - .pEnabledValidationFeatures = config_.enableValidation ? validationFeaturesEnabled : nullptr, + .enabledValidationFeatureCount = config_.enableValidation ? (uint32_t)LVK_ARRAY_NUM_ELEMENTS(validationFeaturesEnabled) : 0u, + .pEnabledValidationFeatures = config_.enableValidation ? validationFeaturesEnabled : nullptr, #endif #if defined(__APPLE__) - .disabledValidationFeatureCount = config_.enableValidation ? (uint32_t)LVK_ARRAY_NUM_ELEMENTS(validationFeaturesDisabled) : 0u, - .pDisabledValidationFeatures = config_.enableValidation ? validationFeaturesDisabled : nullptr, + .disabledValidationFeatureCount = config_.enableValidation ? (uint32_t)LVK_ARRAY_NUM_ELEMENTS(validationFeaturesDisabled) : 0u, + .pDisabledValidationFeatures = config_.enableValidation ? validationFeaturesDisabled : nullptr, #endif }; - + #if defined(__APPLE__) // https://github.com/KhronosGroup/MoltenVK/blob/main/Docs/MoltenVK_Configuration_Parameters.md const int useMetalArgumentBuffers = 1; const VkLayerSettingEXT settings[] = { - {"MoltenVK", "MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS", VK_LAYER_SETTING_TYPE_INT32_EXT, 1, &useMetalArgumentBuffers}}; + {"MoltenVK", "MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS", VK_LAYER_SETTING_TYPE_INT32_EXT, 1, &useMetalArgumentBuffers}}; const VkLayerSettingsCreateInfoEXT layerSettingsCreateInfo = { - .sType = VK_STRUCTURE_TYPE_LAYER_SETTINGS_CREATE_INFO_EXT, - .pNext = config_.enableValidation ? &features : nullptr, - .settingCount = (uint32_t)LVK_ARRAY_NUM_ELEMENTS(settings), - .pSettings = settings + .sType = VK_STRUCTURE_TYPE_LAYER_SETTINGS_CREATE_INFO_EXT, + .pNext = config_.enableValidation ? &features : nullptr, + .settingCount = (uint32_t)LVK_ARRAY_NUM_ELEMENTS(settings), + .pSettings = settings, }; #endif // __APPLE__ @@ -4424,18 +4301,18 @@ void lvk::VulkanContext::createInstance() { flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; #endif const VkInstanceCreateInfo ci = { - .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, #if defined(__APPLE__) - .pNext = &layerSettingsCreateInfo, + .pNext = &layerSettingsCreateInfo, #else - .pNext = config_.enableValidation ? &features : nullptr, + .pNext = config_.enableValidation ? &features : nullptr, #endif - .flags = flags, - .pApplicationInfo = &appInfo, - .enabledLayerCount = config_.enableValidation ? (uint32_t)LVK_ARRAY_NUM_ELEMENTS(kDefaultValidationLayers) : 0u, - .ppEnabledLayerNames = config_.enableValidation ? kDefaultValidationLayers : nullptr, - .enabledExtensionCount = numInstanceExtensions, - .ppEnabledExtensionNames = instanceExtensionNames, + .flags = flags, + .pApplicationInfo = &appInfo, + .enabledLayerCount = config_.enableValidation ? (uint32_t)LVK_ARRAY_NUM_ELEMENTS(kDefaultValidationLayers) : 0u, + .ppEnabledLayerNames = config_.enableValidation ? kDefaultValidationLayers : nullptr, + .enabledExtensionCount = numInstanceExtensions, + .ppEnabledExtensionNames = instanceExtensionNames, }; VK_ASSERT(vkCreateInstance(&ci, nullptr, &vkInstance_)); @@ -4480,10 +4357,7 @@ void lvk::VulkanContext::createSurface(void* window, void* display) { VK_ASSERT(vkCreateWin32SurfaceKHR(vkInstance_, &ci, nullptr, &vkSurface_)); #elif defined(VK_USE_PLATFORM_ANDROID_KHR) const VkAndroidSurfaceCreateInfoKHR ci = { - .sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR, - .pNext = nullptr, - .flags = 0, - .window = (ANativeWindow*)window}; + .sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR, .pNext = nullptr, .flags = 0, .window = (ANativeWindow*)window}; VK_ASSERT(vkCreateAndroidSurfaceKHR(vkInstance_, &ci, nullptr, &vkSurface_)); #elif defined(VK_USE_PLATFORM_XLIB_KHR) const VkXlibSurfaceCreateInfoKHR ci = { @@ -4661,20 +4535,20 @@ lvk::Result lvk::VulkanContext::initContext(const HWDeviceDesc& desc) { VkPhysicalDeviceFeatures deviceFeatures10 = { #if !defined(__APPLE__) - .geometryShader = VK_TRUE, - .tessellationShader = VK_TRUE, + .geometryShader = VK_TRUE, + .tessellationShader = VK_TRUE, #endif // !defined(__APPLE__) - .multiDrawIndirect = VK_TRUE, - .drawIndirectFirstInstance = VK_TRUE, - .depthBiasClamp = VK_TRUE, + .multiDrawIndirect = VK_TRUE, + .drawIndirectFirstInstance = VK_TRUE, + .depthBiasClamp = VK_TRUE, #if !defined(ANDROID) - .fillModeNonSolid = VK_TRUE, + .fillModeNonSolid = VK_TRUE, #endif - .samplerAnisotropy = VK_TRUE, + .samplerAnisotropy = VK_TRUE, #if !defined(ANDROID) - .textureCompressionBC = VK_TRUE, + .textureCompressionBC = VK_TRUE, #endif - .fragmentStoresAndAtomics = VK_TRUE, + .fragmentStoresAndAtomics = VK_TRUE, }; VkPhysicalDeviceVulkan11Features deviceFeatures11 = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES, @@ -4682,22 +4556,22 @@ lvk::Result lvk::VulkanContext::initContext(const HWDeviceDesc& desc) { .shaderDrawParameters = VK_TRUE, }; VkPhysicalDeviceVulkan12Features deviceFeatures12 = { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES, - .pNext = &deviceFeatures11, + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES, + .pNext = &deviceFeatures11, #if !defined(__APPLE__) - .drawIndirectCount = VK_TRUE, + .drawIndirectCount = VK_TRUE, #endif // !defined(__APPLE__) - .descriptorIndexing = VK_TRUE, - .shaderSampledImageArrayNonUniformIndexing = VK_TRUE, - .descriptorBindingSampledImageUpdateAfterBind = VK_TRUE, - .descriptorBindingStorageImageUpdateAfterBind = VK_TRUE, - .descriptorBindingUpdateUnusedWhilePending = VK_TRUE, - .descriptorBindingPartiallyBound = VK_TRUE, - .descriptorBindingVariableDescriptorCount = VK_TRUE, - .runtimeDescriptorArray = VK_TRUE, - .uniformBufferStandardLayout = VK_TRUE, - .timelineSemaphore = VK_TRUE, - .bufferDeviceAddress = VK_TRUE, + .descriptorIndexing = VK_TRUE, + .shaderSampledImageArrayNonUniformIndexing = VK_TRUE, + .descriptorBindingSampledImageUpdateAfterBind = VK_TRUE, + .descriptorBindingStorageImageUpdateAfterBind = VK_TRUE, + .descriptorBindingUpdateUnusedWhilePending = VK_TRUE, + .descriptorBindingPartiallyBound = VK_TRUE, + .descriptorBindingVariableDescriptorCount = VK_TRUE, + .runtimeDescriptorArray = VK_TRUE, + .uniformBufferStandardLayout = VK_TRUE, + .timelineSemaphore = VK_TRUE, + .bufferDeviceAddress = VK_TRUE, }; VkPhysicalDeviceVulkan13Features deviceFeatures13 = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES, @@ -4707,18 +4581,18 @@ lvk::Result lvk::VulkanContext::initContext(const HWDeviceDesc& desc) { .dynamicRendering = VK_TRUE, .maintenance4 = VK_TRUE, }; - + #ifdef __APPLE__ VkPhysicalDeviceExtendedDynamicStateFeaturesEXT dynamicStateFeature = { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT, - .pNext = &deviceFeatures13, - .extendedDynamicState = VK_TRUE, + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT, + .pNext = &deviceFeatures13, + .extendedDynamicState = VK_TRUE, }; VkPhysicalDeviceExtendedDynamicState2FeaturesEXT dynamicState2Feature = { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_2_FEATURES_EXT, - .pNext = &dynamicStateFeature, - .extendedDynamicState2 = VK_TRUE, + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_2_FEATURES_EXT, + .pNext = &dynamicStateFeature, + .extendedDynamicState2 = VK_TRUE, }; VkPhysicalDeviceSynchronization2FeaturesKHR synchronization2Feature = { @@ -4731,7 +4605,7 @@ lvk::Result lvk::VulkanContext::initContext(const HWDeviceDesc& desc) { #else const void* createInfoNext = &deviceFeatures13; #endif - + const VkDeviceCreateInfo ci = { .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, .pNext = createInfoNext, @@ -5142,6 +5016,10 @@ lvk::BufferHandle lvk::VulkanContext::createBuffer(VkDeviceSize bufferSize, VkMemoryPropertyFlags memFlags, lvk::Result* outResult, const char* debugName) { + LVK_PROFILER_FUNCTION_COLOR(LVK_PROFILER_COLOR_CREATE); + + LVK_ASSERT(bufferSize > 0); + #define ENSURE_BUFFER_SIZE(flag, maxSize) \ if (usageFlags & flag) { \ if (!LVK_VERIFY(bufferSize <= maxSize)) { \ @@ -5157,7 +5035,95 @@ lvk::BufferHandle lvk::VulkanContext::createBuffer(VkDeviceSize bufferSize, ENSURE_BUFFER_SIZE(VK_BUFFER_USAGE_FLAG_BITS_MAX_ENUM, limits.maxStorageBufferRange); #undef ENSURE_BUFFER_SIZE - return buffersPool_.create(VulkanBuffer(this, vkDevice_, bufferSize, usageFlags, memFlags, debugName)); + VulkanBuffer buf = { + .bufferSize_ = bufferSize, + .vkUsageFlags_ = usageFlags, + .vkMemFlags_ = memFlags, + }; + + const VkBufferCreateInfo ci = { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .size = bufferSize, + .usage = usageFlags, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = 0, + .pQueueFamilyIndices = nullptr, + }; + + if (LVK_VULKAN_USE_VMA) { + VmaAllocationCreateInfo vmaAllocInfo_ = {}; + + // Initialize VmaAllocation Info + if (memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) { + vmaAllocInfo_ = { + .flags = VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT, + .requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, + .preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT, + }; + } + + if (memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) { + // Check if coherent buffer is available. + VK_ASSERT(vkCreateBuffer(vkDevice_, &ci, nullptr, &buf.vkBuffer_)); + VkMemoryRequirements requirements = {}; + vkGetBufferMemoryRequirements(vkDevice_, buf.vkBuffer_, &requirements); + vkDestroyBuffer(vkDevice_, buf.vkBuffer_, nullptr); + buf.vkBuffer_ = VK_NULL_HANDLE; + + if (requirements.memoryTypeBits & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) { + vmaAllocInfo_.requiredFlags |= VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + buf.isCoherentMemory_ = true; + } + } + + vmaAllocInfo_.usage = VMA_MEMORY_USAGE_AUTO; + + vmaCreateBuffer((VmaAllocator)getVmaAllocator(), &ci, &vmaAllocInfo_, &buf.vkBuffer_, &buf.vmaAllocation_, nullptr); + + // handle memory-mapped buffers + if (memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) { + vmaMapMemory((VmaAllocator)getVmaAllocator(), buf.vmaAllocation_, &buf.mappedPtr_); + } + } else { + // create buffer + VK_ASSERT(vkCreateBuffer(vkDevice_, &ci, nullptr, &buf.vkBuffer_)); + + // back the buffer with some memory + { + VkMemoryRequirements requirements = {}; + vkGetBufferMemoryRequirements(vkDevice_, buf.vkBuffer_, &requirements); + if (requirements.memoryTypeBits & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) { + buf.isCoherentMemory_ = true; + } + + VK_ASSERT(lvk::allocateMemory(vkPhysicalDevice_, vkDevice_, &requirements, memFlags, &buf.vkMemory_)); + VK_ASSERT(vkBindBufferMemory(vkDevice_, buf.vkBuffer_, buf.vkMemory_, 0)); + } + + // handle memory-mapped buffers + if (memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) { + VK_ASSERT(vkMapMemory(vkDevice_, buf.vkMemory_, 0, buf.bufferSize_, 0, &buf.mappedPtr_)); + } + } + + LVK_ASSERT(buf.vkBuffer_ != VK_NULL_HANDLE); + + // set debug name + VK_ASSERT(lvk::setDebugObjectName(vkDevice_, VK_OBJECT_TYPE_BUFFER, (uint64_t)buf.vkBuffer_, debugName)); + + // handle shader access + if (usageFlags & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT) { + const VkBufferDeviceAddressInfo ai = { + .sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, + .buffer = buf.vkBuffer_, + }; + buf.vkDeviceAddress_ = vkGetBufferDeviceAddress(vkDevice_, &ai); + LVK_ASSERT(buf.vkDeviceAddress_); + } + + return buffersPool_.create(std::move(buf)); } lvk::VulkanImage lvk::VulkanContext::createImage(VkImageType imageType, diff --git a/lvk/vulkan/VulkanClasses.h b/lvk/vulkan/VulkanClasses.h index c6f185528b..a0593b7fa9 100644 --- a/lvk/vulkan/VulkanClasses.h +++ b/lvk/vulkan/VulkanClasses.h @@ -19,7 +19,7 @@ namespace lvk { class VulkanContext; -struct DeviceQueues { +struct DeviceQueues final { const static uint32_t INVALID = 0xFFFFFFFF; uint32_t graphicsQueueFamilyIndex = INVALID; uint32_t computeQueueFamilyIndex = INVALID; @@ -28,40 +28,21 @@ struct DeviceQueues { VkQueue computeQueue = VK_NULL_HANDLE; }; -class VulkanBuffer final { - public: - VulkanBuffer() = default; - VulkanBuffer(lvk::VulkanContext* ctx, - VkDevice device, - VkDeviceSize bufferSize, - VkBufferUsageFlags usageFlags, - VkMemoryPropertyFlags memFlags, - const char* debugName = nullptr); - ~VulkanBuffer(); - - VulkanBuffer(const VulkanBuffer&) = delete; - VulkanBuffer& operator=(const VulkanBuffer&) = delete; - - VulkanBuffer(VulkanBuffer&& other); - VulkanBuffer& operator=(VulkanBuffer&& other); - - void bufferSubData(size_t offset, size_t size, const void* data); - void getBufferSubData(size_t offset, size_t size, void* data); - [[nodiscard]] uint8_t* getMappedPtr() const { +struct VulkanBuffer final { + void bufferSubData(const VulkanContext& ctx, size_t offset, size_t size, const void* data); + void getBufferSubData(const VulkanContext& ctx, size_t offset, size_t size, void* data); + inline [[nodiscard]] uint8_t* getMappedPtr() const { return static_cast(mappedPtr_); } - bool isMapped() const { + inline [[nodiscard]] bool isMapped() const { return mappedPtr_ != nullptr; } - void flushMappedMemory(VkDeviceSize offset, VkDeviceSize size) const; - void invalidateMappedMemory(VkDeviceSize offset, VkDeviceSize size) const; + void flushMappedMemory(const VulkanContext& ctx, VkDeviceSize offset, VkDeviceSize size) const; + void invalidateMappedMemory(const VulkanContext& ctx, VkDeviceSize offset, VkDeviceSize size) const; public: - lvk::VulkanContext* ctx_ = nullptr; - VkDevice device_ = VK_NULL_HANDLE; VkBuffer vkBuffer_ = VK_NULL_HANDLE; VkDeviceMemory vkMemory_ = VK_NULL_HANDLE; - VmaAllocationCreateInfo vmaAllocInfo_ = {}; VmaAllocation vmaAllocation_ = VK_NULL_HANDLE; VkDeviceAddress vkDeviceAddress_ = 0; VkDeviceSize bufferSize_ = 0;