diff --git a/ci/licenses_golden/excluded_files b/ci/licenses_golden/excluded_files index 1cf139cf375e2..2d3b7636892fb 100644 --- a/ci/licenses_golden/excluded_files +++ b/ci/licenses_golden/excluded_files @@ -144,6 +144,8 @@ ../../../flutter/impeller/golden_tests_harvester/test ../../../flutter/impeller/image/README.md ../../../flutter/impeller/playground +../../../flutter/impeller/renderer/backend/vulkan/blit_command_vk_unittests.cc +../../../flutter/impeller/renderer/backend/vulkan/test ../../../flutter/impeller/renderer/capabilities_unittests.cc ../../../flutter/impeller/renderer/compute_subgroup_unittests.cc ../../../flutter/impeller/renderer/compute_unittests.cc diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index 6ca7bde884982..fb111b0e4c31e 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -97,6 +97,10 @@ impeller_component("impeller_unittests") { ] } + if (impeller_enable_vulkan) { + deps += [ "//flutter/impeller/renderer/backend/vulkan:vulkan_unittests" ] + } + if (impeller_enable_compute) { deps += [ "renderer:compute_tessellation_unittests" ] } diff --git a/impeller/renderer/backend/vulkan/BUILD.gn b/impeller/renderer/backend/vulkan/BUILD.gn index 303d5f6f1c8c8..a2386ddbc3d22 100644 --- a/impeller/renderer/backend/vulkan/BUILD.gn +++ b/impeller/renderer/backend/vulkan/BUILD.gn @@ -4,6 +4,19 @@ import("../../../tools/impeller.gni") +impeller_component("vulkan_unittests") { + testonly = true + sources = [ + "blit_command_vk_unittests.cc", + "test/mock_vulkan.cc", + "test/mock_vulkan.h", + ] + deps = [ + ":vulkan", + "//flutter/testing:testing_lib", + ] +} + impeller_component("vulkan") { sources = [ "allocator_vk.cc", diff --git a/impeller/renderer/backend/vulkan/blit_command_vk.cc b/impeller/renderer/backend/vulkan/blit_command_vk.cc index 1c45c3411deb9..edf2cd73dc555 100644 --- a/impeller/renderer/backend/vulkan/blit_command_vk.cc +++ b/impeller/renderer/backend/vulkan/blit_command_vk.cc @@ -100,7 +100,7 @@ bool BlitCopyTextureToBufferCommandVK::Encode(CommandEncoderVK& encoder) const { // cast source and destination to TextureVK const auto& src = TextureVK::Cast(*source); - if (!encoder.Track(source)) { + if (!encoder.Track(source) || !encoder.Track(destination)) { return false; } diff --git a/impeller/renderer/backend/vulkan/blit_command_vk_unittests.cc b/impeller/renderer/backend/vulkan/blit_command_vk_unittests.cc new file mode 100644 index 0000000000000..a6f871d64c694 --- /dev/null +++ b/impeller/renderer/backend/vulkan/blit_command_vk_unittests.cc @@ -0,0 +1,65 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/testing/testing.h" +#include "impeller/renderer/backend/vulkan/blit_command_vk.h" +#include "impeller/renderer/backend/vulkan/command_encoder_vk.h" +#include "impeller/renderer/backend/vulkan/test/mock_vulkan.h" + +namespace impeller { +namespace testing { + +TEST(BlitCommandVkTest, BlitCopyTextureToTextureCommandVK) { + auto context = CreateMockVulkanContext(); + auto pool = CommandPoolVK::GetThreadLocal(context.get()); + CommandEncoderVK encoder(context->GetDevice(), context->GetGraphicsQueue(), + pool, context->GetFenceWaiter()); + BlitCopyTextureToTextureCommandVK cmd; + cmd.source = context->GetResourceAllocator()->CreateTexture({ + .size = ISize(100, 100), + }); + cmd.destination = context->GetResourceAllocator()->CreateTexture({ + .size = ISize(100, 100), + }); + bool result = cmd.Encode(encoder); + EXPECT_TRUE(result); + EXPECT_TRUE(encoder.IsTracking(cmd.source)); + EXPECT_TRUE(encoder.IsTracking(cmd.destination)); +} + +TEST(BlitCommandVkTest, BlitCopyTextureToBufferCommandVK) { + auto context = CreateMockVulkanContext(); + auto pool = CommandPoolVK::GetThreadLocal(context.get()); + CommandEncoderVK encoder(context->GetDevice(), context->GetGraphicsQueue(), + pool, context->GetFenceWaiter()); + BlitCopyTextureToBufferCommandVK cmd; + cmd.source = context->GetResourceAllocator()->CreateTexture({ + .size = ISize(100, 100), + }); + cmd.destination = context->GetResourceAllocator()->CreateBuffer({ + .size = 1, + }); + bool result = cmd.Encode(encoder); + EXPECT_TRUE(result); + EXPECT_TRUE(encoder.IsTracking(cmd.source)); + EXPECT_TRUE(encoder.IsTracking(cmd.destination)); +} + +TEST(BlitCommandVkTest, BlitGenerateMipmapCommandVK) { + auto context = CreateMockVulkanContext(); + auto pool = CommandPoolVK::GetThreadLocal(context.get()); + CommandEncoderVK encoder(context->GetDevice(), context->GetGraphicsQueue(), + pool, context->GetFenceWaiter()); + BlitGenerateMipmapCommandVK cmd; + cmd.texture = context->GetResourceAllocator()->CreateTexture({ + .size = ISize(100, 100), + .mip_count = 2, + }); + bool result = cmd.Encode(encoder); + EXPECT_TRUE(result); + EXPECT_TRUE(encoder.IsTracking(cmd.texture)); +} + +} // namespace testing +} // namespace impeller diff --git a/impeller/renderer/backend/vulkan/command_encoder_vk.cc b/impeller/renderer/backend/vulkan/command_encoder_vk.cc index 35e0da71a903f..fd6cbec90be2a 100644 --- a/impeller/renderer/backend/vulkan/command_encoder_vk.cc +++ b/impeller/renderer/backend/vulkan/command_encoder_vk.cc @@ -58,6 +58,13 @@ class TrackedObjectsVK { tracked_buffers_.insert(std::move(buffer)); } + bool IsTracking(const std::shared_ptr& buffer) const { + if (!buffer) { + return false; + } + return tracked_buffers_.find(buffer) != tracked_buffers_.end(); + } + void Track(std::shared_ptr texture) { if (!texture) { return; @@ -65,6 +72,13 @@ class TrackedObjectsVK { tracked_textures_.insert(std::move(texture)); } + bool IsTracking(const std::shared_ptr& texture) const { + if (!texture) { + return false; + } + return tracked_textures_.find(texture) != tracked_textures_.end(); + } + vk::CommandBuffer GetCommandBuffer() const { return *buffer_; } DescriptorPoolVK& GetDescriptorPool() { return desc_pool_; } @@ -172,6 +186,14 @@ bool CommandEncoderVK::Track(std::shared_ptr buffer) { return true; } +bool CommandEncoderVK::IsTracking( + const std::shared_ptr& buffer) const { + if (!IsValid()) { + return false; + } + return tracked_objects_->IsTracking(buffer); +} + bool CommandEncoderVK::Track(std::shared_ptr texture) { if (!IsValid()) { return false; @@ -190,6 +212,16 @@ bool CommandEncoderVK::Track(const std::shared_ptr& texture) { return Track(TextureVK::Cast(*texture).GetTextureSource()); } +bool CommandEncoderVK::IsTracking( + const std::shared_ptr& texture) const { + if (!IsValid()) { + return false; + } + std::shared_ptr source = + TextureVK::Cast(*texture).GetTextureSource(); + return tracked_objects_->IsTracking(source); +} + std::optional CommandEncoderVK::AllocateDescriptorSet( const vk::DescriptorSetLayout& layout) { if (!IsValid()) { diff --git a/impeller/renderer/backend/vulkan/command_encoder_vk.h b/impeller/renderer/backend/vulkan/command_encoder_vk.h index 9e79fb36ee420..9a4581cdf9c9b 100644 --- a/impeller/renderer/backend/vulkan/command_encoder_vk.h +++ b/impeller/renderer/backend/vulkan/command_encoder_vk.h @@ -16,6 +16,12 @@ namespace impeller { +namespace testing { +class BlitCommandVkTest_BlitCopyTextureToTextureCommandVK_Test; +class BlitCommandVkTest_BlitCopyTextureToBufferCommandVK_Test; +class BlitCommandVkTest_BlitGenerateMipmapCommandVK_Test; +} // namespace testing + class ContextVK; class DeviceBuffer; class Texture; @@ -35,8 +41,12 @@ class CommandEncoderVK { bool Track(std::shared_ptr buffer); + bool IsTracking(const std::shared_ptr& texture) const; + bool Track(const std::shared_ptr& texture); + bool IsTracking(const std::shared_ptr& texture) const; + bool Track(std::shared_ptr texture); vk::CommandBuffer GetCommandBuffer() const; @@ -52,6 +62,12 @@ class CommandEncoderVK { private: friend class ContextVK; + friend class ::impeller::testing:: + BlitCommandVkTest_BlitCopyTextureToTextureCommandVK_Test; + friend class ::impeller::testing:: + BlitCommandVkTest_BlitCopyTextureToBufferCommandVK_Test; + friend class ::impeller::testing:: + BlitCommandVkTest_BlitGenerateMipmapCommandVK_Test; vk::Device device_ = {}; std::shared_ptr queue_; diff --git a/impeller/renderer/backend/vulkan/test/mock_vulkan.cc b/impeller/renderer/backend/vulkan/test/mock_vulkan.cc new file mode 100644 index 0000000000000..100b36b1cb175 --- /dev/null +++ b/impeller/renderer/backend/vulkan/test/mock_vulkan.cc @@ -0,0 +1,283 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/vulkan/test/mock_vulkan.h" + +namespace impeller { +namespace testing { + +namespace { +void noop() {} + +VkResult vkEnumerateInstanceExtensionProperties( + const char* pLayerName, + uint32_t* pPropertyCount, + VkExtensionProperties* pProperties) { + if (!pProperties) { + *pPropertyCount = 2; + + } else { + strcpy(pProperties[0].extensionName, "VK_KHR_surface"); + pProperties[0].specVersion = 0; + strcpy(pProperties[1].extensionName, "VK_MVK_macos_surface"); + pProperties[1].specVersion = 0; + } + return VK_SUCCESS; +} + +VkResult vkEnumerateInstanceLayerProperties(uint32_t* pPropertyCount, + VkLayerProperties* pProperties) { + *pPropertyCount = 0; + return VK_SUCCESS; +} + +VkResult vkEnumeratePhysicalDevices(VkInstance instance, + uint32_t* pPhysicalDeviceCount, + VkPhysicalDevice* pPhysicalDevices) { + if (!pPhysicalDevices) { + *pPhysicalDeviceCount = 1; + } else { + pPhysicalDevices[0] = reinterpret_cast(0xfeedface); + } + return VK_SUCCESS; +} + +void vkGetPhysicalDeviceFormatProperties( + VkPhysicalDevice physicalDevice, + VkFormat format, + VkFormatProperties* pFormatProperties) { + if (format == VK_FORMAT_B8G8R8A8_UNORM) { + pFormatProperties->optimalTilingFeatures = + static_cast( + vk::FormatFeatureFlagBits::eColorAttachment); + } else if (format == VK_FORMAT_S8_UINT) { + pFormatProperties->optimalTilingFeatures = + static_cast( + vk::FormatFeatureFlagBits::eDepthStencilAttachment); + } +} + +void vkGetPhysicalDeviceProperties(VkPhysicalDevice physicalDevice, + VkPhysicalDeviceProperties* pProperties) { + pProperties->limits.framebufferColorSampleCounts = + static_cast(VK_SAMPLE_COUNT_1_BIT | + VK_SAMPLE_COUNT_4_BIT); + pProperties->limits.maxImageDimension2D = 4096; +} + +void vkGetPhysicalDeviceQueueFamilyProperties( + VkPhysicalDevice physicalDevice, + uint32_t* pQueueFamilyPropertyCount, + VkQueueFamilyProperties* pQueueFamilyProperties) { + if (!pQueueFamilyProperties) { + *pQueueFamilyPropertyCount = 1; + } else { + pQueueFamilyProperties[0].queueCount = 3; + pQueueFamilyProperties[0].queueFlags = static_cast( + VK_QUEUE_TRANSFER_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_GRAPHICS_BIT); + } +} + +VkResult vkEnumerateDeviceExtensionProperties( + VkPhysicalDevice physicalDevice, + const char* pLayerName, + uint32_t* pPropertyCount, + VkExtensionProperties* pProperties) { + if (!pProperties) { + *pPropertyCount = 1; + } else { + strcpy(pProperties[0].extensionName, "VK_KHR_swapchain"); + pProperties[0].specVersion = 0; + } + return VK_SUCCESS; +} + +VkResult vkCreateDevice(VkPhysicalDevice physicalDevice, + const VkDeviceCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkDevice* pDevice) { + *pDevice = reinterpret_cast(0xcafebabe); + return VK_SUCCESS; +} + +VkResult vkCreateInstance(const VkInstanceCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkInstance* pInstance) { + *pInstance = reinterpret_cast(0xbaadf00d); + return VK_SUCCESS; +} + +void vkGetPhysicalDeviceMemoryProperties( + VkPhysicalDevice physicalDevice, + VkPhysicalDeviceMemoryProperties* pMemoryProperties) { + pMemoryProperties->memoryTypeCount = 1; + pMemoryProperties->memoryTypes[0].heapIndex = 0; + // pMemoryProperties->memoryTypes[0].propertyFlags = + // VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | + // VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD; + pMemoryProperties->memoryHeapCount = 1; + pMemoryProperties->memoryHeaps[0].size = 1024 * 1024 * 1024; + pMemoryProperties->memoryHeaps[0].flags = 0; +} + +VkResult vkCreatePipelineCache(VkDevice device, + const VkPipelineCacheCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkPipelineCache* pPipelineCache) { + *pPipelineCache = reinterpret_cast(0xb000dead); + return VK_SUCCESS; +} + +VkResult vkCreateCommandPool(VkDevice device, + const VkCommandPoolCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkCommandPool* pCommandPool) { + *pCommandPool = reinterpret_cast(0xc0de0001); + return VK_SUCCESS; +} + +VkResult vkAllocateCommandBuffers( + VkDevice device, + const VkCommandBufferAllocateInfo* pAllocateInfo, + VkCommandBuffer* pCommandBuffers) { + *pCommandBuffers = reinterpret_cast(0x0b0ffe12); + return VK_SUCCESS; +} + +VkResult vkBeginCommandBuffer(VkCommandBuffer commandBuffer, + const VkCommandBufferBeginInfo* pBeginInfo) { + return VK_SUCCESS; +} + +VkResult vkCreateImage(VkDevice device, + const VkImageCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkImage* pImage) { + *pImage = reinterpret_cast(0xD0D0CACA); + return VK_SUCCESS; +} + +void vkGetImageMemoryRequirements2KHR( + VkDevice device, + const VkImageMemoryRequirementsInfo2* pInfo, + VkMemoryRequirements2* pMemoryRequirements) { + pMemoryRequirements->memoryRequirements.size = 1024; + pMemoryRequirements->memoryRequirements.memoryTypeBits = 1; +} + +VkResult vkAllocateMemory(VkDevice device, + const VkMemoryAllocateInfo* pAllocateInfo, + const VkAllocationCallbacks* pAllocator, + VkDeviceMemory* pMemory) { + *pMemory = reinterpret_cast(0xCAFEB0BA); + return VK_SUCCESS; +} + +VkResult vkBindImageMemory(VkDevice device, + VkImage image, + VkDeviceMemory memory, + VkDeviceSize memoryOffset) { + return VK_SUCCESS; +} + +VkResult vkCreateImageView(VkDevice device, + const VkImageViewCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkImageView* pView) { + *pView = reinterpret_cast(0xFEE1DEAD); + return VK_SUCCESS; +} + +VkResult vkCreateBuffer(VkDevice device, + const VkBufferCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkBuffer* pBuffer) { + *pBuffer = reinterpret_cast(0xDEADDEAD); + return VK_SUCCESS; +} + +void vkGetBufferMemoryRequirements2KHR( + VkDevice device, + const VkBufferMemoryRequirementsInfo2* pInfo, + VkMemoryRequirements2* pMemoryRequirements) { + pMemoryRequirements->memoryRequirements.size = 1024; + pMemoryRequirements->memoryRequirements.memoryTypeBits = 1; +} + +VkResult vkBindBufferMemory(VkDevice device, + VkBuffer buffer, + VkDeviceMemory memory, + VkDeviceSize memoryOffset) { + return VK_SUCCESS; +} + +PFN_vkVoidFunction GetMockVulkanProcAddress(VkInstance instance, + const char* pName) { + if (strcmp("vkEnumerateInstanceExtensionProperties", pName) == 0) { + return (PFN_vkVoidFunction)vkEnumerateInstanceExtensionProperties; + } else if (strcmp("vkEnumerateInstanceLayerProperties", pName) == 0) { + return (PFN_vkVoidFunction)vkEnumerateInstanceLayerProperties; + } else if (strcmp("vkEnumeratePhysicalDevices", pName) == 0) { + return (PFN_vkVoidFunction)vkEnumeratePhysicalDevices; + } else if (strcmp("vkGetPhysicalDeviceFormatProperties", pName) == 0) { + return (PFN_vkVoidFunction)vkGetPhysicalDeviceFormatProperties; + } else if (strcmp("vkGetPhysicalDeviceProperties", pName) == 0) { + return (PFN_vkVoidFunction)vkGetPhysicalDeviceProperties; + } else if (strcmp("vkGetPhysicalDeviceQueueFamilyProperties", pName) == 0) { + return (PFN_vkVoidFunction)vkGetPhysicalDeviceQueueFamilyProperties; + } else if (strcmp("vkEnumerateDeviceExtensionProperties", pName) == 0) { + return (PFN_vkVoidFunction)vkEnumerateDeviceExtensionProperties; + } else if (strcmp("vkCreateDevice", pName) == 0) { + return (PFN_vkVoidFunction)vkCreateDevice; + } else if (strcmp("vkCreateInstance", pName) == 0) { + return (PFN_vkVoidFunction)vkCreateInstance; + } else if (strcmp("vkGetPhysicalDeviceMemoryProperties", pName) == 0) { + return (PFN_vkVoidFunction)vkGetPhysicalDeviceMemoryProperties; + } else if (strcmp("vkCreatePipelineCache", pName) == 0) { + return (PFN_vkVoidFunction)vkCreatePipelineCache; + } else if (strcmp("vkCreateCommandPool", pName) == 0) { + return (PFN_vkVoidFunction)vkCreateCommandPool; + } else if (strcmp("vkAllocateCommandBuffers", pName) == 0) { + return (PFN_vkVoidFunction)vkAllocateCommandBuffers; + } else if (strcmp("vkBeginCommandBuffer", pName) == 0) { + return (PFN_vkVoidFunction)vkBeginCommandBuffer; + } else if (strcmp("vkCreateImage", pName) == 0) { + return (PFN_vkVoidFunction)vkCreateImage; + } else if (strcmp("vkGetInstanceProcAddr", pName) == 0) { + return (PFN_vkVoidFunction)GetMockVulkanProcAddress; + } else if (strcmp("vkGetDeviceProcAddr", pName) == 0) { + return (PFN_vkVoidFunction)GetMockVulkanProcAddress; + } else if (strcmp("vkGetImageMemoryRequirements2KHR", pName) == 0 || + strcmp("vkGetImageMemoryRequirements2", pName) == 0) { + return (PFN_vkVoidFunction)vkGetImageMemoryRequirements2KHR; + } else if (strcmp("vkAllocateMemory", pName) == 0) { + return (PFN_vkVoidFunction)vkAllocateMemory; + } else if (strcmp("vkBindImageMemory", pName) == 0) { + return (PFN_vkVoidFunction)vkBindImageMemory; + } else if (strcmp("vkCreateImageView", pName) == 0) { + return (PFN_vkVoidFunction)vkCreateImageView; + } else if (strcmp("vkCreateBuffer", pName) == 0) { + return (PFN_vkVoidFunction)vkCreateBuffer; + } else if (strcmp("vkGetBufferMemoryRequirements2KHR", pName) == 0 || + strcmp("vkGetBufferMemoryRequirements2", pName) == 0) { + return (PFN_vkVoidFunction)vkGetBufferMemoryRequirements2KHR; + } else if (strcmp("vkBindBufferMemory", pName) == 0) { + return (PFN_vkVoidFunction)vkBindBufferMemory; + } + return noop; +} + +} // namespace + +std::shared_ptr CreateMockVulkanContext(void) { + ContextVK::Settings settings; + auto message_loop = fml::ConcurrentMessageLoop::Create(); + settings.worker_task_runner = + std::make_shared(message_loop); + settings.proc_address_callback = GetMockVulkanProcAddress; + return ContextVK::Create(std::move(settings)); +} + +} // namespace testing +} // namespace impeller diff --git a/impeller/renderer/backend/vulkan/test/mock_vulkan.h b/impeller/renderer/backend/vulkan/test/mock_vulkan.h new file mode 100644 index 0000000000000..cc075850ff37a --- /dev/null +++ b/impeller/renderer/backend/vulkan/test/mock_vulkan.h @@ -0,0 +1,15 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "impeller/renderer/backend/vulkan/context_vk.h" + +namespace impeller { +namespace testing { + +std::shared_ptr CreateMockVulkanContext(void); + +} +} // namespace impeller