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/vk/GrVkCommandPool.h"
9
10#include "src/core/SkTraceEvent.h"
11#include "src/gpu/GrDirectContextPriv.h"
12#include "src/gpu/vk/GrVkCommandBuffer.h"
13#include "src/gpu/vk/GrVkGpu.h"
14
15GrVkCommandPool* GrVkCommandPool::Create(GrVkGpu* gpu) {
16    VkCommandPoolCreateFlags cmdPoolCreateFlags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
17    if (gpu->protectedContext()) {
18        cmdPoolCreateFlags |= VK_COMMAND_POOL_CREATE_PROTECTED_BIT;
19    }
20
21    const VkCommandPoolCreateInfo cmdPoolInfo = {
22        VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,  // sType
23        nullptr,                                     // pNext
24        cmdPoolCreateFlags,                          // CmdPoolCreateFlags
25        gpu->queueIndex(),                           // queueFamilyIndex
26    };
27    VkResult result;
28    VkCommandPool pool;
29    GR_VK_CALL_RESULT(gpu, result, CreateCommandPool(gpu->device(), &cmdPoolInfo, nullptr, &pool));
30    if (result != VK_SUCCESS) {
31        return nullptr;
32    }
33
34    GrVkPrimaryCommandBuffer* primaryCmdBuffer = GrVkPrimaryCommandBuffer::Create(gpu, pool);
35    if (!primaryCmdBuffer) {
36        GR_VK_CALL(gpu->vkInterface(), DestroyCommandPool(gpu->device(), pool, nullptr));
37        return nullptr;
38    }
39
40    return new GrVkCommandPool(gpu, pool, primaryCmdBuffer);
41}
42
43GrVkCommandPool::GrVkCommandPool(GrVkGpu* gpu, VkCommandPool commandPool,
44                                 GrVkPrimaryCommandBuffer* primaryCmdBuffer)
45        : GrVkManagedResource(gpu)
46        , fCommandPool(commandPool)
47        , fPrimaryCommandBuffer(primaryCmdBuffer)
48        , fMaxCachedSecondaryCommandBuffers(
49                gpu->vkCaps().maxPerPoolCachedSecondaryCommandBuffers()) {
50}
51
52std::unique_ptr<GrVkSecondaryCommandBuffer> GrVkCommandPool::findOrCreateSecondaryCommandBuffer(
53        GrVkGpu* gpu) {
54    std::unique_ptr<GrVkSecondaryCommandBuffer> result;
55    if (fAvailableSecondaryBuffers.count()) {
56        result = std::move(fAvailableSecondaryBuffers.back());
57        fAvailableSecondaryBuffers.pop_back();
58    } else{
59        result.reset(GrVkSecondaryCommandBuffer::Create(gpu, this));
60    }
61    return result;
62}
63
64void GrVkCommandPool::recycleSecondaryCommandBuffer(GrVkSecondaryCommandBuffer* buffer) {
65    std::unique_ptr<GrVkSecondaryCommandBuffer> scb(buffer);
66    if (fAvailableSecondaryBuffers.count() < fMaxCachedSecondaryCommandBuffers) {
67        fAvailableSecondaryBuffers.push_back(std::move(scb));
68    } else {
69        VkCommandBuffer vkBuffer = buffer->vkCommandBuffer();
70        GR_VK_CALL(fGpu->vkInterface(),
71                   FreeCommandBuffers(fGpu->device(), fCommandPool, 1, &vkBuffer));
72    }
73}
74
75void GrVkCommandPool::close() {
76    fOpen = false;
77}
78
79void GrVkCommandPool::reset(GrVkGpu* gpu) {
80    SkASSERT(!fOpen);
81    fOpen = true;
82    // We can't use the normal result macro calls here because we may call reset on a different
83    // thread and we can't be modifying the lost state on the GrVkGpu. We just call
84    // vkResetCommandPool and assume the "next" vulkan call will catch the lost device.
85    SkDEBUGCODE(VkResult result = )GR_VK_CALL(gpu->vkInterface(),
86                                              ResetCommandPool(gpu->device(), fCommandPool, 0));
87    SkASSERT(result == VK_SUCCESS || result == VK_ERROR_DEVICE_LOST);
88}
89
90void GrVkCommandPool::releaseResources() {
91    TRACE_EVENT0("skia.gpu", TRACE_FUNC);
92    SkASSERT(!fOpen);
93    fPrimaryCommandBuffer->releaseResources();
94    fPrimaryCommandBuffer->recycleSecondaryCommandBuffers(this);
95}
96
97void GrVkCommandPool::freeGPUData() const {
98    // TODO: having freeGPUData virtual on GrManagedResource be const seems like a bad restriction since
99    // we are changing the internal objects of these classes when it is called. We should go back a
100    // revisit how much of a headache it would be to make this function non-const
101    GrVkCommandPool* nonConstThis = const_cast<GrVkCommandPool*>(this);
102    nonConstThis->close();
103    nonConstThis->releaseResources();
104    fPrimaryCommandBuffer->freeGPUData(fGpu, fCommandPool);
105    for (const auto& buffer : fAvailableSecondaryBuffers) {
106        buffer->freeGPUData(fGpu, fCommandPool);
107    }
108    if (fCommandPool != VK_NULL_HANDLE) {
109        GR_VK_CALL(fGpu->vkInterface(),
110                   DestroyCommandPool(fGpu->device(), fCommandPool, nullptr));
111    }
112}
113