| // Copyright (C) 2018 The Android Open Source Project |
| // |
| // 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 <gtest/gtest.h> |
| |
| #include "GoldfishOpenglTestEnv.h" |
| #include "GrallocDispatch.h" |
| #include "GrallocUsageConversion.h" |
| #include "AndroidVulkanDispatch.h" |
| |
| #include <vulkan/vulkan.h> |
| #include <vulkan/vulkan_android.h> |
| #include <vulkan/vk_android_native_buffer.h> |
| |
| #include "android/base/ArraySize.h" |
| #include "android/base/files/MemStream.h" |
| #include "android/base/files/Stream.h" |
| #include "android/base/files/PathUtils.h" |
| #include "android/base/memory/ScopedPtr.h" |
| #include "android/base/synchronization/ConditionVariable.h" |
| #include "android/base/synchronization/Lock.h" |
| #include "android/base/system/System.h" |
| #include "android/base/threads/FunctorThread.h" |
| #include "android/opengles.h" |
| #include "android/snapshot/interface.h" |
| |
| #include "OpenglSystemCommon/HostConnection.h" |
| #include "OpenglSystemCommon/ProcessPipe.h" |
| |
| #include <android/hardware_buffer.h> |
| #include <cutils/properties.h> |
| |
| #include <atomic> |
| #include <random> |
| #include <memory> |
| #include <vector> |
| |
| using android::base::AutoLock; |
| using android::base::ConditionVariable; |
| using android::base::FunctorThread; |
| using android::base::Lock; |
| using android::base::pj; |
| using android::base::System; |
| |
| namespace aemu { |
| |
| static constexpr int kWindowSize = 256; |
| |
| static android_vulkan_dispatch* vk = nullptr; |
| |
| class VulkanHalTest : |
| public ::testing::Test, |
| public ::testing::WithParamInterface<const char*> { |
| protected: |
| |
| static GoldfishOpenglTestEnv* testEnv; |
| |
| static void SetUpTestCase() { |
| testEnv = new GoldfishOpenglTestEnv; |
| #ifdef _WIN32 |
| const char* libFilename = "vulkan_android.dll"; |
| #elif defined(__APPLE__) |
| const char* libFilename = "libvulkan_android.dylib"; |
| #else |
| const char* libFilename = "libvulkan_android.so"; |
| #endif |
| auto path = |
| pj(System::get()->getProgramDirectory(), |
| "lib64", libFilename); |
| vk = load_android_vulkan_dispatch(path.c_str()); |
| } |
| |
| static void TearDownTestCase() { |
| // Cancel all host threads as well |
| android_finishOpenglesRenderer(); |
| |
| delete testEnv; |
| testEnv = nullptr; |
| |
| delete vk; |
| } |
| |
| static constexpr kGltransportPropName = "ro.boot.qemu.gltransport"; |
| |
| bool usingAddressSpaceGraphics() { |
| char value[PROPERTY_VALUE_MAX]; |
| if (property_get( |
| kGltransportPropName, value, "pipe") > 0) { |
| return !strcmp("asg", value); |
| } |
| return false; |
| } |
| |
| void SetUp() override { |
| mProcessPipeRestarted = false; |
| |
| property_set(kGltransportPropName, GetParam()); |
| printf("%s: using transport: %s\n", __func__, GetParam()); |
| |
| setupGralloc(); |
| setupVulkan(); |
| } |
| |
| void TearDown() override { |
| teardownVulkan(); |
| teardownGralloc(); |
| } |
| |
| void restartProcessPipeAndHostConnection() { |
| processPipeRestart(); |
| cutHostConnectionUnclean(); |
| refreshHostConnection(); |
| } |
| |
| void cutHostConnectionUnclean() { |
| HostConnection::exitUnclean(); |
| } |
| |
| void setupGralloc() { |
| auto grallocPath = pj(System::get()->getProgramDirectory(), "lib64", |
| "gralloc.ranchu" LIBSUFFIX); |
| |
| load_gralloc_module(grallocPath.c_str(), &mGralloc); |
| set_global_gralloc_module(&mGralloc); |
| |
| EXPECT_NE(nullptr, mGralloc.alloc_dev); |
| EXPECT_NE(nullptr, mGralloc.alloc_module); |
| } |
| |
| void teardownGralloc() { unload_gralloc_module(&mGralloc); } |
| |
| buffer_handle_t createTestGrallocBuffer( |
| int usage, int format, |
| int width, int height, int* stride_out) { |
| buffer_handle_t buffer; |
| int res; |
| |
| res = mGralloc.alloc(width, height, format, usage, &buffer, stride_out); |
| if (res) { |
| fprintf(stderr, "%s:%d res=%d buffer=%p\n", __func__, __LINE__, res, buffer); |
| ::abort(); |
| } |
| |
| res = mGralloc.registerBuffer(buffer); |
| if (res) { |
| fprintf(stderr, "%s:%d res=%d buffer=%p\n", __func__, __LINE__, res, buffer); |
| ::abort(); |
| } |
| |
| return buffer; |
| } |
| |
| void destroyTestGrallocBuffer(buffer_handle_t buffer) { |
| int res; |
| |
| res = mGralloc.unregisterBuffer(buffer); |
| if (res) { |
| fprintf(stderr, "%s:%d res=%d buffer=%p\n", __func__, __LINE__, res, buffer); |
| ::abort(); |
| } |
| |
| res = mGralloc.free(buffer); |
| if (res) { |
| fprintf(stderr, "%s:%d res=%d buffer=%p\n", __func__, __LINE__, res, buffer); |
| ::abort(); |
| } |
| } |
| |
| void setupVulkan() { |
| uint32_t extCount = 0; |
| std::vector<VkExtensionProperties> exts; |
| EXPECT_EQ(VK_SUCCESS, vk->vkEnumerateInstanceExtensionProperties( |
| nullptr, &extCount, nullptr)); |
| exts.resize(extCount); |
| EXPECT_EQ(VK_SUCCESS, vk->vkEnumerateInstanceExtensionProperties( |
| nullptr, &extCount, exts.data())); |
| |
| bool hasGetPhysicalDeviceProperties2 = false; |
| bool hasExternalMemoryCapabilities = false; |
| |
| for (const auto& prop : exts) { |
| if (!strcmp("VK_KHR_get_physical_device_properties2", prop.extensionName)) { |
| hasGetPhysicalDeviceProperties2 = true; |
| } |
| if (!strcmp("VK_KHR_external_memory_capabilities", prop.extensionName)) { |
| hasExternalMemoryCapabilities = true; |
| } |
| } |
| |
| std::vector<const char*> enabledExtensions; |
| |
| if (hasGetPhysicalDeviceProperties2) { |
| enabledExtensions.push_back("VK_KHR_get_physical_device_properties2"); |
| mInstanceHasGetPhysicalDeviceProperties2Support = true; |
| } |
| |
| if (hasExternalMemoryCapabilities) { |
| enabledExtensions.push_back("VK_KHR_external_memory_capabilities"); |
| mInstanceHasExternalMemorySupport = true; |
| } |
| |
| const char* const* enabledExtensionNames = |
| enabledExtensions.size() > 0 ? enabledExtensions.data() |
| : nullptr; |
| |
| VkApplicationInfo appInfo = { |
| VK_STRUCTURE_TYPE_APPLICATION_INFO, 0, |
| "someAppName", 1, |
| "someEngineName", 1, |
| VK_VERSION_1_0, |
| }; |
| |
| VkInstanceCreateInfo instCi = { |
| VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, |
| 0, 0, &appInfo, |
| 0, nullptr, |
| (uint32_t)enabledExtensions.size(), |
| enabledExtensionNames, |
| }; |
| |
| EXPECT_EQ(VK_SUCCESS, vk->vkCreateInstance(&instCi, nullptr, &mInstance)); |
| |
| uint32_t physdevCount = 0; |
| std::vector<VkPhysicalDevice> physdevs; |
| EXPECT_EQ(VK_SUCCESS, |
| vk->vkEnumeratePhysicalDevices(mInstance, &physdevCount, nullptr)); |
| physdevs.resize(physdevCount); |
| EXPECT_EQ(VK_SUCCESS, |
| vk->vkEnumeratePhysicalDevices(mInstance, &physdevCount, |
| physdevs.data())); |
| std::vector<VkPhysicalDevice> physdevsSecond(physdevCount); |
| EXPECT_EQ(VK_SUCCESS, |
| vk->vkEnumeratePhysicalDevices(mInstance, &physdevCount, |
| physdevsSecond.data())); |
| // Check that a second call to vkEnumeratePhysicalDevices |
| // retrieves the same physical device handles. |
| EXPECT_EQ(physdevs, physdevsSecond); |
| |
| uint32_t bestPhysicalDevice = 0; |
| bool queuesGood = false; |
| bool hasAndroidNativeBuffer = false; |
| bool hasExternalMemorySupport = false; |
| |
| for (uint32_t i = 0; i < physdevCount; ++i) { |
| |
| queuesGood = false; |
| hasAndroidNativeBuffer = false; |
| hasExternalMemorySupport = false; |
| |
| bool hasGetMemoryRequirements2 = false; |
| bool hasDedicatedAllocation = false; |
| bool hasExternalMemoryBaseExtension = false; |
| bool hasExternalMemoryPlatformExtension = false; |
| |
| uint32_t queueFamilyCount = 0; |
| std::vector<VkQueueFamilyProperties> queueFamilyProps; |
| vk->vkGetPhysicalDeviceQueueFamilyProperties( |
| physdevs[i], &queueFamilyCount, nullptr); |
| queueFamilyProps.resize(queueFamilyCount); |
| vk->vkGetPhysicalDeviceQueueFamilyProperties( |
| physdevs[i], &queueFamilyCount, queueFamilyProps.data()); |
| |
| for (uint32_t j = 0; j < queueFamilyCount; ++j) { |
| auto count = queueFamilyProps[j].queueCount; |
| auto flags = queueFamilyProps[j].queueFlags; |
| if (count > 0 && (flags & VK_QUEUE_GRAPHICS_BIT)) { |
| bestPhysicalDevice = i; |
| mGraphicsQueueFamily = j; |
| queuesGood = true; |
| break; |
| } |
| } |
| |
| uint32_t devExtCount = 0; |
| std::vector<VkExtensionProperties> availableDeviceExtensions; |
| vk->vkEnumerateDeviceExtensionProperties(physdevs[i], nullptr, |
| &devExtCount, nullptr); |
| availableDeviceExtensions.resize(devExtCount); |
| vk->vkEnumerateDeviceExtensionProperties( |
| physdevs[i], nullptr, &devExtCount, availableDeviceExtensions.data()); |
| for (uint32_t j = 0; j < devExtCount; ++j) { |
| if (!strcmp("VK_KHR_swapchain", |
| availableDeviceExtensions[j].extensionName)) { |
| hasAndroidNativeBuffer = true; |
| } |
| if (!strcmp("VK_KHR_get_memory_requirements2", |
| availableDeviceExtensions[j].extensionName)) { |
| hasGetMemoryRequirements2 = true; |
| } |
| if (!strcmp("VK_KHR_dedicated_allocation", |
| availableDeviceExtensions[j].extensionName)) { |
| hasDedicatedAllocation = true; |
| } |
| if (!strcmp("VK_KHR_external_memory", |
| availableDeviceExtensions[j].extensionName)) { |
| hasExternalMemoryBaseExtension = true; |
| } |
| static const char* externalMemoryPlatformExtension = |
| "VK_ANDROID_external_memory_android_hardware_buffer"; |
| |
| if (!strcmp(externalMemoryPlatformExtension, |
| availableDeviceExtensions[j].extensionName)) { |
| hasExternalMemoryPlatformExtension = true; |
| } |
| } |
| |
| hasExternalMemorySupport = |
| (hasGetMemoryRequirements2 && |
| hasDedicatedAllocation && |
| hasExternalMemoryBaseExtension && |
| hasExternalMemoryPlatformExtension); |
| |
| if (queuesGood && hasExternalMemorySupport) { |
| bestPhysicalDevice = i; |
| break; |
| } |
| } |
| |
| EXPECT_TRUE(queuesGood); |
| EXPECT_TRUE(hasAndroidNativeBuffer); |
| |
| mDeviceHasExternalMemorySupport = |
| hasExternalMemorySupport; |
| |
| mPhysicalDevice = physdevs[bestPhysicalDevice]; |
| |
| VkPhysicalDeviceMemoryProperties memProps; |
| vk->vkGetPhysicalDeviceMemoryProperties(mPhysicalDevice, &memProps); |
| |
| bool foundHostVisibleMemoryTypeIndex = false; |
| bool foundDeviceLocalMemoryTypeIndex = false; |
| |
| for (uint32_t i = 0; i < memProps.memoryTypeCount; ++i) { |
| if (memProps.memoryTypes[i].propertyFlags & |
| VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) { |
| mHostVisibleMemoryTypeIndex = i; |
| foundHostVisibleMemoryTypeIndex = true; |
| } |
| |
| if (memProps.memoryTypes[i].propertyFlags & |
| VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) { |
| mDeviceLocalMemoryTypeIndex = i; |
| foundDeviceLocalMemoryTypeIndex = true; |
| } |
| |
| if (foundHostVisibleMemoryTypeIndex && |
| foundDeviceLocalMemoryTypeIndex) { |
| break; |
| } |
| } |
| |
| EXPECT_TRUE( |
| foundHostVisibleMemoryTypeIndex && |
| foundDeviceLocalMemoryTypeIndex); |
| |
| EXPECT_TRUE(foundHostVisibleMemoryTypeIndex); |
| |
| float priority = 1.0f; |
| VkDeviceQueueCreateInfo dqCi = { |
| VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, |
| 0, 0, |
| mGraphicsQueueFamily, 1, |
| &priority, |
| }; |
| |
| VkDeviceCreateInfo dCi = { |
| VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, 0, 0, |
| 1, &dqCi, |
| 0, nullptr, // no layers |
| 0, nullptr, // no extensions |
| nullptr, // no features |
| }; |
| |
| std::vector<const char*> externalMemoryExtensions = { |
| "VK_KHR_get_memory_requirements2", |
| "VK_KHR_dedicated_allocation", |
| "VK_KHR_external_memory", |
| "VK_ANDROID_external_memory_android_hardware_buffer", |
| }; |
| |
| // Mostly for MoltenVK or any other driver that doesn't support |
| // external memory. |
| std::vector<const char*> usefulExtensions = { |
| "VK_KHR_get_memory_requirements2", |
| "VK_KHR_dedicated_allocation", |
| }; |
| |
| if (mDeviceHasExternalMemorySupport) { |
| dCi.enabledExtensionCount = |
| (uint32_t)externalMemoryExtensions.size(); |
| dCi.ppEnabledExtensionNames = |
| externalMemoryExtensions.data(); |
| } else { |
| dCi.enabledExtensionCount = |
| (uint32_t)usefulExtensions.size(); |
| dCi.ppEnabledExtensionNames = |
| usefulExtensions.data(); |
| } |
| |
| EXPECT_EQ(VK_SUCCESS, vk->vkCreateDevice(physdevs[bestPhysicalDevice], &dCi, |
| nullptr, &mDevice)); |
| vk->vkGetDeviceQueue(mDevice, mGraphicsQueueFamily, 0, &mQueue); |
| } |
| |
| void teardownVulkan() { |
| vk->vkDestroyDevice(mDevice, nullptr); |
| vk->vkDestroyInstance(mInstance, nullptr); |
| } |
| |
| void createAndroidNativeImage(buffer_handle_t* buffer_out, VkImage* image_out) { |
| |
| int usage = GRALLOC_USAGE_HW_RENDER; |
| int format = HAL_PIXEL_FORMAT_RGBA_8888; |
| int stride; |
| buffer_handle_t buffer = |
| createTestGrallocBuffer( |
| usage, format, kWindowSize, kWindowSize, &stride); |
| |
| uint64_t producerUsage, consumerUsage; |
| android_convertGralloc0To1Usage(usage, &producerUsage, &consumerUsage); |
| |
| VkNativeBufferANDROID nativeBufferInfo = { |
| VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID, nullptr, |
| buffer, stride, |
| format, |
| usage, |
| { |
| consumerUsage, |
| producerUsage, |
| }, |
| }; |
| |
| VkImageCreateInfo testImageCi = { |
| VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, (const void*)&nativeBufferInfo, |
| 0, |
| VK_IMAGE_TYPE_2D, |
| VK_FORMAT_R8G8B8A8_UNORM, |
| { kWindowSize, kWindowSize, 1, }, |
| 1, 1, |
| VK_SAMPLE_COUNT_1_BIT, |
| VK_IMAGE_TILING_OPTIMAL, |
| VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, |
| VK_SHARING_MODE_EXCLUSIVE, |
| 0, nullptr /* shared queue families */, |
| VK_IMAGE_LAYOUT_UNDEFINED, |
| }; |
| |
| VkImage testAndroidNativeImage; |
| EXPECT_EQ(VK_SUCCESS, vk->vkCreateImage(mDevice, &testImageCi, nullptr, |
| &testAndroidNativeImage)); |
| |
| *buffer_out = buffer; |
| *image_out = testAndroidNativeImage; |
| } |
| |
| void destroyAndroidNativeImage(buffer_handle_t buffer, VkImage image) { |
| vk->vkDestroyImage(mDevice, image, nullptr); |
| destroyTestGrallocBuffer(buffer); |
| } |
| |
| AHardwareBuffer* allocateAndroidHardwareBuffer( |
| int width = kWindowSize, |
| int height = kWindowSize, |
| AHardwareBuffer_Format format = |
| AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| uint64_t usage = |
| AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | |
| AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | |
| AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN) { |
| |
| AHardwareBuffer_Desc desc = { |
| kWindowSize, kWindowSize, 1, |
| AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, |
| usage, |
| 4, // stride ignored for allocate; don't check this |
| }; |
| |
| AHardwareBuffer* buf = nullptr; |
| AHardwareBuffer_allocate(&desc, &buf); |
| |
| EXPECT_NE(nullptr, buf); |
| |
| return buf; |
| } |
| |
| void exportAllocateAndroidHardwareBuffer( |
| VkMemoryDedicatedAllocateInfo* dedicated, |
| VkDeviceSize allocSize, |
| uint32_t memoryTypeIndex, |
| VkDeviceMemory* pMemory, |
| AHardwareBuffer** ahw) { |
| |
| EXPECT_TRUE(mDeviceHasExternalMemorySupport); |
| |
| VkExportMemoryAllocateInfo exportAi = { |
| VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO, dedicated, |
| VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID, |
| }; |
| |
| VkMemoryAllocateInfo allocInfo = { |
| VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, &exportAi, |
| allocSize, memoryTypeIndex, |
| }; |
| |
| VkResult res = vk->vkAllocateMemory(mDevice, &allocInfo, nullptr, pMemory); |
| EXPECT_EQ(VK_SUCCESS, res); |
| |
| if (ahw) { |
| VkMemoryGetAndroidHardwareBufferInfoANDROID getAhbInfo = { |
| VK_STRUCTURE_TYPE_MEMORY_GET_ANDROID_HARDWARE_BUFFER_INFO_ANDROID, 0, *pMemory, |
| }; |
| |
| EXPECT_EQ(VK_SUCCESS, |
| vk->vkGetMemoryAndroidHardwareBufferANDROID( |
| mDevice, &getAhbInfo, ahw)); |
| } |
| } |
| |
| void importAllocateAndroidHardwareBuffer( |
| VkMemoryDedicatedAllocateInfo* dedicated, |
| VkDeviceSize allocSize, |
| uint32_t memoryTypeIndex, |
| AHardwareBuffer* ahw, |
| VkDeviceMemory* pMemory) { |
| |
| VkImportAndroidHardwareBufferInfoANDROID importInfo = { |
| VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID, |
| dedicated, ahw, |
| }; |
| |
| VkMemoryAllocateInfo allocInfo = { |
| VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, &importInfo, |
| allocSize, memoryTypeIndex, |
| }; |
| |
| VkDeviceMemory memory; |
| VkResult res = vk->vkAllocateMemory(mDevice, &allocInfo, nullptr, pMemory); |
| |
| EXPECT_EQ(VK_SUCCESS, res); |
| } |
| |
| void createExternalImage( |
| VkImage* pImage, |
| uint32_t width = kWindowSize, |
| uint32_t height = kWindowSize, |
| VkFormat format = VK_FORMAT_R8G8B8A8_UNORM, |
| VkImageTiling tiling = VK_IMAGE_TILING_OPTIMAL) { |
| |
| VkExternalMemoryImageCreateInfo extMemImgCi = { |
| VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO, 0, |
| VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID, |
| }; |
| |
| VkImageCreateInfo testImageCi = { |
| VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, |
| (const void*)&extMemImgCi, 0, |
| VK_IMAGE_TYPE_2D, format, |
| { width, height, 1, }, 1, 1, |
| VK_SAMPLE_COUNT_1_BIT, |
| tiling, |
| VK_IMAGE_USAGE_SAMPLED_BIT, |
| VK_SHARING_MODE_EXCLUSIVE, |
| 0, nullptr /* shared queue families */, |
| VK_IMAGE_LAYOUT_UNDEFINED, |
| }; |
| |
| EXPECT_EQ(VK_SUCCESS, |
| vk->vkCreateImage( |
| mDevice, &testImageCi, nullptr, pImage)); |
| } |
| |
| uint32_t getFirstMemoryTypeIndexForImage(VkImage image) { |
| VkMemoryRequirements memReqs; |
| vk->vkGetImageMemoryRequirements( |
| mDevice, image, &memReqs); |
| |
| uint32_t memoryTypeIndex = 0; |
| EXPECT_NE(0, memReqs.memoryTypeBits); |
| |
| for (uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i) { |
| if (memReqs.memoryTypeBits & (1 << i)) { |
| memoryTypeIndex = i; |
| break; |
| } |
| } |
| return memoryTypeIndex; |
| } |
| |
| VkDeviceSize getNeededMemorySizeForImage(VkImage image) { |
| VkMemoryRequirements memReqs; |
| vk->vkGetImageMemoryRequirements( |
| mDevice, image, &memReqs); |
| return memReqs.size; |
| } |
| |
| VkResult allocateTestDescriptorSetsFromExistingPool( |
| uint32_t setsToAllocate, |
| VkDescriptorPool pool, |
| VkDescriptorSetLayout setLayout, |
| VkDescriptorSet* sets_out) { |
| |
| std::vector<VkDescriptorSetLayout> setLayouts; |
| for (uint32_t i = 0; i < setsToAllocate; ++i) { |
| setLayouts.push_back(setLayout); |
| } |
| |
| VkDescriptorSetAllocateInfo setAi = { |
| VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, 0, |
| pool, setsToAllocate, setLayouts.data(), |
| }; |
| |
| return vk->vkAllocateDescriptorSets( |
| mDevice, &setAi, sets_out); |
| } |
| |
| VkResult allocateTestDescriptorSets( |
| uint32_t maxSetCount, |
| uint32_t setsToAllocate, |
| VkDescriptorType descriptorType, |
| VkDescriptorPoolCreateFlags poolCreateFlags, |
| VkDescriptorPool* pool_out, |
| VkDescriptorSetLayout* setLayout_out, |
| VkDescriptorSet* sets_out) { |
| |
| VkDescriptorPoolSize poolSize = { |
| descriptorType, |
| maxSetCount, |
| }; |
| |
| VkDescriptorPoolCreateInfo poolCi = { |
| VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, 0, |
| VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, |
| maxSetCount /* maxSets */, |
| 1 /* poolSizeCount */, |
| &poolSize, |
| }; |
| |
| EXPECT_EQ(VK_SUCCESS, vk->vkCreateDescriptorPool(mDevice, &poolCi, nullptr, pool_out)); |
| |
| VkDescriptorSetLayoutBinding binding = { |
| 0, |
| descriptorType, |
| 1, |
| VK_SHADER_STAGE_VERTEX_BIT, |
| nullptr, |
| }; |
| |
| VkDescriptorSetLayoutCreateInfo setLayoutCi = { |
| VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, 0, 0, |
| 1, |
| &binding, |
| }; |
| |
| EXPECT_EQ(VK_SUCCESS, vk->vkCreateDescriptorSetLayout( |
| mDevice, &setLayoutCi, nullptr, setLayout_out)); |
| |
| return allocateTestDescriptorSetsFromExistingPool( |
| setsToAllocate, *pool_out, *setLayout_out, sets_out); |
| } |
| |
| VkResult allocateImmutableSamplerDescriptorSets( |
| uint32_t maxSetCount, |
| uint32_t setsToAllocate, |
| std::vector<bool> bindingImmutabilities, |
| VkSampler* sampler_out, |
| VkDescriptorPool* pool_out, |
| VkDescriptorSetLayout* setLayout_out, |
| VkDescriptorSet* sets_out) { |
| |
| VkSamplerCreateInfo samplerCi = { |
| VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, 0, 0, |
| VK_FILTER_NEAREST, |
| VK_FILTER_NEAREST, |
| VK_SAMPLER_MIPMAP_MODE_NEAREST, |
| VK_SAMPLER_ADDRESS_MODE_REPEAT, |
| VK_SAMPLER_ADDRESS_MODE_REPEAT, |
| VK_SAMPLER_ADDRESS_MODE_REPEAT, |
| 0.0f, |
| VK_FALSE, |
| 1.0f, |
| VK_FALSE, |
| VK_COMPARE_OP_NEVER, |
| 0.0f, |
| 1.0f, |
| VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, |
| VK_FALSE, |
| }; |
| |
| EXPECT_EQ(VK_SUCCESS, |
| vk->vkCreateSampler( |
| mDevice, &samplerCi, nullptr, sampler_out)); |
| |
| VkDescriptorPoolSize poolSize = { |
| VK_DESCRIPTOR_TYPE_SAMPLER, |
| maxSetCount, |
| }; |
| |
| VkDescriptorPoolCreateInfo poolCi = { |
| VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, 0, |
| VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, |
| maxSetCount /* maxSets */, |
| 1 /* poolSizeCount */, |
| &poolSize, |
| }; |
| |
| EXPECT_EQ(VK_SUCCESS, vk->vkCreateDescriptorPool(mDevice, &poolCi, nullptr, pool_out)); |
| |
| std::vector<VkDescriptorSetLayoutBinding> samplerBindings; |
| |
| for (size_t i = 0; i < bindingImmutabilities.size(); ++i) { |
| VkDescriptorSetLayoutBinding samplerBinding = { |
| (uint32_t)i, VK_DESCRIPTOR_TYPE_SAMPLER, |
| 1, VK_SHADER_STAGE_FRAGMENT_BIT, |
| bindingImmutabilities[i] ? sampler_out : nullptr, |
| }; |
| samplerBindings.push_back(samplerBinding); |
| } |
| |
| VkDescriptorSetLayoutCreateInfo setLayoutCi = { |
| VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, 0, 0, |
| (uint32_t)samplerBindings.size(), |
| samplerBindings.data(), |
| }; |
| |
| EXPECT_EQ(VK_SUCCESS, vk->vkCreateDescriptorSetLayout( |
| mDevice, &setLayoutCi, nullptr, setLayout_out)); |
| |
| return allocateTestDescriptorSetsFromExistingPool( |
| setsToAllocate, *pool_out, *setLayout_out, sets_out); |
| } |
| |
| struct gralloc_implementation mGralloc; |
| |
| bool mInstanceHasGetPhysicalDeviceProperties2Support = false; |
| bool mInstanceHasExternalMemorySupport = false; |
| bool mDeviceHasExternalMemorySupport = false; |
| bool mProcessPipeRestarted = false; |
| |
| VkInstance mInstance; |
| VkPhysicalDevice mPhysicalDevice; |
| VkDevice mDevice; |
| VkQueue mQueue; |
| uint32_t mHostVisibleMemoryTypeIndex; |
| uint32_t mDeviceLocalMemoryTypeIndex; |
| uint32_t mGraphicsQueueFamily; |
| }; |
| |
| // static |
| GoldfishOpenglTestEnv* VulkanHalTest::testEnv = nullptr; |
| |
| // A basic test of Vulkan HAL: |
| // - Touch the Android loader at global, instance, and device level. |
| TEST_P(VulkanHalTest, Basic) { } |
| |
| // Test: Allocate, map, flush, invalidate some host visible memory. |
| TEST_P(VulkanHalTest, MemoryMapping) { |
| static constexpr VkDeviceSize kTestAlloc = 16 * 1024; |
| VkMemoryAllocateInfo allocInfo = { |
| VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, 0, |
| kTestAlloc, |
| mHostVisibleMemoryTypeIndex, |
| }; |
| VkDeviceMemory mem; |
| EXPECT_EQ(VK_SUCCESS, vk->vkAllocateMemory(mDevice, &allocInfo, nullptr, &mem)); |
| |
| void* hostPtr; |
| EXPECT_EQ(VK_SUCCESS, vk->vkMapMemory(mDevice, mem, 0, VK_WHOLE_SIZE, 0, &hostPtr)); |
| |
| memset(hostPtr, 0xff, kTestAlloc); |
| |
| VkMappedMemoryRange toFlush = { |
| VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, 0, |
| mem, 0, kTestAlloc, |
| }; |
| |
| EXPECT_EQ(VK_SUCCESS, vk->vkFlushMappedMemoryRanges(mDevice, 1, &toFlush)); |
| EXPECT_EQ(VK_SUCCESS, vk->vkInvalidateMappedMemoryRanges(mDevice, 1, &toFlush)); |
| |
| for (uint32_t i = 0; i < kTestAlloc; ++i) { |
| EXPECT_EQ(0xff, *((uint8_t*)hostPtr + i)); |
| } |
| |
| int usage = GRALLOC_USAGE_HW_RENDER; |
| int format = HAL_PIXEL_FORMAT_RGBA_8888; |
| int stride; |
| buffer_handle_t buffer = |
| createTestGrallocBuffer( |
| usage, format, kWindowSize, kWindowSize, &stride); |
| |
| uint64_t producerUsage, consumerUsage; |
| android_convertGralloc0To1Usage(usage, &producerUsage, &consumerUsage); |
| |
| VkNativeBufferANDROID nativeBufferInfo = { |
| VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID, nullptr, |
| buffer, stride, |
| format, |
| usage, |
| { |
| consumerUsage, |
| producerUsage, |
| }, |
| }; |
| |
| VkImageCreateInfo testImageCi = { |
| VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, (const void*)&nativeBufferInfo, |
| 0, |
| VK_IMAGE_TYPE_2D, |
| VK_FORMAT_R8G8B8A8_UNORM, |
| { kWindowSize, kWindowSize, 1, }, |
| 1, 1, |
| VK_SAMPLE_COUNT_1_BIT, |
| VK_IMAGE_TILING_OPTIMAL, |
| VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, |
| VK_SHARING_MODE_EXCLUSIVE, |
| 0, nullptr /* shared queue families */, |
| VK_IMAGE_LAYOUT_UNDEFINED, |
| }; |
| |
| VkImage testAndroidNativeImage; |
| EXPECT_EQ(VK_SUCCESS, vk->vkCreateImage(mDevice, &testImageCi, nullptr, |
| &testAndroidNativeImage)); |
| vk->vkDestroyImage(mDevice, testAndroidNativeImage, nullptr); |
| destroyTestGrallocBuffer(buffer); |
| |
| vk->vkUnmapMemory(mDevice, mem); |
| vk->vkFreeMemory(mDevice, mem, nullptr); |
| } |
| |
| // Tests creation of VkImages backed by gralloc buffers. |
| TEST_P(VulkanHalTest, AndroidNativeImageCreation) { |
| VkImage image; |
| buffer_handle_t buffer; |
| createAndroidNativeImage(&buffer, &image); |
| destroyAndroidNativeImage(buffer, image); |
| } |
| |
| // Tests the path to sync Android native buffers with Gralloc buffers. |
| TEST_P(VulkanHalTest, AndroidNativeImageQueueSignal) { |
| VkImage image; |
| buffer_handle_t buffer; |
| int fenceFd; |
| |
| createAndroidNativeImage(&buffer, &image); |
| |
| PFN_vkQueueSignalReleaseImageANDROID func = |
| (PFN_vkQueueSignalReleaseImageANDROID) |
| vk->vkGetDeviceProcAddr(mDevice, "vkQueueSignalReleaseImageANDROID"); |
| |
| if (func) { |
| fprintf(stderr, "%s: qsig\n", __func__); |
| func(mQueue, 0, nullptr, image, &fenceFd); |
| } |
| |
| destroyAndroidNativeImage(buffer, image); |
| } |
| |
| // Tests VK_KHR_get_physical_device_properties2: |
| // new API: vkGetPhysicalDeviceProperties2KHR |
| TEST_P(VulkanHalTest, GetPhysicalDeviceProperties2) { |
| if (!mInstanceHasGetPhysicalDeviceProperties2Support) { |
| printf("Warning: Not testing VK_KHR_physical_device_properties2, not " |
| "supported\n"); |
| return; |
| } |
| |
| PFN_vkGetPhysicalDeviceProperties2KHR physProps2KHRFunc = |
| (PFN_vkGetPhysicalDeviceProperties2KHR)vk->vkGetInstanceProcAddr( |
| mInstance, "vkGetPhysicalDeviceProperties2KHR"); |
| |
| EXPECT_NE(nullptr, physProps2KHRFunc); |
| |
| VkPhysicalDeviceProperties2KHR props2 = { |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, 0, |
| }; |
| |
| physProps2KHRFunc(mPhysicalDevice, &props2); |
| |
| VkPhysicalDeviceProperties props; |
| vk->vkGetPhysicalDeviceProperties(mPhysicalDevice, &props); |
| |
| EXPECT_EQ(props.vendorID, props2.properties.vendorID); |
| EXPECT_EQ(props.deviceID, props2.properties.deviceID); |
| } |
| |
| // Tests VK_KHR_get_physical_device_properties2: |
| // new API: vkGetPhysicalDeviceFeatures2KHR |
| TEST_P(VulkanHalTest, GetPhysicalDeviceFeatures2KHR) { |
| if (!mInstanceHasGetPhysicalDeviceProperties2Support) { |
| printf("Warning: Not testing VK_KHR_physical_device_properties2, not " |
| "supported\n"); |
| return; |
| } |
| |
| PFN_vkGetPhysicalDeviceFeatures2KHR physDeviceFeatures = |
| (PFN_vkGetPhysicalDeviceFeatures2KHR)vk->vkGetInstanceProcAddr( |
| mInstance, "vkGetPhysicalDeviceFeatures2KHR"); |
| |
| EXPECT_NE(nullptr, physDeviceFeatures); |
| |
| VkPhysicalDeviceFeatures2 features2 = { |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, 0, |
| }; |
| |
| physDeviceFeatures(mPhysicalDevice, &features2); |
| } |
| |
| // Tests VK_KHR_get_physical_device_properties2: |
| // new API: vkGetPhysicalDeviceImageFormatProperties2KHR |
| TEST_P(VulkanHalTest, GetPhysicalDeviceImageFormatProperties2KHR) { |
| if (!mInstanceHasGetPhysicalDeviceProperties2Support) { |
| printf("Warning: Not testing VK_KHR_physical_device_properties2, not " |
| "supported\n"); |
| return; |
| } |
| |
| PFN_vkGetPhysicalDeviceImageFormatProperties2KHR |
| physDeviceImageFormatPropertiesFunc = |
| (PFN_vkGetPhysicalDeviceImageFormatProperties2KHR) |
| vk->vkGetInstanceProcAddr(mInstance, |
| "vkGetPhysicalDeviceImageForm" |
| "atProperties2KHR"); |
| |
| EXPECT_NE(nullptr, physDeviceImageFormatPropertiesFunc); |
| |
| VkPhysicalDeviceImageFormatInfo2 imageFormatInfo = { |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2, 0, |
| VK_FORMAT_R8G8B8A8_UNORM, |
| VK_IMAGE_TYPE_2D, |
| VK_IMAGE_TILING_OPTIMAL, |
| VK_IMAGE_USAGE_SAMPLED_BIT, |
| 0, |
| }; |
| |
| VkImageFormatProperties2 res = { |
| VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2, 0, |
| }; |
| |
| EXPECT_EQ(VK_SUCCESS, physDeviceImageFormatPropertiesFunc( |
| mPhysicalDevice, &imageFormatInfo, &res)); |
| } |
| |
| // Tests that if we create an instance and the API version is less than 1.1, |
| // we return null for 1.1 core API calls. |
| TEST_P(VulkanHalTest, Hide1_1FunctionPointers) { |
| VkPhysicalDeviceProperties props; |
| |
| vk->vkGetPhysicalDeviceProperties(mPhysicalDevice, &props); |
| |
| if (props.apiVersion < VK_API_VERSION_1_1) { |
| EXPECT_EQ(nullptr, |
| vk->vkGetDeviceProcAddr(mDevice, "vkTrimCommandPool")); |
| } else { |
| EXPECT_NE(nullptr, |
| vk->vkGetDeviceProcAddr(mDevice, "vkTrimCommandPool")); |
| } |
| } |
| |
| // Tests VK_ANDROID_external_memory_android_hardware_buffer's allocation API. |
| // The simplest: export allocate device local memory. |
| // Disabled for now: currently goes down invalid paths in the GL side |
| TEST_P(VulkanHalTest, DISABLED_AndroidHardwareBufferAllocate_ExportDeviceLocal) { |
| if (!mDeviceHasExternalMemorySupport) return; |
| |
| VkDeviceMemory memory; |
| AHardwareBuffer* ahw; |
| exportAllocateAndroidHardwareBuffer( |
| nullptr, 4096, mDeviceLocalMemoryTypeIndex, |
| &memory, &ahw); |
| |
| vk->vkFreeMemory(mDevice, memory, nullptr); |
| } |
| |
| // Test AHB allocation via import. |
| // Disabled for now: currently goes down invalid paths in the GL side |
| TEST_P(VulkanHalTest, DISABLED_AndroidHardwareBufferAllocate_ImportDeviceLocal) { |
| if (!mDeviceHasExternalMemorySupport) return; |
| |
| AHardwareBuffer* testBuf = allocateAndroidHardwareBuffer(); |
| |
| VkDeviceMemory memory; |
| |
| importAllocateAndroidHardwareBuffer( |
| nullptr, |
| 4096, // also checks that the top-level allocation size is ignored |
| mDeviceLocalMemoryTypeIndex, |
| testBuf, |
| &memory); |
| |
| vk->vkFreeMemory(mDevice, memory, nullptr); |
| |
| AHardwareBuffer_release(testBuf); |
| } |
| |
| // Test AHB allocation via export, but with a dedicated allocation (image). |
| // Disabled for now: currently goes down invalid paths in the GL side |
| TEST_P(VulkanHalTest, DISABLED_AndroidHardwareBufferAllocate_Dedicated_Export) { |
| if (!mDeviceHasExternalMemorySupport) return; |
| |
| VkImage testAhbImage; |
| createExternalImage(&testAhbImage); |
| |
| VkMemoryDedicatedAllocateInfo dedicatedAi = { |
| VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO, 0, |
| testAhbImage, VK_NULL_HANDLE, |
| }; |
| |
| VkDeviceMemory memory; |
| AHardwareBuffer* buffer; |
| exportAllocateAndroidHardwareBuffer( |
| &dedicatedAi, |
| 4096, |
| getFirstMemoryTypeIndexForImage(testAhbImage), |
| &memory, &buffer); |
| |
| EXPECT_EQ(VK_SUCCESS, vk->vkBindImageMemory(mDevice, testAhbImage, memory, 0)); |
| |
| vk->vkFreeMemory(mDevice, memory, nullptr); |
| vk->vkDestroyImage(mDevice, testAhbImage, nullptr); |
| } |
| |
| // Test AHB allocation via import, but with a dedicated allocation (image). |
| // Disabled for now: currently goes down invalid paths in the GL side |
| TEST_P(VulkanHalTest, DISABLED_AndroidHardwareBufferAllocate_Dedicated_Import) { |
| if (!mDeviceHasExternalMemorySupport) return; |
| |
| AHardwareBuffer* testBuf = |
| allocateAndroidHardwareBuffer(); |
| |
| VkImage testAhbImage; |
| createExternalImage(&testAhbImage); |
| |
| VkMemoryDedicatedAllocateInfo dedicatedAi = { |
| VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO, 0, |
| testAhbImage, VK_NULL_HANDLE, |
| }; |
| |
| VkDeviceMemory memory; |
| importAllocateAndroidHardwareBuffer( |
| &dedicatedAi, |
| 4096, // also checks that the top-level allocation size is ignored |
| getFirstMemoryTypeIndexForImage(testAhbImage), |
| testBuf, |
| &memory); |
| |
| EXPECT_EQ(VK_SUCCESS, |
| vk->vkBindImageMemory(mDevice, testAhbImage, memory, 0)); |
| |
| vk->vkFreeMemory(mDevice, memory, nullptr); |
| vk->vkDestroyImage(mDevice, testAhbImage, nullptr); |
| |
| AHardwareBuffer_release(testBuf); |
| } |
| |
| // Test many host visible allocations. |
| TEST_P(VulkanHalTest, HostVisibleAllocations) { |
| static constexpr VkDeviceSize kTestAllocSizesSmall[] = |
| { 4, 5, 6, 16, 32, 37, 64, 255, 256, 267, |
| 1024, 1023, 1025, 4095, 4096, |
| 4097, 16333, }; |
| |
| static constexpr size_t kNumSmallAllocSizes = android::base::arraySize(kTestAllocSizesSmall); |
| static constexpr size_t kNumTrialsSmall = 1000; |
| |
| static constexpr VkDeviceSize kTestAllocSizesLarge[] = |
| { 1048576, 1048577, 1048575 }; |
| |
| static constexpr size_t kNumLargeAllocSizes = android::base::arraySize(kTestAllocSizesLarge); |
| static constexpr size_t kNumTrialsLarge = 20; |
| |
| static constexpr float kLargeAllocChance = 0.05; |
| |
| std::default_random_engine generator; |
| // Use a consistent seed value to avoid flakes |
| generator.seed(0); |
| |
| std::uniform_int_distribution<size_t> |
| smallAllocIndexDistribution(0, kNumSmallAllocSizes - 1); |
| std::uniform_int_distribution<size_t> |
| largeAllocIndexDistribution(0, kNumLargeAllocSizes - 1); |
| std::bernoulli_distribution largeAllocDistribution(kLargeAllocChance); |
| |
| size_t smallAllocCount = 0; |
| size_t largeAllocCount = 0; |
| |
| VkMemoryAllocateInfo allocInfo = { |
| VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, 0, |
| 0, |
| mHostVisibleMemoryTypeIndex, |
| }; |
| |
| std::vector<VkDeviceMemory> allocs; |
| |
| while (smallAllocCount < kNumTrialsSmall || |
| largeAllocCount < kNumTrialsLarge) { |
| |
| VkDeviceMemory mem = VK_NULL_HANDLE; |
| void* hostPtr = nullptr; |
| |
| if (largeAllocDistribution(generator)) { |
| if (largeAllocCount < kNumTrialsLarge) { |
| fprintf(stderr, "%s: large alloc\n", __func__); |
| allocInfo.allocationSize = |
| kTestAllocSizesLarge[ |
| largeAllocIndexDistribution(generator)]; |
| ++largeAllocCount; |
| } |
| } else { |
| if (smallAllocCount < kNumTrialsSmall) { |
| allocInfo.allocationSize = |
| kTestAllocSizesSmall[ |
| smallAllocIndexDistribution(generator)]; |
| ++smallAllocCount; |
| } |
| } |
| |
| EXPECT_EQ(VK_SUCCESS, |
| vk->vkAllocateMemory(mDevice, &allocInfo, nullptr, &mem)); |
| |
| if (!mem) continue; |
| |
| allocs.push_back(mem); |
| |
| EXPECT_EQ(VK_SUCCESS, |
| vk->vkMapMemory(mDevice, mem, 0, VK_WHOLE_SIZE, 0, &hostPtr)); |
| |
| memset(hostPtr, 0xff, 4); |
| |
| VkMappedMemoryRange toFlush = { |
| VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, 0, |
| mem, 0, 4, |
| }; |
| |
| EXPECT_EQ(VK_SUCCESS, vk->vkFlushMappedMemoryRanges(mDevice, 1, &toFlush)); |
| EXPECT_EQ(VK_SUCCESS, vk->vkInvalidateMappedMemoryRanges(mDevice, 1, &toFlush)); |
| |
| for (uint32_t i = 0; i < 4; ++i) { |
| EXPECT_EQ(0xff, *((uint8_t*)hostPtr + i)); |
| } |
| } |
| |
| for (auto mem : allocs) { |
| vk->vkUnmapMemory(mDevice, mem); |
| vk->vkFreeMemory(mDevice, mem, nullptr); |
| } |
| } |
| |
| TEST_P(VulkanHalTest, BufferCreate) { |
| VkBufferCreateInfo bufCi = { |
| VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, 0, 0, |
| 4096, |
| VK_BUFFER_USAGE_TRANSFER_SRC_BIT, |
| VK_SHARING_MODE_EXCLUSIVE, |
| 0, nullptr, |
| }; |
| |
| VkBuffer buffer; |
| vk->vkCreateBuffer(mDevice, &bufCi, nullptr, &buffer); |
| |
| VkMemoryRequirements memReqs; |
| vk->vkGetBufferMemoryRequirements(mDevice, buffer, &memReqs); |
| |
| vk->vkDestroyBuffer(mDevice, buffer, nullptr); |
| } |
| |
| TEST_P(VulkanHalTest, SnapshotSaveLoad) { |
| // TODO: Skip if using address space graphics |
| if (usingAddressSpaceGraphics()) { |
| printf("%s: skipping, ASG does not yet support snapshots\n", __func__); |
| return; |
| } |
| androidSnapshot_save("test_snapshot"); |
| androidSnapshot_load("test_snapshot"); |
| } |
| |
| TEST_P(VulkanHalTest, SnapshotSaveLoadSimpleNonDispatchable) { |
| // TODO: Skip if using address space graphics |
| if (usingAddressSpaceGraphics()) { |
| printf("%s: skipping, ASG does not yet support snapshots\n", __func__); |
| return; |
| } |
| VkBufferCreateInfo bufCi = { |
| VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, 0, 0, |
| 4096, |
| VK_BUFFER_USAGE_TRANSFER_SRC_BIT, |
| VK_SHARING_MODE_EXCLUSIVE, |
| 0, nullptr, |
| }; |
| |
| VkFence fence; |
| VkFenceCreateInfo fenceCi = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, 0, }; |
| vk->vkCreateFence(mDevice, &fenceCi, nullptr, &fence); |
| |
| fprintf(stderr, "%s: guest fence: %p\n", __func__, fence); |
| |
| VkBuffer buffer; |
| vk->vkCreateBuffer(mDevice, &bufCi, nullptr, &buffer); |
| |
| fprintf(stderr, "%s: guest buffer: %p\n", __func__, buffer); |
| |
| androidSnapshot_save("test_snapshot"); |
| androidSnapshot_load("test_snapshot"); |
| |
| VkMemoryRequirements memReqs; |
| vk->vkGetBufferMemoryRequirements(mDevice, buffer, &memReqs); |
| vk->vkDestroyBuffer(mDevice, buffer, nullptr); |
| |
| vk->vkDestroyFence(mDevice, fence, nullptr); |
| } |
| |
| // Tests save/load of host visible memory. This is not yet a viable host-only |
| // test because, the only way to really test it is to be able to preserve a |
| // host visible address for the simulated guest while the backing memory under |
| // it changes due to the new snapshot. In other words, this is arbitrary |
| // remapping of virtual addrs and is functionality that does not exist on |
| // Linux/macOS. It would ironically require a hypervisor (or an OS that |
| // supports freer ways of mapping memory) in order to test properly. |
| // Disabled for now: currently goes down invalid paths in the GL side |
| TEST_P(VulkanHalTest, DISABLED_SnapshotSaveLoadHostVisibleMemory) { |
| // TODO: Skip if using address space graphics |
| if (usingAddressSpaceGraphics()) { |
| printf("%s: skipping, ASG does not yet support snapshots\n", __func__); |
| return; |
| } |
| |
| static constexpr VkDeviceSize kTestAlloc = 16 * 1024; |
| VkMemoryAllocateInfo allocInfo = { |
| VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, 0, |
| kTestAlloc, |
| mHostVisibleMemoryTypeIndex, |
| }; |
| VkDeviceMemory mem; |
| EXPECT_EQ(VK_SUCCESS, vk->vkAllocateMemory(mDevice, &allocInfo, nullptr, &mem)); |
| |
| void* hostPtr; |
| EXPECT_EQ(VK_SUCCESS, vk->vkMapMemory(mDevice, mem, 0, VK_WHOLE_SIZE, 0, &hostPtr)); |
| androidSnapshot_save("test_snapshot"); |
| androidSnapshot_load("test_snapshot"); |
| |
| |
| memset(hostPtr, 0xff, kTestAlloc); |
| |
| VkMappedMemoryRange toFlush = { |
| VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, 0, |
| mem, 0, kTestAlloc, |
| }; |
| |
| EXPECT_EQ(VK_SUCCESS, vk->vkFlushMappedMemoryRanges(mDevice, 1, &toFlush)); |
| EXPECT_EQ(VK_SUCCESS, vk->vkInvalidateMappedMemoryRanges(mDevice, 1, &toFlush)); |
| |
| vk->vkUnmapMemory(mDevice, mem); |
| vk->vkFreeMemory(mDevice, mem, nullptr); |
| } |
| |
| // Tests save/load of a dispatchable handle, such as VkCommandBuffer. |
| // Note that the internal state of the command buffer is not snapshotted yet. |
| TEST_P(VulkanHalTest, SnapshotSaveLoadSimpleDispatchable) { |
| // TODO: Skip if using address space graphics |
| if (usingAddressSpaceGraphics()) { |
| printf("%s: skipping, ASG does not yet support snapshots\n", __func__); |
| return; |
| } |
| VkCommandPoolCreateInfo poolCi = { |
| VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, 0, 0, mGraphicsQueueFamily, |
| }; |
| |
| VkCommandPool pool; |
| vk->vkCreateCommandPool(mDevice, &poolCi, nullptr, &pool); |
| |
| VkCommandBuffer cb; |
| VkCommandBufferAllocateInfo cbAi = { |
| VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, 0, |
| pool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1, |
| }; |
| |
| vk->vkAllocateCommandBuffers(mDevice, &cbAi, &cb); |
| |
| androidSnapshot_save("test_snapshot"); |
| androidSnapshot_load("test_snapshot"); |
| |
| vk->vkFreeCommandBuffers(mDevice, pool, 1, &cb); |
| vk->vkDestroyCommandPool(mDevice, pool, nullptr); |
| } |
| |
| // Tests that dependencies are respected between different handle types, |
| // such as VkImage and VkImageView. |
| TEST_P(VulkanHalTest, SnapshotSaveLoadDependentHandlesImageView) { |
| // TODO: Skip if using address space graphics |
| if (usingAddressSpaceGraphics()) { |
| printf("%s: skipping, ASG does not yet support snapshots\n", __func__); |
| return; |
| } |
| VkImage image; |
| VkImageCreateInfo imageCi = { |
| VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, 0, 0, |
| VK_IMAGE_TYPE_2D, |
| VK_FORMAT_R8G8B8A8_UNORM, |
| { 1, 1, 1, }, |
| 1, 1, |
| VK_SAMPLE_COUNT_1_BIT, |
| VK_IMAGE_TILING_LINEAR, |
| VK_IMAGE_USAGE_TRANSFER_DST_BIT, |
| VK_SHARING_MODE_EXCLUSIVE, |
| 0, nullptr, |
| VK_IMAGE_LAYOUT_UNDEFINED, |
| }; |
| |
| vk->vkCreateImage(mDevice, &imageCi, nullptr, &image); |
| |
| VkImageView imageView; |
| VkImageViewCreateInfo imageViewCi = { |
| VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, 0, 0, |
| image, |
| VK_IMAGE_VIEW_TYPE_2D, |
| VK_FORMAT_R8G8B8A8_UNORM, |
| { |
| VK_COMPONENT_SWIZZLE_IDENTITY, |
| VK_COMPONENT_SWIZZLE_IDENTITY, |
| VK_COMPONENT_SWIZZLE_IDENTITY, |
| VK_COMPONENT_SWIZZLE_IDENTITY, |
| }, |
| { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1, }, |
| }; |
| |
| vk->vkCreateImageView(mDevice, &imageViewCi, nullptr, &imageView); |
| |
| androidSnapshot_save("test_snapshot"); |
| androidSnapshot_load("test_snapshot"); |
| |
| vk->vkDestroyImageView(mDevice, imageView, nullptr); |
| vk->vkCreateImageView(mDevice, &imageViewCi, nullptr, &imageView); |
| vk->vkDestroyImageView(mDevice, imageView, nullptr); |
| |
| vk->vkDestroyImage(mDevice, image, nullptr); |
| } |
| |
| // Tests beginning and ending command buffers from separate threads. |
| TEST_P(VulkanHalTest, SeparateThreadCommandBufferBeginEnd) { |
| Lock lock; |
| ConditionVariable cvSequence; |
| uint32_t begins = 0; |
| uint32_t ends = 0; |
| constexpr uint32_t kTrials = 1000; |
| |
| VkCommandPoolCreateInfo poolCi = { |
| VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, 0, 0, mGraphicsQueueFamily, |
| }; |
| |
| VkCommandPool pool; |
| vk->vkCreateCommandPool(mDevice, &poolCi, nullptr, &pool); |
| |
| VkCommandBuffer cb; |
| VkCommandBufferAllocateInfo cbAi = { |
| VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, 0, |
| pool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1, |
| }; |
| |
| vk->vkAllocateCommandBuffers(mDevice, &cbAi, &cb); |
| |
| auto timeoutDeadline = []() { |
| return System::get()->getUnixTimeUs() + 5000000; // 5 s |
| }; |
| |
| FunctorThread beginThread([this, cb, &lock, &cvSequence, &begins, &ends, timeoutDeadline]() { |
| |
| while (begins < kTrials) { |
| AutoLock a(lock); |
| |
| while (ends != begins) { |
| if (!cvSequence.timedWait(&lock, timeoutDeadline())) { |
| EXPECT_TRUE(false) << "Error: begin thread timed out!"; |
| return 0; |
| } |
| } |
| |
| VkCommandBufferBeginInfo beginInfo = { |
| VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, 0, |
| VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, 0, |
| }; |
| |
| vk->vkBeginCommandBuffer(cb, &beginInfo); |
| |
| ++begins; |
| cvSequence.signal(); |
| } |
| |
| vk->vkDeviceWaitIdle(mDevice); |
| return 0; |
| }); |
| |
| FunctorThread endThread([this, cb, &lock, &cvSequence, &begins, &ends, timeoutDeadline]() { |
| |
| while (ends < kTrials) { |
| AutoLock a(lock); |
| |
| while (begins - ends != 1) { |
| if (!cvSequence.timedWait(&lock, timeoutDeadline())) { |
| EXPECT_TRUE(false) << "Error: end thread timed out!"; |
| return 0; |
| } |
| } |
| |
| vk->vkEndCommandBuffer(cb); |
| |
| ++ends; |
| cvSequence.signal(); |
| } |
| |
| vk->vkDeviceWaitIdle(mDevice); |
| return 0; |
| }); |
| |
| beginThread.start(); |
| endThread.start(); |
| beginThread.wait(); |
| endThread.wait(); |
| |
| vk->vkFreeCommandBuffers(mDevice, pool, 1, &cb); |
| vk->vkDestroyCommandPool(mDevice, pool, nullptr); |
| } |
| |
| // Tests creating a bunch of descriptor sets and freeing them via vkFreeDescriptorSet. |
| // 1. Via vkFreeDescriptorSet directly |
| // 2. Via vkResetDescriptorPool |
| // 3. Via vkDestroyDescriptorPool |
| // 4. Via vkResetDescriptorPool and double frees in vkFreeDescriptorSet |
| // 5. Via vkResetDescriptorPool and double frees in vkFreeDescriptorSet |
| // 4. Via vkResetDescriptorPool, creating more, and freeing vai vkFreeDescriptorSet |
| // (because vkFree* APIs are expected to never fail) |
| // https://github.com/KhronosGroup/Vulkan-Docs/issues/1070 |
| TEST_P(VulkanHalTest, DescriptorSetAllocFreeBasic) { |
| const uint32_t kSetCount = 4; |
| VkDescriptorPool pool; |
| VkDescriptorSetLayout setLayout; |
| std::vector<VkDescriptorSet> sets(kSetCount); |
| |
| EXPECT_EQ(VK_SUCCESS, allocateTestDescriptorSets( |
| kSetCount, kSetCount, |
| VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, |
| VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, |
| &pool, &setLayout, sets.data())); |
| |
| EXPECT_EQ(VK_SUCCESS, vk->vkFreeDescriptorSets( |
| mDevice, pool, kSetCount, sets.data())); |
| |
| // The double free should also work |
| EXPECT_EQ(VK_SUCCESS, vk->vkFreeDescriptorSets( |
| mDevice, pool, kSetCount, sets.data())); |
| |
| // Alloc/free again should also work |
| EXPECT_EQ(VK_SUCCESS, |
| allocateTestDescriptorSetsFromExistingPool( |
| kSetCount, pool, setLayout, sets.data())); |
| |
| EXPECT_EQ(VK_SUCCESS, vk->vkFreeDescriptorSets( |
| mDevice, pool, kSetCount, sets.data())); |
| |
| vk->vkDestroyDescriptorPool(mDevice, pool, nullptr); |
| } |
| |
| // Tests creating a bunch of descriptor sets and freeing them via |
| // vkResetDescriptorPool, and that vkFreeDescriptorSets still succeeds. |
| TEST_P(VulkanHalTest, DescriptorSetAllocFreeReset) { |
| const uint32_t kSetCount = 4; |
| VkDescriptorPool pool; |
| VkDescriptorSetLayout setLayout; |
| std::vector<VkDescriptorSet> sets(kSetCount); |
| |
| EXPECT_EQ(VK_SUCCESS, allocateTestDescriptorSets( |
| kSetCount, kSetCount, |
| VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, |
| VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, |
| &pool, &setLayout, sets.data())); |
| |
| EXPECT_EQ(VK_SUCCESS, vk->vkResetDescriptorPool( |
| mDevice, pool, 0)); |
| |
| // The double free should also work |
| EXPECT_EQ(VK_SUCCESS, vk->vkFreeDescriptorSets( |
| mDevice, pool, kSetCount, sets.data())); |
| |
| // Alloc/reset/free again should also work |
| EXPECT_EQ(VK_SUCCESS, |
| allocateTestDescriptorSetsFromExistingPool( |
| kSetCount, pool, setLayout, sets.data())); |
| |
| EXPECT_EQ(VK_SUCCESS, vk->vkResetDescriptorPool( |
| mDevice, pool, 0)); |
| |
| EXPECT_EQ(VK_SUCCESS, vk->vkFreeDescriptorSets( |
| mDevice, pool, kSetCount, sets.data())); |
| |
| vk->vkDestroyDescriptorPool(mDevice, pool, nullptr); |
| } |
| |
| // Tests creating a bunch of descriptor sets and freeing them via vkDestroyDescriptorPool, and that vkFreeDescriptorSets still succeeds. |
| TEST_P(VulkanHalTest, DescriptorSetAllocFreeDestroy) { |
| const uint32_t kSetCount = 4; |
| VkDescriptorPool pool; |
| VkDescriptorSetLayout setLayout; |
| std::vector<VkDescriptorSet> sets(kSetCount); |
| |
| EXPECT_EQ(VK_SUCCESS, allocateTestDescriptorSets( |
| kSetCount, kSetCount, |
| VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, |
| VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, |
| &pool, &setLayout, sets.data())); |
| |
| vk->vkDestroyDescriptorPool(mDevice, pool, nullptr); |
| |
| EXPECT_EQ(VK_SUCCESS, vk->vkFreeDescriptorSets( |
| mDevice, pool, kSetCount, sets.data())); |
| } |
| |
| // Tests that immutable sampler descriptors properly cause |
| // the |sampler| field of VkWriteDescriptorSet's descriptor image info |
| // to be ignored. |
| TEST_P(VulkanHalTest, ImmutableSamplersSuppressVkWriteDescriptorSetSampler) { |
| const uint32_t kSetCount = 4; |
| std::vector<bool> bindingImmutabilities = { |
| false, |
| true, |
| false, |
| false, |
| }; |
| |
| VkSampler sampler; |
| VkDescriptorPool pool; |
| VkDescriptorSetLayout setLayout; |
| std::vector<VkDescriptorSet> sets(kSetCount); |
| |
| EXPECT_EQ(VK_SUCCESS, |
| allocateImmutableSamplerDescriptorSets( |
| kSetCount * bindingImmutabilities.size(), |
| kSetCount, |
| bindingImmutabilities, |
| &sampler, |
| &pool, |
| &setLayout, |
| sets.data())); |
| |
| for (uint32_t i = 0; i < bindingImmutabilities.size(); ++i) { |
| VkDescriptorImageInfo imageInfo = { |
| bindingImmutabilities[i] ? (VkSampler)0xdeadbeef : sampler, |
| 0, |
| VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, |
| }; |
| |
| VkWriteDescriptorSet write = { |
| VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 0, |
| sets[0], |
| 1, |
| 0, |
| 1, |
| VK_DESCRIPTOR_TYPE_SAMPLER, |
| &imageInfo, |
| nullptr, |
| nullptr, |
| }; |
| |
| vk->vkUpdateDescriptorSets(mDevice, 1, &write, 0, nullptr); |
| } |
| |
| vk->vkDestroyDescriptorPool(mDevice, pool, nullptr); |
| vk->vkDestroyDescriptorSetLayout(mDevice, setLayout, nullptr); |
| vk->vkDestroySampler(mDevice, sampler, nullptr); |
| } |
| |
| |
| // Tests vkGetImageMemoryRequirements2 |
| TEST_P(VulkanHalTest, GetImageMemoryRequirements2) { |
| VkImageCreateInfo testImageCi = { |
| VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, nullptr, |
| 0, |
| VK_IMAGE_TYPE_2D, |
| VK_FORMAT_R8G8B8A8_UNORM, |
| { kWindowSize, kWindowSize, 1, }, |
| 1, 1, |
| VK_SAMPLE_COUNT_1_BIT, |
| VK_IMAGE_TILING_OPTIMAL, |
| VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, |
| VK_SHARING_MODE_EXCLUSIVE, |
| 0, nullptr /* shared queue families */, |
| VK_IMAGE_LAYOUT_UNDEFINED, |
| }; |
| |
| VkImage testImage; |
| EXPECT_EQ(VK_SUCCESS, vk->vkCreateImage(mDevice, &testImageCi, nullptr, |
| &testImage)); |
| |
| VkImageMemoryRequirementsInfo2 info2 = { |
| VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR, 0, |
| testImage, |
| }; |
| |
| VkMemoryDedicatedRequirements dedicatedReqs { |
| VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR, 0, |
| }; |
| |
| VkMemoryRequirements2 reqs2 = { |
| VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR, 0, |
| }; |
| |
| reqs2.pNext = &dedicatedReqs; |
| |
| PFN_vkGetImageMemoryRequirements2KHR func = |
| (PFN_vkGetImageMemoryRequirements2KHR) |
| vk->vkGetDeviceProcAddr(mDevice, "vkGetImageMemoryRequirements2KHR"); |
| |
| EXPECT_NE(nullptr, func); |
| |
| func(mDevice, &info2, &reqs2); |
| |
| vk->vkDestroyImage(mDevice, testImage, nullptr); |
| } |
| |
| TEST_P(VulkanHalTest, ProcessCleanup) { |
| static constexpr uint32_t kNumTrials = 10; |
| static constexpr uint32_t kNumImagesPerTrial = 100; |
| |
| for (uint32_t i = 0; i < kNumTrials; ++i) { |
| VkImage images[kNumImagesPerTrial]; |
| for (uint32_t j = 0; j < kNumImagesPerTrial; ++j) { |
| VkImageCreateInfo imageCi = { |
| VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, 0, 0, |
| VK_IMAGE_TYPE_2D, |
| VK_FORMAT_R8G8B8A8_UNORM, |
| { 1, 1, 1, }, |
| 1, 1, |
| VK_SAMPLE_COUNT_1_BIT, |
| VK_IMAGE_TILING_LINEAR, |
| VK_IMAGE_USAGE_TRANSFER_DST_BIT, |
| VK_SHARING_MODE_EXCLUSIVE, |
| 0, nullptr, |
| VK_IMAGE_LAYOUT_UNDEFINED, |
| }; |
| |
| vk->vkCreateImage(mDevice, &imageCi, nullptr, &images[j]); |
| } |
| |
| for (uint32_t j = 0; j < kNumImagesPerTrial; ++j) { |
| vk->vkDestroyImage(mDevice, images[j], nullptr); |
| } |
| |
| restartProcessPipeAndHostConnection(); |
| setupVulkan(); |
| } |
| } |
| |
| // Multithreaded benchmarks: Speed of light with simple vkCmd's. |
| // |
| // Currently disabled until we land |
| // VulkanQueueSubmitWithCommands---syncEncodersFor** interferes with rc |
| // encoders |
| TEST_P(VulkanHalTest, DISABLED_MultithreadedSimpleCommand) { |
| Lock lock; |
| |
| constexpr uint32_t kThreadCount = 4; |
| VkDescriptorPool pool; |
| VkDescriptorSetLayout setLayout; |
| |
| std::vector<VkDescriptorSet> sets(kThreadCount); |
| |
| // Setup descriptor sets |
| EXPECT_EQ(VK_SUCCESS, allocateTestDescriptorSets( |
| kThreadCount, kThreadCount, |
| VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, |
| VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, |
| &pool, &setLayout, sets.data())); |
| |
| VkPipelineLayout pipelineLayout; |
| |
| VkPipelineLayoutCreateInfo pipelineLayoutCi = { |
| VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, 0, 0, |
| 1, &setLayout, |
| 0, nullptr, |
| }; |
| vk->vkCreatePipelineLayout(mDevice, &pipelineLayoutCi, nullptr, &pipelineLayout); |
| |
| // Setup command buffers |
| VkCommandPoolCreateInfo poolCi = { |
| VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, 0, 0, mGraphicsQueueFamily, |
| }; |
| |
| VkCommandPool commandPool; |
| vk->vkCreateCommandPool(mDevice, &poolCi, nullptr, &commandPool); |
| |
| VkCommandBuffer cbs[kThreadCount]; |
| VkCommandBufferAllocateInfo cbAi = { |
| VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, 0, |
| commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, kThreadCount, |
| }; |
| |
| vk->vkAllocateCommandBuffers(mDevice, &cbAi, cbs); |
| |
| std::vector<FunctorThread*> threads; |
| |
| constexpr uint32_t kRecordsPerThread = 20000; |
| constexpr uint32_t kRepeatSubmits = 1; |
| constexpr uint32_t kTotalRecords = kThreadCount * kRecordsPerThread * kRepeatSubmits; |
| |
| for (uint32_t i = 0; i < kThreadCount; ++i) { |
| FunctorThread* thread = new FunctorThread([this, &lock, cbs, sets, pipelineLayout, i]() { |
| VkCommandBufferBeginInfo beginInfo = { |
| VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, 0, |
| VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, 0, |
| }; |
| |
| for (uint32_t k = 0; k < kRepeatSubmits; ++k) { |
| |
| vk->vkBeginCommandBuffer(cbs[i], &beginInfo); |
| VkRect2D scissor = { |
| { 0, 0, }, |
| { 256, 256, }, |
| }; |
| |
| for (uint32_t j = 0; j < kRecordsPerThread; ++j) { |
| vk->vkCmdBindDescriptorSets( |
| cbs[i], |
| VK_PIPELINE_BIND_POINT_GRAPHICS, |
| pipelineLayout, 0, 1, &sets[i], 0, nullptr); |
| } |
| |
| vk->vkEndCommandBuffer(cbs[i]); |
| VkSubmitInfo si = { |
| VK_STRUCTURE_TYPE_SUBMIT_INFO, 0, |
| 0, 0, |
| 0, |
| 1, &cbs[i], |
| 0, 0, |
| }; |
| |
| { |
| AutoLock queueLock(lock); |
| vk->vkQueueSubmit(mQueue, 1, &si, 0); |
| } |
| |
| } |
| VkPhysicalDeviceMemoryProperties memProps; |
| vk->vkGetPhysicalDeviceMemoryProperties(mPhysicalDevice, &memProps); |
| return 0; |
| }); |
| threads.push_back(thread); |
| } |
| |
| auto cpuTimeStart = System::cpuTime(); |
| |
| for (uint32_t i = 0; i < kThreadCount; ++i) { |
| threads[i]->start(); |
| } |
| |
| for (uint32_t i = 0; i < kThreadCount; ++i) { |
| threads[i]->wait(); |
| delete threads[i]; |
| } |
| |
| vk->vkQueueWaitIdle(mQueue); |
| vk->vkDeviceWaitIdle(mDevice); |
| |
| auto cpuTime = System::cpuTime() - cpuTimeStart; |
| |
| uint64_t duration_us = cpuTime.wall_time_us; |
| uint64_t duration_cpu_us = cpuTime.usageUs(); |
| |
| float ms = duration_us / 1000.0f; |
| float sec = duration_us / 1000000.0f; |
| |
| float submitHz = (float)kTotalRecords / sec; |
| |
| printf("Record %u times in %f ms. Rate: %f Hz per thread: %f Hz\n", kTotalRecords, ms, submitHz, (float)submitHz / (float)kThreadCount); |
| |
| vk->vkFreeCommandBuffers(mDevice, commandPool, 1, cbs); |
| vk->vkDestroyCommandPool(mDevice, commandPool, nullptr); |
| |
| EXPECT_EQ(VK_SUCCESS, vk->vkFreeDescriptorSets( |
| mDevice, pool, kThreadCount, sets.data())); |
| |
| vk->vkDestroyDescriptorPool(mDevice, pool, nullptr); |
| } |
| |
| // Multithreaded benchmarks: Round trip speed of light. |
| // Currently disabled until we land |
| // VulkanQueueSubmitWithCommands---syncEncodersFor** interferes with rc |
| // encoders |
| TEST_P(VulkanHalTest, DISABLED_MultithreadedRoundTrip) { |
| android::base::enableTracing(); |
| |
| constexpr uint32_t kThreadCount = 6; |
| |
| std::vector<FunctorThread*> threads; |
| constexpr uint32_t kRecordsPerThread = 50; |
| constexpr uint32_t kTotalRecords = kThreadCount * kRecordsPerThread; |
| |
| for (uint32_t i = 0; i < kThreadCount; ++i) { |
| FunctorThread* thread = new FunctorThread([this]() { |
| for (uint32_t j = 0; j < kRecordsPerThread; ++j) { |
| VkPhysicalDeviceMemoryProperties memProps; |
| vk->vkGetPhysicalDeviceMemoryProperties(mPhysicalDevice, &memProps); |
| } |
| return 0; |
| }); |
| threads.push_back(thread); |
| } |
| |
| auto cpuTimeStart = System::cpuTime(); |
| |
| for (uint32_t i = 0; i < kThreadCount; ++i) { |
| threads[i]->start(); |
| } |
| |
| for (uint32_t i = 0; i < kThreadCount; ++i) { |
| threads[i]->wait(); |
| delete threads[i]; |
| } |
| |
| vk->vkDeviceWaitIdle(mDevice); |
| |
| auto cpuTime = System::cpuTime() - cpuTimeStart; |
| |
| uint64_t duration_us = cpuTime.wall_time_us; |
| uint64_t duration_cpu_us = cpuTime.usageUs(); |
| |
| float ms = duration_us / 1000.0f; |
| float sec = duration_us / 1000000.0f; |
| |
| float submitHz = (float)kTotalRecords / sec; |
| |
| printf("Round trip %u times in %f ms. Rate: %f Hz per thread: %f Hz\n", kTotalRecords, ms, submitHz, (float)submitHz / (float)kThreadCount); |
| android::base::disableTracing(); |
| usleep(1000000); |
| } |
| |
| // Secondary command buffers. |
| TEST_P(VulkanHalTest, SecondaryCommandBuffers) { |
| constexpr uint32_t kThreadCount = 1; |
| VkDescriptorPool pool; |
| VkDescriptorSetLayout setLayout; |
| |
| std::vector<VkDescriptorSet> sets(kThreadCount); |
| |
| // Setup descriptor sets |
| EXPECT_EQ(VK_SUCCESS, allocateTestDescriptorSets( |
| kThreadCount, kThreadCount, |
| VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, |
| VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, |
| &pool, &setLayout, sets.data())); |
| |
| VkPipelineLayout pipelineLayout; |
| |
| VkPipelineLayoutCreateInfo pipelineLayoutCi = { |
| VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, 0, 0, |
| 1, &setLayout, |
| 0, nullptr, |
| }; |
| vk->vkCreatePipelineLayout(mDevice, &pipelineLayoutCi, nullptr, &pipelineLayout); |
| |
| // Setup command buffers |
| VkCommandPoolCreateInfo poolCi = { |
| VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, 0, 0, mGraphicsQueueFamily, |
| }; |
| |
| VkCommandPool commandPool; |
| vk->vkCreateCommandPool(mDevice, &poolCi, nullptr, &commandPool); |
| |
| VkCommandBuffer cbs[kThreadCount]; |
| VkCommandBuffer cbs2[kThreadCount * 2]; |
| |
| VkCommandBufferAllocateInfo cbAi = { |
| VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, 0, |
| commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, kThreadCount, |
| }; |
| |
| vk->vkAllocateCommandBuffers(mDevice, &cbAi, cbs); |
| |
| VkCommandBufferAllocateInfo cbAi2 = { |
| VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, 0, |
| commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, kThreadCount * 2, |
| }; |
| |
| vk->vkAllocateCommandBuffers(mDevice, &cbAi2, cbs2); |
| |
| std::vector<FunctorThread*> threads; |
| |
| constexpr uint32_t kRecordsPerThread = 20000; |
| constexpr uint32_t kRepeatSubmits = 1; |
| constexpr uint32_t kTotalRecords = kThreadCount * kRecordsPerThread * kRepeatSubmits; |
| |
| for (uint32_t i = 0; i < kThreadCount; ++i) { |
| FunctorThread* thread = new FunctorThread([this, cbs, cbs2, sets, pipelineLayout, i]() { |
| VkCommandBufferInheritanceInfo inheritanceInfo = { |
| VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO, 0, |
| VK_NULL_HANDLE, 0, |
| VK_NULL_HANDLE, |
| VK_FALSE, |
| 0, |
| 0, |
| }; |
| |
| VkCommandBufferBeginInfo secondaryBeginInfo = { |
| VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, 0, |
| VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, &inheritanceInfo, |
| }; |
| |
| VkCommandBufferBeginInfo primaryBeginInfo = { |
| VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, 0, |
| VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, 0, |
| }; |
| |
| for (uint32_t k = 0; k < kRepeatSubmits; ++k) { |
| // Secondaries |
| { |
| VkCommandBuffer first = cbs2[2 * i]; |
| VkCommandBuffer second = cbs2[2 * i + 1]; |
| |
| vk->vkBeginCommandBuffer(first, &secondaryBeginInfo); |
| for (uint32_t j = 0; j < kRecordsPerThread / 2; ++j) { |
| vk->vkCmdBindDescriptorSets( |
| first, |
| VK_PIPELINE_BIND_POINT_GRAPHICS, |
| pipelineLayout, 0, 1, &sets[i], 0, nullptr); |
| } |
| vk->vkEndCommandBuffer(first); |
| |
| vk->vkBeginCommandBuffer(second, &secondaryBeginInfo); |
| for (uint32_t j = 0; j < kRecordsPerThread / 2; ++j) { |
| vk->vkCmdBindDescriptorSets( |
| second, |
| VK_PIPELINE_BIND_POINT_GRAPHICS, |
| pipelineLayout, 0, 1, &sets[i], 0, nullptr); |
| } |
| vk->vkEndCommandBuffer(second); |
| } |
| |
| vk->vkBeginCommandBuffer(cbs[i], &primaryBeginInfo); |
| vk->vkCmdExecuteCommands(cbs[i], 2, cbs2 + 2 * i); |
| vk->vkEndCommandBuffer(cbs[i]); |
| |
| VkSubmitInfo si = { |
| VK_STRUCTURE_TYPE_SUBMIT_INFO, 0, |
| 0, 0, |
| 0, |
| 1, &cbs[i], |
| 0, 0, |
| }; |
| |
| vk->vkQueueSubmit(mQueue, 1, &si, 0); |
| } |
| VkPhysicalDeviceMemoryProperties memProps; |
| vk->vkGetPhysicalDeviceMemoryProperties(mPhysicalDevice, &memProps); |
| return 0; |
| }); |
| threads.push_back(thread); |
| } |
| |
| auto cpuTimeStart = System::cpuTime(); |
| |
| for (uint32_t i = 0; i < kThreadCount; ++i) { |
| threads[i]->start(); |
| } |
| |
| for (uint32_t i = 0; i < kThreadCount; ++i) { |
| threads[i]->wait(); |
| delete threads[i]; |
| } |
| |
| vk->vkQueueWaitIdle(mQueue); |
| vk->vkDeviceWaitIdle(mDevice); |
| |
| auto cpuTime = System::cpuTime() - cpuTimeStart; |
| |
| uint64_t duration_us = cpuTime.wall_time_us; |
| uint64_t duration_cpu_us = cpuTime.usageUs(); |
| |
| float ms = duration_us / 1000.0f; |
| float sec = duration_us / 1000000.0f; |
| |
| float submitHz = (float)kTotalRecords / sec; |
| |
| printf("Record %u times in %f ms. Rate: %f Hz per thread: %f Hz\n", kTotalRecords, ms, submitHz, (float)submitHz / (float)kThreadCount); |
| |
| vk->vkFreeCommandBuffers(mDevice, commandPool, kThreadCount * 2, cbs2); |
| vk->vkFreeCommandBuffers(mDevice, commandPool, kThreadCount, cbs); |
| vk->vkDestroyCommandPool(mDevice, commandPool, nullptr); |
| |
| EXPECT_EQ(VK_SUCCESS, vk->vkFreeDescriptorSets( |
| mDevice, pool, kThreadCount, sets.data())); |
| |
| vk->vkDestroyDescriptorPool(mDevice, pool, nullptr); |
| } |
| |
| // Secondary command buffers. |
| TEST_P(VulkanHalTest, SecondaryCommandBuffersWithDescriptorSetUpdate) { |
| constexpr uint32_t kThreadCount = 1; |
| VkDescriptorPool pool; |
| VkDescriptorSetLayout setLayout; |
| |
| // Setup descriptor sets |
| uint32_t maxSetsTotal = 4; |
| |
| std::vector<VkDescriptorSet> sets(maxSetsTotal); |
| |
| VkSampler sampler; |
| VkBuffer buffers[2]; |
| |
| VkSamplerCreateInfo samplerCi = { |
| VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, 0, 0, |
| VK_FILTER_NEAREST, |
| VK_FILTER_NEAREST, |
| VK_SAMPLER_MIPMAP_MODE_NEAREST, |
| VK_SAMPLER_ADDRESS_MODE_REPEAT, |
| VK_SAMPLER_ADDRESS_MODE_REPEAT, |
| VK_SAMPLER_ADDRESS_MODE_REPEAT, |
| 0.0f, |
| VK_FALSE, |
| 1.0f, |
| VK_FALSE, |
| VK_COMPARE_OP_NEVER, |
| 0.0f, |
| 1.0f, |
| VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, |
| VK_FALSE, |
| }; |
| |
| VkBufferCreateInfo bufCi = { |
| VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, 0, 0, |
| 4096, |
| VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, |
| VK_SHARING_MODE_EXCLUSIVE, |
| 0, nullptr, |
| }; |
| |
| EXPECT_EQ(VK_SUCCESS, |
| vk->vkCreateSampler( |
| mDevice, &samplerCi, nullptr, &sampler)); |
| |
| EXPECT_EQ(VK_SUCCESS, |
| vk->vkCreateBuffer(mDevice, &bufCi, nullptr, &buffers[0])); |
| |
| EXPECT_EQ(VK_SUCCESS, |
| vk->vkCreateBuffer(mDevice, &bufCi, nullptr, &buffers[1])); |
| |
| VkDescriptorPoolSize poolSizes[] = { |
| { VK_DESCRIPTOR_TYPE_SAMPLER, 1 * maxSetsTotal, }, |
| { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 8 * maxSetsTotal, }, |
| }; |
| |
| VkDescriptorPoolCreateInfo dpCi = { |
| VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, 0, |
| VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, |
| maxSetsTotal /* maxSets */, |
| 2 /* poolSizeCount */, |
| poolSizes, |
| }; |
| |
| EXPECT_EQ(VK_SUCCESS, vk->vkCreateDescriptorPool(mDevice, &dpCi, nullptr, &pool)); |
| |
| VkDescriptorSetLayoutBinding bindings[] = { |
| { 0, VK_DESCRIPTOR_TYPE_SAMPLER, 1, VK_SHADER_STAGE_VERTEX_BIT, &sampler, }, /* immutable sampler */ |
| { 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 6, VK_SHADER_STAGE_VERTEX_BIT, nullptr, }, |
| { 10, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2, VK_SHADER_STAGE_VERTEX_BIT, nullptr, }, |
| }; |
| |
| VkDescriptorSetLayoutCreateInfo setLayoutCi = { |
| VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, 0, 0, |
| sizeof(bindings) / sizeof(VkDescriptorSetLayoutBinding), |
| bindings, |
| }; |
| |
| EXPECT_EQ(VK_SUCCESS, vk->vkCreateDescriptorSetLayout( |
| mDevice, &setLayoutCi, nullptr, &setLayout)); |
| |
| std::vector<VkDescriptorSetLayout> setLayouts(maxSetsTotal, setLayout); |
| |
| VkDescriptorSetAllocateInfo setAi = { |
| VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, 0, |
| pool, maxSetsTotal, setLayouts.data(), |
| }; |
| |
| EXPECT_EQ(VK_SUCCESS, vk->vkAllocateDescriptorSets( |
| mDevice, &setAi, sets.data())); |
| |
| EXPECT_EQ(VK_SUCCESS, vk->vkResetDescriptorPool( |
| mDevice, pool, 0)); |
| |
| EXPECT_EQ(VK_SUCCESS, vk->vkAllocateDescriptorSets( |
| mDevice, &setAi, sets.data())); |
| |
| VkPipelineLayout pipelineLayout; |
| |
| VkPipelineLayoutCreateInfo pipelineLayoutCi = { |
| VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, 0, 0, |
| 1, &setLayout, |
| 0, nullptr, |
| }; |
| vk->vkCreatePipelineLayout(mDevice, &pipelineLayoutCi, nullptr, &pipelineLayout); |
| |
| // Setup command buffers |
| VkCommandPoolCreateInfo poolCi = { |
| VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, 0, 0, mGraphicsQueueFamily, |
| }; |
| |
| VkCommandPool commandPool; |
| vk->vkCreateCommandPool(mDevice, &poolCi, nullptr, &commandPool); |
| |
| VkCommandBuffer cbs[kThreadCount]; |
| VkCommandBuffer cbs2[kThreadCount * 2]; |
| |
| VkCommandBufferAllocateInfo cbAi = { |
| VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, 0, |
| commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, kThreadCount, |
| }; |
| |
| vk->vkAllocateCommandBuffers(mDevice, &cbAi, cbs); |
| |
| VkCommandBufferAllocateInfo cbAi2 = { |
| VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, 0, |
| commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, kThreadCount * 2, |
| }; |
| |
| vk->vkAllocateCommandBuffers(mDevice, &cbAi2, cbs2); |
| |
| std::vector<FunctorThread*> threads; |
| |
| constexpr uint32_t kRecordsPerThread = 4; |
| constexpr uint32_t kRepeatSubmits = 2; |
| constexpr uint32_t kTotalRecords = kThreadCount * kRecordsPerThread * kRepeatSubmits; |
| |
| for (uint32_t i = 0; i < kThreadCount; ++i) { |
| FunctorThread* thread = new FunctorThread([this, maxSetsTotal, buffers, cbs, cbs2, sets, pipelineLayout, i]() { |
| VkCommandBufferInheritanceInfo inheritanceInfo = { |
| VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO, 0, |
| VK_NULL_HANDLE, 0, |
| VK_NULL_HANDLE, |
| VK_FALSE, |
| 0, |
| 0, |
| }; |
| |
| VkCommandBufferBeginInfo secondaryBeginInfo = { |
| VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, 0, |
| VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, &inheritanceInfo, |
| }; |
| |
| VkCommandBufferBeginInfo primaryBeginInfo = { |
| VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, 0, |
| VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, 0, |
| }; |
| |
| for (uint32_t k = 0; k < kRepeatSubmits; ++k) { |
| // Secondaries |
| { |
| VkCommandBuffer first = cbs2[2 * i]; |
| VkCommandBuffer second = cbs2[2 * i + 1]; |
| |
| vk->vkBeginCommandBuffer(first, &secondaryBeginInfo); |
| for (uint32_t j = 0; j < kRecordsPerThread / 2; ++j) { |
| for (uint32_t l = 0; l < maxSetsTotal; ++l) { |
| VkDescriptorImageInfo immutableSamplerImageInfos[] = { |
| { (VkSampler)0xdeadbeef, VK_NULL_HANDLE, VK_IMAGE_LAYOUT_GENERAL, }, |
| { (VkSampler)0xdeadbeef, VK_NULL_HANDLE, VK_IMAGE_LAYOUT_GENERAL, }, |
| }; |
| |
| VkDescriptorBufferInfo bufferInfos[] = { |
| { buffers[0], 0, 16, }, |
| { buffers[1], 16, 32, }, |
| { buffers[0], 1024, 4096, }, |
| { buffers[1], 256, 512, }, |
| { buffers[0], 0, 512, }, |
| { buffers[1], 8, 48, }, |
| }; |
| |
| VkWriteDescriptorSet descWrites[] = { |
| { |
| VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 0, sets[l], |
| 0, 0, 1, |
| VK_DESCRIPTOR_TYPE_SAMPLER, |
| immutableSamplerImageInfos, |
| bufferInfos, |
| nullptr, |
| }, |
| |
| { |
| VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 0, sets[l], |
| 1, 0, 2, |
| VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, |
| immutableSamplerImageInfos, |
| bufferInfos, |
| nullptr, |
| }, |
| |
| { |
| VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 0, sets[l], |
| 1, 4, 2, |
| VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, |
| immutableSamplerImageInfos, |
| bufferInfos, |
| nullptr, |
| }, |
| |
| { |
| VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 0, sets[l], |
| 10, 0, 2, |
| VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, |
| immutableSamplerImageInfos, |
| bufferInfos + 2, |
| nullptr, |
| }, |
| }; |
| |
| VkCopyDescriptorSet descCopies[] = { |
| { |
| VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET, 0, |
| sets[l], 10, 0, |
| sets[l], 1, 2, |
| 2, |
| }, |
| { |
| VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET, 0, |
| sets[l], 1, 0, |
| sets[l], 10, 0, |
| 2, |
| }, |
| }; |
| |
| vk->vkUpdateDescriptorSets( |
| mDevice, |
| sizeof(descWrites) / sizeof(VkWriteDescriptorSet), descWrites, |
| sizeof(descCopies) / sizeof(VkCopyDescriptorSet), descCopies); |
| } |
| vk->vkCmdBindDescriptorSets( |
| first, |
| VK_PIPELINE_BIND_POINT_GRAPHICS, |
| pipelineLayout, 0, maxSetsTotal, &sets[0], 0, nullptr); |
| } |
| vk->vkEndCommandBuffer(first); |
| |
| vk->vkBeginCommandBuffer(second, &secondaryBeginInfo); |
| for (uint32_t j = 0; j < kRecordsPerThread / 2; ++j) { |
| vk->vkCmdBindDescriptorSets( |
| second, |
| VK_PIPELINE_BIND_POINT_GRAPHICS, |
| pipelineLayout, 0, 1, &sets[i], 0, nullptr); |
| } |
| vk->vkEndCommandBuffer(second); |
| } |
| |
| vk->vkBeginCommandBuffer(cbs[i], &primaryBeginInfo); |
| vk->vkCmdExecuteCommands(cbs[i], 2, cbs2 + 2 * i); |
| vk->vkEndCommandBuffer(cbs[i]); |
| |
| VkSubmitInfo si = { |
| VK_STRUCTURE_TYPE_SUBMIT_INFO, 0, |
| 0, 0, |
| 0, |
| 1, &cbs[i], |
| 0, 0, |
| }; |
| |
| vk->vkQueueSubmit(mQueue, 1, &si, 0); |
| vk->vkQueueWaitIdle(mQueue); |
| } |
| VkPhysicalDeviceMemoryProperties memProps; |
| vk->vkGetPhysicalDeviceMemoryProperties(mPhysicalDevice, &memProps); |
| return 0; |
| }); |
| threads.push_back(thread); |
| } |
| |
| auto cpuTimeStart = System::cpuTime(); |
| |
| for (uint32_t i = 0; i < kThreadCount; ++i) { |
| threads[i]->start(); |
| } |
| |
| for (uint32_t i = 0; i < kThreadCount; ++i) { |
| threads[i]->wait(); |
| delete threads[i]; |
| } |
| |
| vk->vkQueueWaitIdle(mQueue); |
| vk->vkDeviceWaitIdle(mDevice); |
| |
| auto cpuTime = System::cpuTime() - cpuTimeStart; |
| |
| uint64_t duration_us = cpuTime.wall_time_us; |
| uint64_t duration_cpu_us = cpuTime.usageUs(); |
| |
| float ms = duration_us / 1000.0f; |
| float sec = duration_us / 1000000.0f; |
| |
| float submitHz = (float)kTotalRecords / sec; |
| |
| printf("Record %u times in %f ms. Rate: %f Hz per thread: %f Hz\n", kTotalRecords, ms, submitHz, (float)submitHz / (float)kThreadCount); |
| |
| vk->vkFreeCommandBuffers(mDevice, commandPool, kThreadCount * 2, cbs2); |
| vk->vkFreeCommandBuffers(mDevice, commandPool, kThreadCount, cbs); |
| vk->vkDestroyCommandPool(mDevice, commandPool, nullptr); |
| |
| EXPECT_EQ(VK_SUCCESS, vk->vkFreeDescriptorSets( |
| mDevice, pool, maxSetsTotal, sets.data())); |
| |
| vk->vkDestroyDescriptorPool(mDevice, pool, nullptr); |
| } |
| |
| // Flush |
| TEST_P(VulkanHalTest, Flush) { |
| constexpr uint32_t kThreadCount = 1; |
| VkDescriptorPool pool; |
| VkDescriptorSetLayout setLayout; |
| |
| std::vector<VkDescriptorSet> sets(kThreadCount); |
| |
| // Setup descriptor sets |
| EXPECT_EQ(VK_SUCCESS, allocateTestDescriptorSets( |
| kThreadCount, kThreadCount, |
| VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, |
| VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, |
| &pool, &setLayout, sets.data())); |
| |
| VkPipelineLayout pipelineLayout; |
| |
| VkPipelineLayoutCreateInfo pipelineLayoutCi = { |
| VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, 0, 0, |
| 1, &setLayout, |
| 0, nullptr, |
| }; |
| vk->vkCreatePipelineLayout(mDevice, &pipelineLayoutCi, nullptr, &pipelineLayout); |
| |
| // Setup command buffers |
| VkCommandPoolCreateInfo poolCi = { |
| VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, 0, 0, mGraphicsQueueFamily, |
| }; |
| |
| VkCommandPool commandPool; |
| vk->vkCreateCommandPool(mDevice, &poolCi, nullptr, &commandPool); |
| |
| VkCommandBuffer cbs[kThreadCount]; |
| VkCommandBuffer cbs2[kThreadCount * 2]; |
| |
| VkCommandBufferAllocateInfo cbAi = { |
| VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, 0, |
| commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, kThreadCount, |
| }; |
| |
| vk->vkAllocateCommandBuffers(mDevice, &cbAi, cbs); |
| |
| VkCommandBufferAllocateInfo cbAi2 = { |
| VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, 0, |
| commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, kThreadCount * 2, |
| }; |
| |
| vk->vkAllocateCommandBuffers(mDevice, &cbAi2, cbs2); |
| |
| std::vector<FunctorThread*> threads; |
| |
| constexpr uint32_t kRecordsPerThread = 800000; |
| constexpr uint32_t kTotalRecords = kThreadCount * kRecordsPerThread; |
| |
| for (uint32_t i = 0; i < kThreadCount; ++i) { |
| FunctorThread* thread = new FunctorThread([this, cbs, cbs2, sets, pipelineLayout, i]() { |
| for (uint32_t k = 0; k < kRecordsPerThread; ++k) { |
| VkSubmitInfo si = { |
| VK_STRUCTURE_TYPE_SUBMIT_INFO, 0, |
| 0, 0, |
| 0, |
| 0, nullptr, |
| 0, 0, |
| }; |
| |
| vk->vkQueueSubmit(mQueue, 1, &si, 0); |
| } |
| VkPhysicalDeviceMemoryProperties memProps; |
| vk->vkGetPhysicalDeviceMemoryProperties(mPhysicalDevice, &memProps); |
| return 0; |
| }); |
| threads.push_back(thread); |
| } |
| |
| auto cpuTimeStart = System::cpuTime(); |
| |
| for (uint32_t i = 0; i < kThreadCount; ++i) { |
| threads[i]->start(); |
| } |
| |
| for (uint32_t i = 0; i < kThreadCount; ++i) { |
| threads[i]->wait(); |
| delete threads[i]; |
| } |
| |
| vk->vkQueueWaitIdle(mQueue); |
| vk->vkDeviceWaitIdle(mDevice); |
| |
| auto cpuTime = System::cpuTime() - cpuTimeStart; |
| |
| uint64_t duration_us = cpuTime.wall_time_us; |
| uint64_t duration_cpu_us = cpuTime.usageUs(); |
| |
| float ms = duration_us / 1000.0f; |
| float sec = duration_us / 1000000.0f; |
| |
| float submitHz = (float)kTotalRecords / sec; |
| |
| printf("Record %u times in %f ms. Rate: %f Hz per thread: %f Hz\n", kTotalRecords, ms, submitHz, (float)submitHz / (float)kThreadCount); |
| |
| vk->vkFreeCommandBuffers(mDevice, commandPool, kThreadCount * 2, cbs2); |
| vk->vkFreeCommandBuffers(mDevice, commandPool, kThreadCount, cbs); |
| vk->vkDestroyCommandPool(mDevice, commandPool, nullptr); |
| |
| EXPECT_EQ(VK_SUCCESS, vk->vkFreeDescriptorSets( |
| mDevice, pool, kThreadCount, sets.data())); |
| |
| vk->vkDestroyDescriptorPool(mDevice, pool, nullptr); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| MultipleTransports, |
| VulkanHalTest, |
| testing::ValuesIn(GoldfishOpenglTestEnv::getTransportsToTest())); |
| |
| } // namespace aemu |