/* * Copyright 2018 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "src/gpu/ganesh/vk/GrVkCommandPool.h" #include "include/private/base/SkAssert.h" #include "src/core/SkTraceEvent.h" #include "src/gpu/ganesh/vk/GrVkCaps.h" #include "src/gpu/ganesh/vk/GrVkCommandBuffer.h" #include "src/gpu/ganesh/vk/GrVkGpu.h" #include "src/gpu/ganesh/vk/GrVkUtil.h" #include GrVkCommandPool* GrVkCommandPool::Create(GrVkGpu* gpu) { VkCommandPoolCreateFlags cmdPoolCreateFlags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT; if (gpu->protectedContext()) { cmdPoolCreateFlags |= VK_COMMAND_POOL_CREATE_PROTECTED_BIT; } const VkCommandPoolCreateInfo cmdPoolInfo = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, // sType nullptr, // pNext cmdPoolCreateFlags, // CmdPoolCreateFlags gpu->queueIndex(), // queueFamilyIndex }; VkResult result; VkCommandPool pool; GR_VK_CALL_RESULT(gpu, result, CreateCommandPool(gpu->device(), &cmdPoolInfo, nullptr, &pool)); if (result != VK_SUCCESS) { return nullptr; } GrVkPrimaryCommandBuffer* primaryCmdBuffer = GrVkPrimaryCommandBuffer::Create(gpu, pool); if (!primaryCmdBuffer) { GR_VK_CALL(gpu->vkInterface(), DestroyCommandPool(gpu->device(), pool, nullptr)); return nullptr; } return new GrVkCommandPool(gpu, pool, primaryCmdBuffer); } GrVkCommandPool::GrVkCommandPool(GrVkGpu* gpu, VkCommandPool commandPool, GrVkPrimaryCommandBuffer* primaryCmdBuffer) : GrVkManagedResource(gpu) , fCommandPool(commandPool) , fPrimaryCommandBuffer(primaryCmdBuffer) , fMaxCachedSecondaryCommandBuffers( gpu->vkCaps().maxPerPoolCachedSecondaryCommandBuffers()) { } std::unique_ptr GrVkCommandPool::findOrCreateSecondaryCommandBuffer( GrVkGpu* gpu) { std::unique_ptr result; if (!fAvailableSecondaryBuffers.empty()) { result = std::move(fAvailableSecondaryBuffers.back()); fAvailableSecondaryBuffers.pop_back(); } else{ result.reset(GrVkSecondaryCommandBuffer::Create(gpu, this)); } return result; } void GrVkCommandPool::recycleSecondaryCommandBuffer(GrVkSecondaryCommandBuffer* buffer) { std::unique_ptr scb(buffer); if (fAvailableSecondaryBuffers.size() < fMaxCachedSecondaryCommandBuffers) { fAvailableSecondaryBuffers.push_back(std::move(scb)); } else { VkCommandBuffer vkBuffer = buffer->vkCommandBuffer(); GR_VK_CALL(fGpu->vkInterface(), FreeCommandBuffers(fGpu->device(), fCommandPool, 1, &vkBuffer)); } } void GrVkCommandPool::close() { fOpen = false; } void GrVkCommandPool::reset(GrVkGpu* gpu) { TRACE_EVENT0("skia.gpu", TRACE_FUNC); SkASSERT(!fOpen); // We can't use the normal result macro calls here because we may call reset on a different // thread and we can't be modifying the lost state on the GrVkGpu. We just call // vkResetCommandPool and assume the "next" vulkan call will catch the lost device. SkDEBUGCODE(VkResult result = )GR_VK_CALL(gpu->vkInterface(), ResetCommandPool(gpu->device(), fCommandPool, 0)); SkASSERT(result == VK_SUCCESS || result == VK_ERROR_DEVICE_LOST); // It should be safe to release the resources before actually resetting the VkCommandPool. // However, on qualcomm devices running R drivers there was a few months period where the driver // had a bug which it incorrectly was accessing objects on the command buffer while it was being // reset. If these objects were already destroyed (which is a valid thing to do) it would crash. // So to be safe we do the reset first since it doesn't really matter when single threaded. If // we ever add back in threaded resets we'll want to add checks to make sure everything happens // in the right order (and probably do single threaded resets on bad devices). this->releaseResources(); fOpen = true; } void GrVkCommandPool::releaseResources() { TRACE_EVENT0("skia.gpu", TRACE_FUNC); SkASSERT(!fOpen); fPrimaryCommandBuffer->releaseResources(); fPrimaryCommandBuffer->recycleSecondaryCommandBuffers(this); } void GrVkCommandPool::freeGPUData() const { // TODO: having freeGPUData virtual on GrManagedResource be const seems like a bad restriction since // we are changing the internal objects of these classes when it is called. We should go back a // revisit how much of a headache it would be to make this function non-const GrVkCommandPool* nonConstThis = const_cast(this); nonConstThis->close(); nonConstThis->releaseResources(); fPrimaryCommandBuffer->freeGPUData(fGpu, fCommandPool); for (const auto& buffer : fAvailableSecondaryBuffers) { buffer->freeGPUData(fGpu, fCommandPool); } if (fCommandPool != VK_NULL_HANDLE) { GR_VK_CALL(fGpu->vkInterface(), DestroyCommandPool(fGpu->device(), fCommandPool, nullptr)); } }