1 /*
2 * Copyright 2018 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "src/gpu/ganesh/vk/GrVkCommandPool.h"
9
10 #include "include/private/base/SkAssert.h"
11 #include "src/core/SkTraceEvent.h"
12 #include "src/gpu/ganesh/vk/GrVkCaps.h"
13 #include "src/gpu/ganesh/vk/GrVkCommandBuffer.h"
14 #include "src/gpu/ganesh/vk/GrVkGpu.h"
15 #include "src/gpu/ganesh/vk/GrVkUtil.h"
16
17 #include <utility>
18
Create(GrVkGpu * gpu)19 GrVkCommandPool* GrVkCommandPool::Create(GrVkGpu* gpu) {
20 VkCommandPoolCreateFlags cmdPoolCreateFlags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
21 if (gpu->protectedContext()) {
22 cmdPoolCreateFlags |= VK_COMMAND_POOL_CREATE_PROTECTED_BIT;
23 }
24
25 const VkCommandPoolCreateInfo cmdPoolInfo = {
26 VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, // sType
27 nullptr, // pNext
28 cmdPoolCreateFlags, // CmdPoolCreateFlags
29 gpu->queueIndex(), // queueFamilyIndex
30 };
31 VkResult result;
32 VkCommandPool pool;
33 GR_VK_CALL_RESULT(gpu, result, CreateCommandPool(gpu->device(), &cmdPoolInfo, nullptr, &pool));
34 if (result != VK_SUCCESS) {
35 return nullptr;
36 }
37
38 GrVkPrimaryCommandBuffer* primaryCmdBuffer = GrVkPrimaryCommandBuffer::Create(gpu, pool);
39 if (!primaryCmdBuffer) {
40 GR_VK_CALL(gpu->vkInterface(), DestroyCommandPool(gpu->device(), pool, nullptr));
41 return nullptr;
42 }
43
44 return new GrVkCommandPool(gpu, pool, primaryCmdBuffer);
45 }
46
GrVkCommandPool(GrVkGpu * gpu,VkCommandPool commandPool,GrVkPrimaryCommandBuffer * primaryCmdBuffer)47 GrVkCommandPool::GrVkCommandPool(GrVkGpu* gpu, VkCommandPool commandPool,
48 GrVkPrimaryCommandBuffer* primaryCmdBuffer)
49 : GrVkManagedResource(gpu)
50 , fCommandPool(commandPool)
51 , fPrimaryCommandBuffer(primaryCmdBuffer)
52 , fMaxCachedSecondaryCommandBuffers(
53 gpu->vkCaps().maxPerPoolCachedSecondaryCommandBuffers()) {
54 }
55
findOrCreateSecondaryCommandBuffer(GrVkGpu * gpu)56 std::unique_ptr<GrVkSecondaryCommandBuffer> GrVkCommandPool::findOrCreateSecondaryCommandBuffer(
57 GrVkGpu* gpu) {
58 std::unique_ptr<GrVkSecondaryCommandBuffer> result;
59 if (!fAvailableSecondaryBuffers.empty()) {
60 result = std::move(fAvailableSecondaryBuffers.back());
61 fAvailableSecondaryBuffers.pop_back();
62 } else{
63 result.reset(GrVkSecondaryCommandBuffer::Create(gpu, this));
64 }
65 return result;
66 }
67
recycleSecondaryCommandBuffer(GrVkSecondaryCommandBuffer * buffer)68 void GrVkCommandPool::recycleSecondaryCommandBuffer(GrVkSecondaryCommandBuffer* buffer) {
69 std::unique_ptr<GrVkSecondaryCommandBuffer> scb(buffer);
70 if (fAvailableSecondaryBuffers.size() < fMaxCachedSecondaryCommandBuffers) {
71 fAvailableSecondaryBuffers.push_back(std::move(scb));
72 } else {
73 VkCommandBuffer vkBuffer = buffer->vkCommandBuffer();
74 GR_VK_CALL(fGpu->vkInterface(),
75 FreeCommandBuffers(fGpu->device(), fCommandPool, 1, &vkBuffer));
76 }
77 }
78
close()79 void GrVkCommandPool::close() {
80 fOpen = false;
81 }
82
reset(GrVkGpu * gpu)83 void GrVkCommandPool::reset(GrVkGpu* gpu) {
84 TRACE_EVENT0("skia.gpu", TRACE_FUNC);
85 SkASSERT(!fOpen);
86 // We can't use the normal result macro calls here because we may call reset on a different
87 // thread and we can't be modifying the lost state on the GrVkGpu. We just call
88 // vkResetCommandPool and assume the "next" vulkan call will catch the lost device.
89 SkDEBUGCODE(VkResult result = )GR_VK_CALL(gpu->vkInterface(),
90 ResetCommandPool(gpu->device(), fCommandPool, 0));
91 SkASSERT(result == VK_SUCCESS || result == VK_ERROR_DEVICE_LOST);
92
93 // It should be safe to release the resources before actually resetting the VkCommandPool.
94 // However, on qualcomm devices running R drivers there was a few months period where the driver
95 // had a bug which it incorrectly was accessing objects on the command buffer while it was being
96 // reset. If these objects were already destroyed (which is a valid thing to do) it would crash.
97 // So to be safe we do the reset first since it doesn't really matter when single threaded. If
98 // we ever add back in threaded resets we'll want to add checks to make sure everything happens
99 // in the right order (and probably do single threaded resets on bad devices).
100 this->releaseResources();
101
102 fOpen = true;
103 }
104
releaseResources()105 void GrVkCommandPool::releaseResources() {
106 TRACE_EVENT0("skia.gpu", TRACE_FUNC);
107 SkASSERT(!fOpen);
108 fPrimaryCommandBuffer->releaseResources();
109 fPrimaryCommandBuffer->recycleSecondaryCommandBuffers(this);
110 }
111
freeGPUData() const112 void GrVkCommandPool::freeGPUData() const {
113 // TODO: having freeGPUData virtual on GrManagedResource be const seems like a bad restriction since
114 // we are changing the internal objects of these classes when it is called. We should go back a
115 // revisit how much of a headache it would be to make this function non-const
116 GrVkCommandPool* nonConstThis = const_cast<GrVkCommandPool*>(this);
117 nonConstThis->close();
118 nonConstThis->releaseResources();
119 fPrimaryCommandBuffer->freeGPUData(fGpu, fCommandPool);
120 for (const auto& buffer : fAvailableSecondaryBuffers) {
121 buffer->freeGPUData(fGpu, fCommandPool);
122 }
123 if (fCommandPool != VK_NULL_HANDLE) {
124 GR_VK_CALL(fGpu->vkInterface(),
125 DestroyCommandPool(fGpu->device(), fCommandPool, nullptr));
126 }
127 }
128