Coverage Report

Created: 2024-05-20 07:14

/src/skia/tools/gpu/vk/VkTestMemoryAllocator.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2024 Google LLC
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 "tools/gpu/vk/VkTestMemoryAllocator.h"
9
10
#include "include/gpu/vk/VulkanExtensions.h"
11
#include "src/core/SkTraceEvent.h"
12
#include "src/gpu/vk/VulkanInterface.h"
13
14
namespace sk_gpu_test {
15
16
sk_sp<skgpu::VulkanMemoryAllocator> VkTestMemoryAllocator::Make(
17
        VkInstance instance,
18
        VkPhysicalDevice physicalDevice,
19
        VkDevice device,
20
        uint32_t physicalDeviceVersion,
21
        const skgpu::VulkanExtensions* extensions,
22
0
        const skgpu::VulkanInterface* interface) {
23
0
#define SKGPU_COPY_FUNCTION(NAME) functions.vk##NAME = interface->fFunctions.f##NAME
24
0
#define SKGPU_COPY_FUNCTION_KHR(NAME) functions.vk##NAME##KHR = interface->fFunctions.f##NAME
25
26
0
    VmaVulkanFunctions functions;
27
    // We should be setting all the required functions (at least through vulkan 1.1), but this is
28
    // just extra belt and suspenders to make sure there isn't unitialized values here.
29
0
    memset(&functions, 0, sizeof(VmaVulkanFunctions));
30
31
    // We don't use dynamic function getting in the allocator so we set the getProc functions to
32
    // null.
33
0
    functions.vkGetInstanceProcAddr = nullptr;
34
0
    functions.vkGetDeviceProcAddr = nullptr;
35
0
    SKGPU_COPY_FUNCTION(GetPhysicalDeviceProperties);
36
0
    SKGPU_COPY_FUNCTION(GetPhysicalDeviceMemoryProperties);
37
0
    SKGPU_COPY_FUNCTION(AllocateMemory);
38
0
    SKGPU_COPY_FUNCTION(FreeMemory);
39
0
    SKGPU_COPY_FUNCTION(MapMemory);
40
0
    SKGPU_COPY_FUNCTION(UnmapMemory);
41
0
    SKGPU_COPY_FUNCTION(FlushMappedMemoryRanges);
42
0
    SKGPU_COPY_FUNCTION(InvalidateMappedMemoryRanges);
43
0
    SKGPU_COPY_FUNCTION(BindBufferMemory);
44
0
    SKGPU_COPY_FUNCTION(BindImageMemory);
45
0
    SKGPU_COPY_FUNCTION(GetBufferMemoryRequirements);
46
0
    SKGPU_COPY_FUNCTION(GetImageMemoryRequirements);
47
0
    SKGPU_COPY_FUNCTION(CreateBuffer);
48
0
    SKGPU_COPY_FUNCTION(DestroyBuffer);
49
0
    SKGPU_COPY_FUNCTION(CreateImage);
50
0
    SKGPU_COPY_FUNCTION(DestroyImage);
51
0
    SKGPU_COPY_FUNCTION(CmdCopyBuffer);
52
0
    SKGPU_COPY_FUNCTION_KHR(GetBufferMemoryRequirements2);
53
0
    SKGPU_COPY_FUNCTION_KHR(GetImageMemoryRequirements2);
54
0
    SKGPU_COPY_FUNCTION_KHR(BindBufferMemory2);
55
0
    SKGPU_COPY_FUNCTION_KHR(BindImageMemory2);
56
0
    SKGPU_COPY_FUNCTION_KHR(GetPhysicalDeviceMemoryProperties2);
57
58
0
    VmaAllocatorCreateInfo info;
59
0
    info.flags = VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT;
60
0
    if (physicalDeviceVersion >= VK_MAKE_VERSION(1, 1, 0) ||
61
0
        (extensions->hasExtension(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME, 1) &&
62
0
         extensions->hasExtension(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, 1))) {
63
0
        info.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT;
64
0
    }
65
66
0
    info.physicalDevice = physicalDevice;
67
0
    info.device = device;
68
    // 4MB was picked for the size here by looking at memory usage of Android apps and runs of DM.
69
    // It seems to be a good compromise of not wasting unused allocated space and not making too
70
    // many small allocations. The AMD allocator will start making blocks at 1/8 the max size and
71
    // builds up block size as needed before capping at the max set here.
72
0
    info.preferredLargeHeapBlockSize = 4 * 1024 * 1024;
73
0
    info.pAllocationCallbacks = nullptr;
74
0
    info.pDeviceMemoryCallbacks = nullptr;
75
0
    info.pHeapSizeLimit = nullptr;
76
0
    info.pVulkanFunctions = &functions;
77
0
    info.instance = instance;
78
    // TODO: Update our interface and headers to support vulkan 1.3 and add in the new required
79
    // functions for 1.3 that the allocator needs. Until then we just clamp the version to 1.1.
80
0
    info.vulkanApiVersion = std::min(physicalDeviceVersion, VK_MAKE_VERSION(1, 1, 0));
81
0
    info.pTypeExternalMemoryHandleTypes = nullptr;
82
83
0
    VmaAllocator allocator;
84
0
    vmaCreateAllocator(&info, &allocator);
85
86
0
    return sk_sp<VkTestMemoryAllocator>(new VkTestMemoryAllocator(allocator));
87
0
}
88
89
0
VkTestMemoryAllocator::VkTestMemoryAllocator(VmaAllocator allocator) : fAllocator(allocator) {}
90
91
0
VkTestMemoryAllocator::~VkTestMemoryAllocator() {
92
0
    vmaDestroyAllocator(fAllocator);
93
0
    fAllocator = VK_NULL_HANDLE;
94
0
}
95
96
VkResult VkTestMemoryAllocator::allocateImageMemory(VkImage image,
97
                                                    uint32_t allocationPropertyFlags,
98
0
                                                    skgpu::VulkanBackendMemory* backendMemory) {
99
0
    TRACE_EVENT0_ALWAYS("skia.gpu", TRACE_FUNC);
100
0
    VmaAllocationCreateInfo info;
101
0
    info.flags = 0;
102
0
    info.usage = VMA_MEMORY_USAGE_UNKNOWN;
103
0
    info.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
104
0
    info.preferredFlags = 0;
105
0
    info.memoryTypeBits = 0;
106
0
    info.pool = VK_NULL_HANDLE;
107
0
    info.pUserData = nullptr;
108
109
0
    if (kDedicatedAllocation_AllocationPropertyFlag & allocationPropertyFlags) {
110
0
        info.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
111
0
    }
112
0
    if (kLazyAllocation_AllocationPropertyFlag & allocationPropertyFlags) {
113
0
        info.requiredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
114
0
    }
115
0
    if (kProtected_AllocationPropertyFlag & allocationPropertyFlags) {
116
0
        info.requiredFlags |= VK_MEMORY_PROPERTY_PROTECTED_BIT;
117
0
    }
118
119
0
    VmaAllocation allocation;
120
0
    VkResult result = vmaAllocateMemoryForImage(fAllocator, image, &info, &allocation, nullptr);
121
0
    if (VK_SUCCESS == result) {
122
0
        *backendMemory = (skgpu::VulkanBackendMemory)allocation;
123
0
    }
124
0
    return result;
125
0
}
126
127
VkResult VkTestMemoryAllocator::allocateBufferMemory(VkBuffer buffer,
128
                                                     BufferUsage usage,
129
                                                     uint32_t allocationPropertyFlags,
130
0
                                                     skgpu::VulkanBackendMemory* backendMemory) {
131
0
    TRACE_EVENT0("skia.gpu", TRACE_FUNC);
132
0
    VmaAllocationCreateInfo info;
133
0
    info.flags = 0;
134
0
    info.usage = VMA_MEMORY_USAGE_UNKNOWN;
135
0
    info.memoryTypeBits = 0;
136
0
    info.pool = VK_NULL_HANDLE;
137
0
    info.pUserData = nullptr;
138
139
0
    switch (usage) {
140
0
        case BufferUsage::kGpuOnly:
141
0
            info.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
142
0
            info.preferredFlags = 0;
143
0
            break;
144
0
        case BufferUsage::kCpuWritesGpuReads:
145
            // When doing cpu writes and gpu reads the general rule of thumb is to use coherent
146
            // memory. Though this depends on the fact that we are not doing any cpu reads and the
147
            // cpu writes are sequential. For sparse writes we'd want cpu cached memory, however we
148
            // don't do these types of writes in Skia.
149
            //
150
            // TODO: In the future there may be times where specific types of memory could benefit
151
            // from a coherent and cached memory. Typically these allow for the gpu to read cpu
152
            // writes from the cache without needing to flush the writes throughout the cache. The
153
            // reverse is not true and GPU writes tend to invalidate the cache regardless. Also
154
            // these gpu cache read access are typically lower bandwidth than non-cached memory.
155
            // For now Skia doesn't really have a need or want of this type of memory. But if we
156
            // ever do we could pass in an AllocationPropertyFlag that requests the cached property.
157
0
            info.requiredFlags =
158
0
                    VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
159
0
            info.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
160
0
            break;
161
0
        case BufferUsage::kTransfersFromCpuToGpu:
162
0
            info.requiredFlags =
163
0
                    VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
164
0
            info.preferredFlags = 0;
165
0
            break;
166
0
        case BufferUsage::kTransfersFromGpuToCpu:
167
0
            info.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
168
0
            info.preferredFlags = VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
169
0
            break;
170
0
    }
171
172
0
    if (kDedicatedAllocation_AllocationPropertyFlag & allocationPropertyFlags) {
173
0
        info.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
174
0
    }
175
0
    if ((kLazyAllocation_AllocationPropertyFlag & allocationPropertyFlags) &&
176
0
        BufferUsage::kGpuOnly == usage) {
177
0
        info.preferredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
178
0
    }
179
180
0
    if (kPersistentlyMapped_AllocationPropertyFlag & allocationPropertyFlags) {
181
0
        SkASSERT(BufferUsage::kGpuOnly != usage);
182
0
        info.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
183
0
    }
184
185
0
    VmaAllocation allocation;
186
0
    VkResult result = vmaAllocateMemoryForBuffer(fAllocator, buffer, &info, &allocation, nullptr);
187
0
    if (VK_SUCCESS == result) {
188
0
        *backendMemory = (skgpu::VulkanBackendMemory)allocation;
189
0
    }
190
191
0
    return result;
192
0
}
Unexecuted instantiation: sk_gpu_test::VkTestMemoryAllocator::allocateBufferMemory(VkBuffer_T*, skgpu::VulkanMemoryAllocator::BufferUsage, unsigned int, long*)
Unexecuted instantiation: sk_gpu_test::VkTestMemoryAllocator::allocateBufferMemory(VkBuffer_T*, skgpu::VulkanMemoryAllocator::BufferUsage, unsigned int, long*)
193
194
0
void VkTestMemoryAllocator::freeMemory(const skgpu::VulkanBackendMemory& memoryHandle) {
195
0
    TRACE_EVENT0("skia.gpu", TRACE_FUNC);
196
0
    const VmaAllocation allocation = (VmaAllocation)memoryHandle;
197
0
    vmaFreeMemory(fAllocator, allocation);
198
0
}
199
200
void VkTestMemoryAllocator::getAllocInfo(const skgpu::VulkanBackendMemory& memoryHandle,
201
0
                                         skgpu::VulkanAlloc* alloc) const {
202
0
    const VmaAllocation allocation = (VmaAllocation)memoryHandle;
203
0
    VmaAllocationInfo vmaInfo;
204
0
    vmaGetAllocationInfo(fAllocator, allocation, &vmaInfo);
205
206
0
    VkMemoryPropertyFlags memFlags;
207
0
    vmaGetMemoryTypeProperties(fAllocator, vmaInfo.memoryType, &memFlags);
208
209
0
    uint32_t flags = 0;
210
0
    if (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT & memFlags) {
211
0
        flags |= skgpu::VulkanAlloc::kMappable_Flag;
212
0
    }
213
0
    if (!SkToBool(VK_MEMORY_PROPERTY_HOST_COHERENT_BIT & memFlags)) {
214
0
        flags |= skgpu::VulkanAlloc::kNoncoherent_Flag;
215
0
    }
216
0
    if (VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT & memFlags) {
217
0
        flags |= skgpu::VulkanAlloc::kLazilyAllocated_Flag;
218
0
    }
219
220
0
    alloc->fMemory = vmaInfo.deviceMemory;
221
0
    alloc->fOffset = vmaInfo.offset;
222
0
    alloc->fSize = vmaInfo.size;
223
0
    alloc->fFlags = flags;
224
0
    alloc->fBackendMemory = memoryHandle;
225
0
}
226
227
VkResult VkTestMemoryAllocator::mapMemory(const skgpu::VulkanBackendMemory& memoryHandle,
228
0
                                          void** data) {
229
0
    TRACE_EVENT0("skia.gpu", TRACE_FUNC);
230
0
    const VmaAllocation allocation = (VmaAllocation)memoryHandle;
231
0
    return vmaMapMemory(fAllocator, allocation, data);
232
0
}
233
234
0
void VkTestMemoryAllocator::unmapMemory(const skgpu::VulkanBackendMemory& memoryHandle) {
235
0
    TRACE_EVENT0("skia.gpu", TRACE_FUNC);
236
0
    const VmaAllocation allocation = (VmaAllocation)memoryHandle;
237
0
    vmaUnmapMemory(fAllocator, allocation);
238
0
}
239
240
VkResult VkTestMemoryAllocator::flushMemory(const skgpu::VulkanBackendMemory& memoryHandle,
241
                                            VkDeviceSize offset,
242
0
                                            VkDeviceSize size) {
243
0
    TRACE_EVENT0("skia.gpu", TRACE_FUNC);
244
0
    const VmaAllocation allocation = (VmaAllocation)memoryHandle;
245
0
    return vmaFlushAllocation(fAllocator, allocation, offset, size);
246
0
}
247
248
VkResult VkTestMemoryAllocator::invalidateMemory(const skgpu::VulkanBackendMemory& memoryHandle,
249
                                                 VkDeviceSize offset,
250
0
                                                 VkDeviceSize size) {
251
0
    TRACE_EVENT0("skia.gpu", TRACE_FUNC);
252
0
    const VmaAllocation allocation = (VmaAllocation)memoryHandle;
253
0
    return vmaInvalidateAllocation(fAllocator, allocation, offset, size);
254
0
}
255
256
0
std::pair<uint64_t, uint64_t> VkTestMemoryAllocator::totalAllocatedAndUsedMemory() const {
257
0
    VmaTotalStatistics stats;
258
0
    vmaCalculateStatistics(fAllocator, &stats);
259
0
    return {stats.total.statistics.blockBytes, stats.total.statistics.allocationBytes};
260
0
}
261
262
}  // namespace sk_gpu_test