summaryrefslogtreecommitdiff
path: root/Source/Core/VideoBackends/Vulkan/Texture2D.cpp
diff options
context:
space:
mode:
authorStenzek <stenzek@gmail.com>2016-08-13 22:57:50 +1000
committerStenzek <stenzek@gmail.com>2016-10-01 02:40:01 +1000
commit77a128ab8707659a531029a21a2e874108eedf38 (patch)
tree7b4a52ee61208eec2bf44a7745f3dac3bed1bafb /Source/Core/VideoBackends/Vulkan/Texture2D.cpp
parentfdd954e7e757b4601eb6d2636e12623aef63c9b4 (diff)
Implement experimental Vulkan backend
Diffstat (limited to 'Source/Core/VideoBackends/Vulkan/Texture2D.cpp')
-rw-r--r--Source/Core/VideoBackends/Vulkan/Texture2D.cpp280
1 files changed, 280 insertions, 0 deletions
diff --git a/Source/Core/VideoBackends/Vulkan/Texture2D.cpp b/Source/Core/VideoBackends/Vulkan/Texture2D.cpp
new file mode 100644
index 0000000000..470328c47c
--- /dev/null
+++ b/Source/Core/VideoBackends/Vulkan/Texture2D.cpp
@@ -0,0 +1,280 @@
+// Copyright 2016 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include <algorithm>
+
+#include "VideoBackends/Vulkan/CommandBufferManager.h"
+#include "VideoBackends/Vulkan/Texture2D.h"
+#include "VideoBackends/Vulkan/VulkanContext.h"
+
+namespace Vulkan
+{
+Texture2D::Texture2D(u32 width, u32 height, u32 levels, u32 layers, VkFormat format,
+ VkSampleCountFlagBits samples, VkImageViewType view_type, VkImage image,
+ VkDeviceMemory device_memory, VkImageView view)
+ : m_width(width), m_height(height), m_levels(levels), m_layers(layers), m_format(format),
+ m_samples(samples), m_view_type(view_type), m_image(image), m_device_memory(device_memory),
+ m_view(view)
+{
+}
+
+Texture2D::~Texture2D()
+{
+ g_command_buffer_mgr->DeferResourceDestruction(m_view);
+
+ // If we don't have device memory allocated, the image is not owned by us (e.g. swapchain)
+ if (m_device_memory != VK_NULL_HANDLE)
+ {
+ g_command_buffer_mgr->DeferResourceDestruction(m_image);
+ g_command_buffer_mgr->DeferResourceDestruction(m_device_memory);
+ }
+}
+
+std::unique_ptr<Texture2D> Texture2D::Create(u32 width, u32 height, u32 levels, u32 layers,
+ VkFormat format, VkSampleCountFlagBits samples,
+ VkImageViewType view_type, VkImageTiling tiling,
+ VkImageUsageFlags usage)
+{
+ VkImageCreateInfo image_info = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
+ nullptr,
+ 0,
+ VK_IMAGE_TYPE_2D,
+ format,
+ {width, height, 1},
+ levels,
+ layers,
+ samples,
+ tiling,
+ usage,
+ VK_SHARING_MODE_EXCLUSIVE,
+ 0,
+ nullptr,
+ VK_IMAGE_LAYOUT_UNDEFINED};
+
+ VkImage image = VK_NULL_HANDLE;
+ VkResult res = vkCreateImage(g_vulkan_context->GetDevice(), &image_info, nullptr, &image);
+ if (res != VK_SUCCESS)
+ {
+ LOG_VULKAN_ERROR(res, "vkCreateImage failed: ");
+ return nullptr;
+ }
+
+ // Allocate memory to back this texture, we want device local memory in this case
+ VkMemoryRequirements memory_requirements;
+ vkGetImageMemoryRequirements(g_vulkan_context->GetDevice(), image, &memory_requirements);
+
+ VkMemoryAllocateInfo memory_info = {
+ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, nullptr, memory_requirements.size,
+ g_vulkan_context->GetMemoryType(memory_requirements.memoryTypeBits,
+ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)};
+
+ VkDeviceMemory device_memory;
+ res = vkAllocateMemory(g_vulkan_context->GetDevice(), &memory_info, nullptr, &device_memory);
+ if (res != VK_SUCCESS)
+ {
+ LOG_VULKAN_ERROR(res, "vkAllocateMemory failed: ");
+ vkDestroyImage(g_vulkan_context->GetDevice(), image, nullptr);
+ return nullptr;
+ }
+
+ res = vkBindImageMemory(g_vulkan_context->GetDevice(), image, device_memory, 0);
+ if (res != VK_SUCCESS)
+ {
+ LOG_VULKAN_ERROR(res, "vkBindImageMemory failed: ");
+ vkDestroyImage(g_vulkan_context->GetDevice(), image, nullptr);
+ vkFreeMemory(g_vulkan_context->GetDevice(), device_memory, nullptr);
+ return nullptr;
+ }
+
+ VkImageViewCreateInfo view_info = {
+ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
+ nullptr,
+ 0,
+ image,
+ view_type,
+ format,
+ {VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
+ VK_COMPONENT_SWIZZLE_IDENTITY},
+ {Util::IsDepthFormat(format) ? static_cast<VkImageAspectFlags>(VK_IMAGE_ASPECT_DEPTH_BIT) :
+ static_cast<VkImageAspectFlags>(VK_IMAGE_ASPECT_COLOR_BIT),
+ 0, levels, 0, layers}};
+
+ VkImageView view = VK_NULL_HANDLE;
+ res = vkCreateImageView(g_vulkan_context->GetDevice(), &view_info, nullptr, &view);
+ if (res != VK_SUCCESS)
+ {
+ LOG_VULKAN_ERROR(res, "vkCreateImageView failed: ");
+ vkDestroyImage(g_vulkan_context->GetDevice(), image, nullptr);
+ vkFreeMemory(g_vulkan_context->GetDevice(), device_memory, nullptr);
+ return nullptr;
+ }
+
+ return std::make_unique<Texture2D>(width, height, levels, layers, format, samples, view_type,
+ image, device_memory, view);
+}
+
+std::unique_ptr<Texture2D> Texture2D::CreateFromExistingImage(u32 width, u32 height, u32 levels,
+ u32 layers, VkFormat format,
+ VkSampleCountFlagBits samples,
+ VkImageViewType view_type,
+ VkImage existing_image)
+{
+ // Only need to create the image view, this is mainly for swap chains.
+ VkImageViewCreateInfo view_info = {
+ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
+ nullptr,
+ 0,
+ existing_image,
+ view_type,
+ format,
+ {VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
+ VK_COMPONENT_SWIZZLE_IDENTITY},
+ {Util::IsDepthFormat(format) ? static_cast<VkImageAspectFlags>(VK_IMAGE_ASPECT_DEPTH_BIT) :
+ static_cast<VkImageAspectFlags>(VK_IMAGE_ASPECT_COLOR_BIT),
+ 0, levels, 0, layers}};
+
+ VkImageView view = VK_NULL_HANDLE;
+ VkResult res = vkCreateImageView(g_vulkan_context->GetDevice(), &view_info, nullptr, &view);
+ if (res != VK_SUCCESS)
+ {
+ LOG_VULKAN_ERROR(res, "vkCreateImageView failed: ");
+ return nullptr;
+ }
+
+ return std::make_unique<Texture2D>(width, height, levels, layers, format, samples, view_type,
+ existing_image, nullptr, view);
+}
+
+void Texture2D::OverrideImageLayout(VkImageLayout new_layout)
+{
+ m_layout = new_layout;
+}
+
+void Texture2D::TransitionToLayout(VkCommandBuffer command_buffer, VkImageLayout new_layout)
+{
+ if (m_layout == new_layout)
+ return;
+
+ VkImageMemoryBarrier barrier = {
+ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType
+ nullptr, // const void* pNext
+ 0, // VkAccessFlags srcAccessMask
+ 0, // VkAccessFlags dstAccessMask
+ m_layout, // VkImageLayout oldLayout
+ new_layout, // VkImageLayout newLayout
+ VK_QUEUE_FAMILY_IGNORED, // uint32_t srcQueueFamilyIndex
+ VK_QUEUE_FAMILY_IGNORED, // uint32_t dstQueueFamilyIndex
+ m_image, // VkImage image
+ {static_cast<VkImageAspectFlags>(Util::IsDepthFormat(m_format) ? VK_IMAGE_ASPECT_DEPTH_BIT :
+ VK_IMAGE_ASPECT_COLOR_BIT),
+ 0, m_levels, 0, m_layers} // VkImageSubresourceRange subresourceRange
+ };
+
+ // srcStageMask -> Stages that must complete before the barrier
+ // dstStageMask -> Stages that must wait for after the barrier before beginning
+ VkPipelineStageFlags srcStageMask, dstStageMask;
+ switch (m_layout)
+ {
+ case VK_IMAGE_LAYOUT_UNDEFINED:
+ // Layout undefined therefore contents undefined, and we don't care what happens to it.
+ barrier.srcAccessMask = 0;
+ srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
+ break;
+
+ case VK_IMAGE_LAYOUT_PREINITIALIZED:
+ // Image has been pre-initialized by the host, so ensure all writes have completed.
+ barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
+ srcStageMask = VK_PIPELINE_STAGE_HOST_BIT;
+ break;
+
+ case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
+ // Image was being used as a color attachment, so ensure all writes have completed.
+ barrier.srcAccessMask =
+ VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+ srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ break;
+
+ case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
+ // Image was being used as a depthstencil attachment, so ensure all writes have completed.
+ barrier.srcAccessMask =
+ VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
+ srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ break;
+
+ case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
+ // Image was being used as a shader resource, make sure all reads have finished.
+ barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
+ srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
+ break;
+
+ case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
+ // Image was being used as a copy source, ensure all reads have finished.
+ barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
+ srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ break;
+
+ case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
+ // Image was being used as a copy destination, ensure all writes have finished.
+ barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ break;
+
+ default:
+ srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
+ break;
+ }
+
+ switch (new_layout)
+ {
+ case VK_IMAGE_LAYOUT_UNDEFINED:
+ barrier.dstAccessMask = 0;
+ dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
+ break;
+
+ case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
+ barrier.dstAccessMask =
+ VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+ dstStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+ break;
+
+ case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
+ barrier.dstAccessMask =
+ VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
+ dstStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+ break;
+
+ case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
+ // TODO: Can we use FRAGMENT_SHADER here? We don't sample textures in the earlier stages.
+ barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
+ dstStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+ break;
+
+ case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
+ barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
+ dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ break;
+
+ case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
+ barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ break;
+
+ case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
+ barrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
+ srcStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
+ dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
+ break;
+
+ default:
+ dstStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+ break;
+ }
+
+ vkCmdPipelineBarrier(command_buffer, srcStageMask, dstStageMask, 0, 0, nullptr, 0, nullptr, 1,
+ &barrier);
+
+ m_layout = new_layout;
+}
+
+} // namespace Vulkan