| /* |
| * Copyright 2016 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "tools/gpu/vk/VkTestContext.h" |
| |
| #ifdef SK_VULKAN |
| |
| #include "include/gpu/GrContext.h" |
| #include "include/gpu/vk/GrVkExtensions.h" |
| #include "tools/gpu/vk/VkTestUtils.h" |
| |
| namespace { |
| |
| #define ACQUIRE_VK_PROC(name, device) \ |
| f##name = reinterpret_cast<PFN_vk##name>(getProc("vk" #name, nullptr, device)); \ |
| SkASSERT(f##name) |
| |
| /** |
| * Implements sk_gpu_test::FenceSync for Vulkan. It creates a single command |
| * buffer with USAGE_SIMULTANEOUS with no content . On every insertFence request |
| * it submits the command buffer with a new fence. |
| */ |
| class VkFenceSync : public sk_gpu_test::FenceSync { |
| public: |
| VkFenceSync(GrVkGetProc getProc, VkDevice device, VkQueue queue, |
| uint32_t queueFamilyIndex) |
| : fDevice(device) |
| , fQueue(queue) { |
| ACQUIRE_VK_PROC(CreateCommandPool, device); |
| ACQUIRE_VK_PROC(DestroyCommandPool, device); |
| ACQUIRE_VK_PROC(AllocateCommandBuffers, device); |
| ACQUIRE_VK_PROC(FreeCommandBuffers, device); |
| ACQUIRE_VK_PROC(BeginCommandBuffer, device); |
| ACQUIRE_VK_PROC(EndCommandBuffer, device); |
| ACQUIRE_VK_PROC(CreateFence, device); |
| ACQUIRE_VK_PROC(DestroyFence, device); |
| ACQUIRE_VK_PROC(WaitForFences, device); |
| ACQUIRE_VK_PROC(QueueSubmit, device); |
| |
| VkResult result; |
| SkDEBUGCODE(fUnfinishedSyncs = 0;) |
| VkCommandPoolCreateInfo createInfo; |
| createInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; |
| createInfo.pNext = nullptr; |
| createInfo.flags = 0; |
| createInfo.queueFamilyIndex = queueFamilyIndex; |
| result = fCreateCommandPool(fDevice, &createInfo, nullptr, &fCommandPool); |
| SkASSERT(VK_SUCCESS == result); |
| |
| VkCommandBufferAllocateInfo allocateInfo; |
| allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; |
| allocateInfo.pNext = nullptr; |
| allocateInfo.commandBufferCount = 1; |
| allocateInfo.commandPool = fCommandPool; |
| allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; |
| result = fAllocateCommandBuffers(fDevice, &allocateInfo, &fCommandBuffer); |
| SkASSERT(VK_SUCCESS == result); |
| |
| VkCommandBufferBeginInfo beginInfo; |
| beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; |
| beginInfo.pNext = nullptr; |
| beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT; |
| beginInfo.pInheritanceInfo = nullptr; |
| result = fBeginCommandBuffer(fCommandBuffer, &beginInfo); |
| SkASSERT(VK_SUCCESS == result); |
| result = fEndCommandBuffer(fCommandBuffer); |
| SkASSERT(VK_SUCCESS == result); |
| |
| } |
| |
| ~VkFenceSync() override { |
| SkASSERT(!fUnfinishedSyncs); |
| // If the above assertion is true then the command buffer should not be in flight. |
| fFreeCommandBuffers(fDevice, fCommandPool, 1, &fCommandBuffer); |
| fDestroyCommandPool(fDevice, fCommandPool, nullptr); |
| } |
| |
| sk_gpu_test::PlatformFence SK_WARN_UNUSED_RESULT insertFence() const override { |
| VkResult result; |
| |
| VkFence fence; |
| VkFenceCreateInfo info; |
| info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; |
| info.pNext = nullptr; |
| info.flags = 0; |
| result = fCreateFence(fDevice, &info, nullptr, &fence); |
| SkASSERT(VK_SUCCESS == result); |
| |
| VkSubmitInfo submitInfo; |
| submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; |
| submitInfo.pNext = nullptr; |
| submitInfo.waitSemaphoreCount = 0; |
| submitInfo.pWaitSemaphores = nullptr; |
| submitInfo.pWaitDstStageMask = nullptr; |
| submitInfo.commandBufferCount = 1; |
| submitInfo.pCommandBuffers = &fCommandBuffer; |
| submitInfo.signalSemaphoreCount = 0; |
| submitInfo.pSignalSemaphores = nullptr; |
| result = fQueueSubmit(fQueue, 1, &submitInfo, fence); |
| SkASSERT(VK_SUCCESS == result); |
| |
| SkDEBUGCODE(++fUnfinishedSyncs;) |
| return (sk_gpu_test::PlatformFence)fence; |
| } |
| |
| bool waitFence(sk_gpu_test::PlatformFence opaqueFence) const override { |
| VkFence fence = (VkFence)opaqueFence; |
| static constexpr uint64_t kForever = ~((uint64_t)0); |
| auto result = fWaitForFences(fDevice, 1, &fence, true, kForever); |
| return result != VK_TIMEOUT; |
| } |
| |
| void deleteFence(sk_gpu_test::PlatformFence opaqueFence) const override { |
| VkFence fence = (VkFence)opaqueFence; |
| fDestroyFence(fDevice, fence, nullptr); |
| SkDEBUGCODE(--fUnfinishedSyncs;) |
| } |
| |
| private: |
| VkDevice fDevice; |
| VkQueue fQueue; |
| VkCommandPool fCommandPool; |
| VkCommandBuffer fCommandBuffer; |
| |
| PFN_vkCreateCommandPool fCreateCommandPool = nullptr; |
| PFN_vkDestroyCommandPool fDestroyCommandPool = nullptr; |
| PFN_vkAllocateCommandBuffers fAllocateCommandBuffers = nullptr; |
| PFN_vkFreeCommandBuffers fFreeCommandBuffers = nullptr; |
| PFN_vkBeginCommandBuffer fBeginCommandBuffer = nullptr; |
| PFN_vkEndCommandBuffer fEndCommandBuffer = nullptr; |
| PFN_vkCreateFence fCreateFence = nullptr; |
| PFN_vkDestroyFence fDestroyFence = nullptr; |
| PFN_vkWaitForFences fWaitForFences = nullptr; |
| PFN_vkQueueSubmit fQueueSubmit = nullptr; |
| |
| SkDEBUGCODE(mutable int fUnfinishedSyncs;) |
| typedef sk_gpu_test::FenceSync INHERITED; |
| }; |
| |
| GR_STATIC_ASSERT(sizeof(VkFence) <= sizeof(sk_gpu_test::PlatformFence)); |
| |
| // TODO: Implement swap buffers and finish |
| class VkTestContextImpl : public sk_gpu_test::VkTestContext { |
| public: |
| static VkTestContext* Create(VkTestContext* sharedContext) { |
| GrVkBackendContext backendContext; |
| GrVkExtensions* extensions; |
| VkPhysicalDeviceFeatures2* features; |
| bool ownsContext = true; |
| VkDebugReportCallbackEXT debugCallback = VK_NULL_HANDLE; |
| PFN_vkDestroyDebugReportCallbackEXT destroyCallback = nullptr; |
| if (sharedContext) { |
| backendContext = sharedContext->getVkBackendContext(); |
| extensions = const_cast<GrVkExtensions*>(sharedContext->getVkExtensions()); |
| features = const_cast<VkPhysicalDeviceFeatures2*>(sharedContext->getVkFeatures()); |
| // We always delete the parent context last so make sure the child does not think they |
| // own the vulkan context. |
| ownsContext = false; |
| } else { |
| PFN_vkGetInstanceProcAddr instProc; |
| PFN_vkGetDeviceProcAddr devProc; |
| if (!sk_gpu_test::LoadVkLibraryAndGetProcAddrFuncs(&instProc, &devProc)) { |
| return nullptr; |
| } |
| auto getProc = [instProc, devProc](const char* proc_name, |
| VkInstance instance, VkDevice device) { |
| if (device != VK_NULL_HANDLE) { |
| return devProc(device, proc_name); |
| } |
| return instProc(instance, proc_name); |
| }; |
| extensions = new GrVkExtensions(); |
| features = new VkPhysicalDeviceFeatures2; |
| memset(features, 0, sizeof(VkPhysicalDeviceFeatures2)); |
| if (!sk_gpu_test::CreateVkBackendContext(getProc, &backendContext, extensions, |
| features, &debugCallback)) { |
| sk_gpu_test::FreeVulkanFeaturesStructs(features); |
| delete features; |
| delete extensions; |
| return nullptr; |
| } |
| if (debugCallback != VK_NULL_HANDLE) { |
| destroyCallback = (PFN_vkDestroyDebugReportCallbackEXT) instProc( |
| backendContext.fInstance, "vkDestroyDebugReportCallbackEXT"); |
| } |
| } |
| return new VkTestContextImpl(backendContext, extensions, features, ownsContext, |
| debugCallback, destroyCallback); |
| } |
| |
| ~VkTestContextImpl() override { this->teardown(); } |
| |
| void testAbandon() override {} |
| |
| // There is really nothing to here since we don't own any unqueued command buffers here. |
| void submit() override {} |
| |
| void finish() override {} |
| |
| sk_sp<GrContext> makeGrContext(const GrContextOptions& options) override { |
| return GrContext::MakeVulkan(fVk, options); |
| } |
| |
| protected: |
| #define ACQUIRE_VK_PROC_LOCAL(name, inst) \ |
| PFN_vk##name grVk##name = \ |
| reinterpret_cast<PFN_vk##name>(fVk.fGetProc("vk" #name, inst, nullptr)); \ |
| do { \ |
| if (grVk##name == nullptr) { \ |
| SkDebugf("Function ptr for vk%s could not be acquired\n", #name); \ |
| return; \ |
| } \ |
| } while (0) |
| |
| void teardown() override { |
| INHERITED::teardown(); |
| fVk.fMemoryAllocator.reset(); |
| if (fOwnsContext) { |
| ACQUIRE_VK_PROC_LOCAL(DeviceWaitIdle, fVk.fInstance); |
| ACQUIRE_VK_PROC_LOCAL(DestroyDevice, fVk.fInstance); |
| ACQUIRE_VK_PROC_LOCAL(DestroyInstance, fVk.fInstance); |
| grVkDeviceWaitIdle(fVk.fDevice); |
| grVkDestroyDevice(fVk.fDevice, nullptr); |
| #ifdef SK_ENABLE_VK_LAYERS |
| if (fDebugCallback != VK_NULL_HANDLE) { |
| fDestroyDebugReportCallbackEXT(fVk.fInstance, fDebugCallback, nullptr); |
| } |
| #endif |
| grVkDestroyInstance(fVk.fInstance, nullptr); |
| delete fExtensions; |
| |
| sk_gpu_test::FreeVulkanFeaturesStructs(fFeatures); |
| delete fFeatures; |
| } |
| } |
| |
| private: |
| VkTestContextImpl(const GrVkBackendContext& backendContext, const GrVkExtensions* extensions, |
| VkPhysicalDeviceFeatures2* features, bool ownsContext, |
| VkDebugReportCallbackEXT debugCallback, |
| PFN_vkDestroyDebugReportCallbackEXT destroyCallback) |
| : VkTestContext(backendContext, extensions, features, ownsContext, debugCallback, |
| destroyCallback) { |
| fFenceSync.reset(new VkFenceSync(fVk.fGetProc, fVk.fDevice, fVk.fQueue, |
| fVk.fGraphicsQueueIndex)); |
| } |
| |
| void onPlatformMakeCurrent() const override {} |
| std::function<void()> onPlatformGetAutoContextRestore() const override { return nullptr; } |
| void onPlatformSwapBuffers() const override {} |
| |
| typedef sk_gpu_test::VkTestContext INHERITED; |
| }; |
| } // anonymous namespace |
| |
| namespace sk_gpu_test { |
| VkTestContext* CreatePlatformVkTestContext(VkTestContext* sharedContext) { |
| return VkTestContextImpl::Create(sharedContext); |
| } |
| } // namespace sk_gpu_test |
| |
| #endif |