diff --git a/src/backend/CMakeLists.txt b/src/backend/CMakeLists.txt index e27a6c3260..7c6ddb8017 100644 --- a/src/backend/CMakeLists.txt +++ b/src/backend/CMakeLists.txt @@ -301,6 +301,8 @@ if (NXT_ENABLE_VULKAN) ${VULKAN_DIR}/MemoryAllocator.h ${VULKAN_DIR}/PipelineLayoutVk.cpp ${VULKAN_DIR}/PipelineLayoutVk.h + ${VULKAN_DIR}/RenderPassVk.cpp + ${VULKAN_DIR}/RenderPassVk.h ${VULKAN_DIR}/TextureVk.cpp ${VULKAN_DIR}/TextureVk.h ${VULKAN_DIR}/VulkanBackend.cpp diff --git a/src/backend/vulkan/FencedDeleter.cpp b/src/backend/vulkan/FencedDeleter.cpp index 7d920bf435..7a911cb5d5 100644 --- a/src/backend/vulkan/FencedDeleter.cpp +++ b/src/backend/vulkan/FencedDeleter.cpp @@ -23,7 +23,10 @@ namespace backend { namespace vulkan { FencedDeleter::~FencedDeleter() { ASSERT(mBuffersToDelete.Empty()); + ASSERT(mImagesToDelete.Empty()); ASSERT(mMemoriesToDelete.Empty()); + ASSERT(mPipelineLayoutsToDelete.Empty()); + ASSERT(mRenderPassesToDelete.Empty()); } void FencedDeleter::DeleteWhenUnused(VkBuffer buffer) { @@ -42,6 +45,10 @@ namespace backend { namespace vulkan { mPipelineLayoutsToDelete.Enqueue(layout, mDevice->GetSerial()); } + void FencedDeleter::DeleteWhenUnused(VkRenderPass renderPass) { + mRenderPassesToDelete.Enqueue(renderPass, mDevice->GetSerial()); + } + void FencedDeleter::Tick(Serial completedSerial) { VkDevice vkDevice = mDevice->GetVkDevice(); @@ -65,6 +72,11 @@ namespace backend { namespace vulkan { mDevice->fn.DestroyPipelineLayout(vkDevice, layout, nullptr); } mPipelineLayoutsToDelete.ClearUpTo(completedSerial); + + for (VkRenderPass renderPass : mRenderPassesToDelete.IterateUpTo(completedSerial)) { + mDevice->fn.DestroyRenderPass(vkDevice, renderPass, nullptr); + } + mRenderPassesToDelete.ClearUpTo(completedSerial); } }} // namespace backend::vulkan diff --git a/src/backend/vulkan/FencedDeleter.h b/src/backend/vulkan/FencedDeleter.h index 7299ce4417..5de4cf9495 100644 --- a/src/backend/vulkan/FencedDeleter.h +++ b/src/backend/vulkan/FencedDeleter.h @@ -31,6 +31,7 @@ namespace backend { namespace vulkan { void DeleteWhenUnused(VkDeviceMemory memory); void DeleteWhenUnused(VkImage image); void DeleteWhenUnused(VkPipelineLayout layout); + void DeleteWhenUnused(VkRenderPass renderPass); void Tick(Serial completedSerial); @@ -40,6 +41,7 @@ namespace backend { namespace vulkan { SerialQueue mMemoriesToDelete; SerialQueue mImagesToDelete; SerialQueue mPipelineLayoutsToDelete; + SerialQueue mRenderPassesToDelete; }; }} // namespace backend::vulkan diff --git a/src/backend/vulkan/GeneratedCodeIncludes.h b/src/backend/vulkan/GeneratedCodeIncludes.h index 661b9d82c1..4d4ab07afa 100644 --- a/src/backend/vulkan/GeneratedCodeIncludes.h +++ b/src/backend/vulkan/GeneratedCodeIncludes.h @@ -15,5 +15,6 @@ #include "backend/vulkan/BufferVk.h" #include "backend/vulkan/CommandBufferVk.h" #include "backend/vulkan/PipelineLayoutVk.h" +#include "backend/vulkan/RenderPassVk.h" #include "backend/vulkan/TextureVk.h" #include "backend/vulkan/VulkanBackend.h" diff --git a/src/backend/vulkan/RenderPassVk.cpp b/src/backend/vulkan/RenderPassVk.cpp new file mode 100644 index 0000000000..99f3a699fd --- /dev/null +++ b/src/backend/vulkan/RenderPassVk.cpp @@ -0,0 +1,111 @@ +// 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. + +#include "backend/vulkan/RenderPassVk.h" + +#include "backend/vulkan/FencedDeleter.h" +#include "backend/vulkan/TextureVk.h" +#include "backend/vulkan/VulkanBackend.h" + +namespace backend { namespace vulkan { + + RenderPass::RenderPass(RenderPassBuilder* builder) + : RenderPassBase(builder), mDevice(ToBackend(builder->GetDevice())) { + // For now we only support single pass render passes. + ASSERT(GetSubpassCount() == 1); + ASSERT(GetAttachmentCount() <= kMaxColorAttachments + 1); + + const auto& subpass = GetSubpassInfo(0); + + // The Vulkan subpasses want to know the layout of the attachments with VkAttachmentRef. + // Precompute them as they must be pointer-chained in VkSubpassDescription + std::array attachmentRefs; + attachmentRefs.fill(VkAttachmentReference{VK_ATTACHMENT_UNUSED, VK_IMAGE_LAYOUT_UNDEFINED}); + + for (uint32_t i : IterateBitSet(subpass.colorAttachmentsSet)) { + attachmentRefs[i].attachment = subpass.colorAttachments[i]; + attachmentRefs[i].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + } + if (subpass.depthStencilAttachment) { + attachmentRefs[kMaxColorAttachments].attachment = subpass.depthStencilAttachment; + attachmentRefs[kMaxColorAttachments].layout = + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + } + + // Create the VkSubpassDescription that will be chained in the VkRenderPassCreateInfo + VkSubpassDescription subpassDesc; + subpassDesc.flags = 0; + subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpassDesc.inputAttachmentCount = 0; + subpassDesc.pInputAttachments = nullptr; + subpassDesc.colorAttachmentCount = kMaxColorAttachments; + subpassDesc.pColorAttachments = attachmentRefs.data(); + subpassDesc.pResolveAttachments = nullptr; + subpassDesc.pDepthStencilAttachment = &attachmentRefs[kMaxColorAttachments]; + subpassDesc.preserveAttachmentCount = 0; + subpassDesc.pPreserveAttachments = nullptr; + + // Create the VkAttachmentDescriptions that will be chained in the VkRenderPassCreateInfo + std::array attachmentDescs = {}; + for (uint32_t i = 0; i < GetAttachmentCount(); ++i) { + const auto& attachment = GetAttachmentInfo(i); + auto& attachmentDesc = attachmentDescs[i]; + + attachmentDesc.flags = 0; + attachmentDesc.format = VulkanImageFormat(attachment.format); + attachmentDesc.samples = VK_SAMPLE_COUNT_1_BIT; + attachmentDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachmentDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachmentDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachmentDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; + if (TextureFormatHasDepthOrStencil(attachment.format)) { + attachmentDesc.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + attachmentDesc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + } else { + attachmentDesc.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachmentDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + } + } + + // Chain everything in VkRenderPassCreateInfo + VkRenderPassCreateInfo createInfo; + createInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + createInfo.pNext = nullptr; + createInfo.flags = 0; + createInfo.attachmentCount = GetAttachmentCount(); + createInfo.pAttachments = attachmentDescs.data(); + createInfo.subpassCount = 1; + createInfo.pSubpasses = &subpassDesc; + createInfo.dependencyCount = 0; + createInfo.pDependencies = nullptr; + + // Create the render pass from the zillion parameters + if (mDevice->fn.CreateRenderPass(mDevice->GetVkDevice(), &createInfo, nullptr, &mHandle) != + VK_SUCCESS) { + ASSERT(false); + } + } + + RenderPass::~RenderPass() { + if (mHandle != VK_NULL_HANDLE) { + mDevice->GetFencedDeleter()->DeleteWhenUnused(mHandle); + mHandle = VK_NULL_HANDLE; + } + } + + VkRenderPass RenderPass::GetHandle() const { + return mHandle; + } + +}} // namespace backend::vulkan diff --git a/src/backend/vulkan/RenderPassVk.h b/src/backend/vulkan/RenderPassVk.h new file mode 100644 index 0000000000..5e120e8bb3 --- /dev/null +++ b/src/backend/vulkan/RenderPassVk.h @@ -0,0 +1,43 @@ +// 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_RENDERPASSVK_H_ +#define BACKEND_VULKAN_RENDERPASSVK_H_ + +#include "backend/RenderPass.h" + +#include "backend/vulkan/vulkan_platform.h" + +namespace backend { namespace vulkan { + + class Device; + + class RenderPass : public RenderPassBase { + public: + RenderPass(RenderPassBuilder* builder); + ~RenderPass(); + + // TODO(cwallez@chromium.org): We need a way to ask for a compatible VkRenderPass with the + // given load an store operations. Also they should be cached. For now this is hardcoded to + // have Load = Clear and Store = Write + VkRenderPass GetHandle() const; + + private: + VkRenderPass mHandle = VK_NULL_HANDLE; + Device* mDevice = nullptr; + }; + +}} // namespace backend::vulkan + +#endif // BACKEND_VULKAN_PIPELINELAYOUTVK_H_ diff --git a/src/backend/vulkan/TextureVk.cpp b/src/backend/vulkan/TextureVk.cpp index 49dfb41294..5ae7ab3efc 100644 --- a/src/backend/vulkan/TextureVk.cpp +++ b/src/backend/vulkan/TextureVk.cpp @@ -32,22 +32,6 @@ namespace backend { namespace vulkan { } } - // 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) { @@ -200,6 +184,22 @@ namespace backend { namespace vulkan { } // namespace + // 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(); + } + } + Texture::Texture(TextureBuilder* builder) : TextureBase(builder) { Device* device = ToBackend(GetDevice()); diff --git a/src/backend/vulkan/TextureVk.h b/src/backend/vulkan/TextureVk.h index f22cb2fe4c..bf48a65105 100644 --- a/src/backend/vulkan/TextureVk.h +++ b/src/backend/vulkan/TextureVk.h @@ -22,6 +22,8 @@ namespace backend { namespace vulkan { + VkFormat VulkanImageFormat(nxt::TextureFormat format); + class Texture : public TextureBase { public: Texture(TextureBuilder* builder); diff --git a/src/backend/vulkan/VulkanBackend.cpp b/src/backend/vulkan/VulkanBackend.cpp index 16c02cc99f..4a8c45cd84 100644 --- a/src/backend/vulkan/VulkanBackend.cpp +++ b/src/backend/vulkan/VulkanBackend.cpp @@ -20,6 +20,7 @@ #include "backend/vulkan/CommandBufferVk.h" #include "backend/vulkan/FencedDeleter.h" #include "backend/vulkan/PipelineLayoutVk.h" +#include "backend/vulkan/RenderPassVk.h" #include "backend/vulkan/TextureVk.h" #include "common/Platform.h" diff --git a/src/backend/vulkan/VulkanBackend.h b/src/backend/vulkan/VulkanBackend.h index cbb481ed9a..ce7011f2e3 100644 --- a/src/backend/vulkan/VulkanBackend.h +++ b/src/backend/vulkan/VulkanBackend.h @@ -26,7 +26,6 @@ #include "backend/Framebuffer.h" #include "backend/InputState.h" #include "backend/Queue.h" -#include "backend/RenderPass.h" #include "backend/RenderPipeline.h" #include "backend/Sampler.h" #include "backend/ShaderModule.h" @@ -55,7 +54,7 @@ namespace backend { namespace vulkan { using InputState = InputStateBase; class PipelineLayout; class Queue; - using RenderPass = RenderPassBase; + class RenderPass; using RenderPipeline = RenderPipelineBase; using Sampler = SamplerBase; using ShaderModule = ShaderModuleBase;