From aa43d162ec3c8fca7db1dda34b2a5e17dac51c51 Mon Sep 17 00:00:00 2001 From: Corentin Wallez Date: Wed, 3 Jan 2018 07:58:14 -0800 Subject: [PATCH] Vulkan: Implement texture creation. This commit only implements the creation and destruction of VkImage as well as the allocation and freeing of its memory. --- src/backend/CMakeLists.txt | 2 + src/backend/vulkan/CommandBufferVk.cpp | 2 - src/backend/vulkan/FencedDeleter.cpp | 10 +- src/backend/vulkan/FencedDeleter.h | 2 + src/backend/vulkan/GeneratedCodeIncludes.h | 1 + src/backend/vulkan/TextureVk.cpp | 150 +++++++++++++++++++++ src/backend/vulkan/TextureVk.h | 46 +++++++ src/backend/vulkan/VulkanBackend.cpp | 12 +- src/backend/vulkan/VulkanBackend.h | 10 -- 9 files changed, 211 insertions(+), 24 deletions(-) create mode 100644 src/backend/vulkan/TextureVk.cpp create mode 100644 src/backend/vulkan/TextureVk.h diff --git a/src/backend/CMakeLists.txt b/src/backend/CMakeLists.txt index 515f955a47..2d6d63a16a 100644 --- a/src/backend/CMakeLists.txt +++ b/src/backend/CMakeLists.txt @@ -299,6 +299,8 @@ if (NXT_ENABLE_VULKAN) ${VULKAN_DIR}/FencedDeleter.h ${VULKAN_DIR}/MemoryAllocator.cpp ${VULKAN_DIR}/MemoryAllocator.h + ${VULKAN_DIR}/TextureVk.cpp + ${VULKAN_DIR}/TextureVk.h ${VULKAN_DIR}/VulkanBackend.cpp ${VULKAN_DIR}/VulkanBackend.h ${VULKAN_DIR}/VulkanFunctions.cpp diff --git a/src/backend/vulkan/CommandBufferVk.cpp b/src/backend/vulkan/CommandBufferVk.cpp index dd0bb6a993..51dec02378 100644 --- a/src/backend/vulkan/CommandBufferVk.cpp +++ b/src/backend/vulkan/CommandBufferVk.cpp @@ -18,8 +18,6 @@ #include "backend/vulkan/BufferVk.h" #include "backend/vulkan/VulkanBackend.h" -#include - namespace backend { namespace vulkan { CommandBuffer::CommandBuffer(CommandBufferBuilder* builder) diff --git a/src/backend/vulkan/FencedDeleter.cpp b/src/backend/vulkan/FencedDeleter.cpp index 1ee61a919b..b5e4788234 100644 --- a/src/backend/vulkan/FencedDeleter.cpp +++ b/src/backend/vulkan/FencedDeleter.cpp @@ -34,13 +34,21 @@ namespace backend { namespace vulkan { mMemoriesToDelete.Enqueue(memory, mDevice->GetSerial()); } + void FencedDeleter::DeleteWhenUnused(VkImage image) { + mImagesToDelete.Enqueue(image, mDevice->GetSerial()); + } + void FencedDeleter::Tick(Serial completedSerial) { - // Buffers and textures must be deleted before memories because it is invalid to free memory + // Buffers and images must be deleted before memories because it is invalid to free memory // that still have resources bound to it. for (VkBuffer buffer : mBuffersToDelete.IterateUpTo(completedSerial)) { mDevice->fn.DestroyBuffer(mDevice->GetVkDevice(), buffer, nullptr); } mBuffersToDelete.ClearUpTo(completedSerial); + for (VkImage image : mImagesToDelete.IterateUpTo(completedSerial)) { + mDevice->fn.DestroyImage(mDevice->GetVkDevice(), image, nullptr); + } + mImagesToDelete.ClearUpTo(completedSerial); for (VkDeviceMemory memory : mMemoriesToDelete.IterateUpTo(completedSerial)) { mDevice->fn.FreeMemory(mDevice->GetVkDevice(), memory, nullptr); diff --git a/src/backend/vulkan/FencedDeleter.h b/src/backend/vulkan/FencedDeleter.h index 0890d023f4..abb93065ee 100644 --- a/src/backend/vulkan/FencedDeleter.h +++ b/src/backend/vulkan/FencedDeleter.h @@ -29,6 +29,7 @@ namespace backend { namespace vulkan { void DeleteWhenUnused(VkBuffer buffer); void DeleteWhenUnused(VkDeviceMemory memory); + void DeleteWhenUnused(VkImage image); void Tick(Serial completedSerial); @@ -36,6 +37,7 @@ namespace backend { namespace vulkan { Device* mDevice = nullptr; SerialQueue mBuffersToDelete; SerialQueue mMemoriesToDelete; + SerialQueue mImagesToDelete; }; }} // namespace backend::vulkan diff --git a/src/backend/vulkan/GeneratedCodeIncludes.h b/src/backend/vulkan/GeneratedCodeIncludes.h index cc2dc4a082..875acfeaf9 100644 --- a/src/backend/vulkan/GeneratedCodeIncludes.h +++ b/src/backend/vulkan/GeneratedCodeIncludes.h @@ -14,4 +14,5 @@ #include "backend/vulkan/BufferVk.h" #include "backend/vulkan/CommandBufferVk.h" +#include "backend/vulkan/TextureVk.h" #include "backend/vulkan/VulkanBackend.h" diff --git a/src/backend/vulkan/TextureVk.cpp b/src/backend/vulkan/TextureVk.cpp new file mode 100644 index 0000000000..a264823b88 --- /dev/null +++ b/src/backend/vulkan/TextureVk.cpp @@ -0,0 +1,150 @@ +// Copyright 2017 The NXT Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "backend/vulkan/TextureVk.h" + +#include "backend/vulkan/FencedDeleter.h" +#include "backend/vulkan/VulkanBackend.h" + +namespace backend { namespace vulkan { + + namespace { + // Converts an NXT texture dimension to a Vulkan image type. + // Note that in Vulkan dimensionality is only 1D, 2D, 3D. Arrays and cube maps are expressed + // via the array size and a "cubemap compatible" flag. + VkImageType VulkanImageType(nxt::TextureDimension dimension) { + switch (dimension) { + case nxt::TextureDimension::e2D: + return VK_IMAGE_TYPE_2D; + default: + UNREACHABLE(); + } + } + + // Converts NXT texture format to Vulkan formats. + VkFormat VulkanImageFormat(nxt::TextureFormat format) { + switch (format) { + case nxt::TextureFormat::R8G8B8A8Unorm: + return VK_FORMAT_R8G8B8A8_UNORM; + case nxt::TextureFormat::R8G8B8A8Uint: + return VK_FORMAT_R8G8B8A8_UINT; + case nxt::TextureFormat::B8G8R8A8Unorm: + return VK_FORMAT_B8G8R8A8_UNORM; + case nxt::TextureFormat::D32FloatS8Uint: + return VK_FORMAT_D32_SFLOAT_S8_UINT; + default: + UNREACHABLE(); + } + } + + // Converts the NXT usage flags to Vulkan usage flags. Also needs the format to choose + // between color and depth attachment usages. + VkImageUsageFlags VulkanImageUsage(nxt::TextureUsageBit usage, nxt::TextureFormat format) { + VkImageUsageFlags flags = 0; + + if (usage & nxt::TextureUsageBit::TransferSrc) { + flags |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + } + if (usage & nxt::TextureUsageBit::TransferDst) { + flags |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; + } + if (usage & nxt::TextureUsageBit::Sampled) { + flags |= VK_IMAGE_USAGE_SAMPLED_BIT; + } + if (usage & nxt::TextureUsageBit::Storage) { + flags |= VK_IMAGE_USAGE_STORAGE_BIT; + } + if (usage & nxt::TextureUsageBit::OutputAttachment) { + if (TextureFormatHasDepthOrStencil(format)) { + flags |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + } else { + flags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + } + } + + return flags; + } + + } // namespace + + Texture::Texture(TextureBuilder* builder) : TextureBase(builder) { + Device* device = ToBackend(GetDevice()); + + // Create the Vulkan image "container". We don't need to check that the format supports the + // combination of sample, usage etc. because validation should have been done in the NXT + // frontend already based on the minimum supported formats in the Vulkan spec + VkImageCreateInfo createInfo; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + createInfo.pNext = nullptr; + createInfo.flags = 0; + createInfo.imageType = VulkanImageType(GetDimension()); + createInfo.format = VulkanImageFormat(GetFormat()); + createInfo.extent = VkExtent3D{GetWidth(), GetHeight(), GetDepth()}; + createInfo.mipLevels = GetNumMipLevels(); + createInfo.arrayLayers = 1; + createInfo.samples = VK_SAMPLE_COUNT_1_BIT; + createInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + createInfo.usage = VulkanImageUsage(GetAllowedUsage(), GetFormat()); + createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + createInfo.queueFamilyIndexCount = 0; + createInfo.pQueueFamilyIndices = nullptr; + createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + if (device->fn.CreateImage(device->GetVkDevice(), &createInfo, nullptr, &mHandle) != + VK_SUCCESS) { + ASSERT(false); + } + + // Create the image memory and associate it with the container + VkMemoryRequirements requirements; + device->fn.GetImageMemoryRequirements(device->GetVkDevice(), mHandle, &requirements); + + if (!device->GetMemoryAllocator()->Allocate(requirements, false, &mMemoryAllocation)) { + ASSERT(false); + } + + if (device->fn.BindImageMemory(device->GetVkDevice(), mHandle, + mMemoryAllocation.GetMemory(), + mMemoryAllocation.GetMemoryOffset()) != VK_SUCCESS) { + ASSERT(false); + } + } + + Texture::~Texture() { + Device* device = ToBackend(GetDevice()); + + // We need to free both the memory allocation and the container. Memory should be freed + // after the VkImage is destroyed and this is taken care of by the FencedDeleter. + device->GetMemoryAllocator()->Free(&mMemoryAllocation); + + if (mHandle != VK_NULL_HANDLE) { + device->GetFencedDeleter()->DeleteWhenUnused(mHandle); + mHandle = VK_NULL_HANDLE; + } + } + + VkImage Texture::GetHandle() const { + return mHandle; + } + + void Texture::RecordBarrier(VkCommandBuffer, + nxt::TextureUsageBit, + nxt::TextureUsageBit) const { + } + + void Texture::TransitionUsageImpl(nxt::TextureUsageBit, + nxt::TextureUsageBit) { + } + +}} // namespace backend::vulkan diff --git a/src/backend/vulkan/TextureVk.h b/src/backend/vulkan/TextureVk.h new file mode 100644 index 0000000000..885aa68f31 --- /dev/null +++ b/src/backend/vulkan/TextureVk.h @@ -0,0 +1,46 @@ +// Copyright 2018 The NXT Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef BACKEND_VULKAN_TEXTUREVK_H_ +#define BACKEND_VULKAN_TEXTUREVK_H_ + +#include "backend/Texture.h" + +#include "backend/vulkan/MemoryAllocator.h" +#include "backend/vulkan/vulkan_platform.h" + +namespace backend { namespace vulkan { + + class Texture : public TextureBase { + public: + Texture(TextureBuilder* builder); + ~Texture(); + + VkImage GetHandle() const; + + void RecordBarrier(VkCommandBuffer commands, + nxt::TextureUsageBit currentUsage, + nxt::TextureUsageBit targetUsage) const; + + private: + void TransitionUsageImpl(nxt::TextureUsageBit currentUsage, + nxt::TextureUsageBit targetUsage) override; + + VkImage mHandle = VK_NULL_HANDLE; + DeviceMemoryAllocation mMemoryAllocation; + }; + +}} // namespace backend::vulkan + +#endif // BACKEND_VULKAN_TEXTUREVK_H_ diff --git a/src/backend/vulkan/VulkanBackend.cpp b/src/backend/vulkan/VulkanBackend.cpp index fd3b722a1a..362ee40b2c 100644 --- a/src/backend/vulkan/VulkanBackend.cpp +++ b/src/backend/vulkan/VulkanBackend.cpp @@ -19,6 +19,7 @@ #include "backend/vulkan/BufferVk.h" #include "backend/vulkan/CommandBufferVk.h" #include "backend/vulkan/FencedDeleter.h" +#include "backend/vulkan/TextureVk.h" #include "common/Platform.h" #include @@ -601,17 +602,6 @@ namespace backend { namespace vulkan { device->SubmitPendingCommands(); } - // Texture - - Texture::Texture(TextureBuilder* builder) : TextureBase(builder) { - } - - Texture::~Texture() { - } - - void Texture::TransitionUsageImpl(nxt::TextureUsageBit, nxt::TextureUsageBit) { - } - // SwapChain SwapChain::SwapChain(SwapChainBuilder* builder) : SwapChainBase(builder) { diff --git a/src/backend/vulkan/VulkanBackend.h b/src/backend/vulkan/VulkanBackend.h index 0af323476b..ec72abd69c 100644 --- a/src/backend/vulkan/VulkanBackend.h +++ b/src/backend/vulkan/VulkanBackend.h @@ -215,16 +215,6 @@ namespace backend { namespace vulkan { void Submit(uint32_t numCommands, CommandBuffer* const* commands); }; - class Texture : public TextureBase { - public: - Texture(TextureBuilder* builder); - ~Texture(); - - private: - void TransitionUsageImpl(nxt::TextureUsageBit currentUsage, - nxt::TextureUsageBit targetUsage) override; - }; - class SwapChain : public SwapChainBase { public: SwapChain(SwapChainBuilder* builder);