1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
|
/*
* Copyright 2019-2021 Diligent Graphics LLC
* Copyright 2015-2019 Egor Yusov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* In no event and under no legal theory, whether in tort (including negligence),
* contract, or otherwise, unless required by applicable law (such as deliberate
* and grossly negligent acts) or agreed to in writing, shall any Contributor be
* liable for any damages, including any direct, indirect, special, incidental,
* or consequential damages of any character arising as a result of this License or
* out of the use or inability to use the software (including but not limited to damages
* for loss of goodwill, work stoppage, computer failure or malfunction, or any and
* all other commercial damages or losses), even if such Contributor has been advised
* of the possibility of such damages.
*/
#include "pch.h"
#include "VulkanUploadHeap.hpp"
#include "RenderDeviceVkImpl.hpp"
namespace Diligent
{
VulkanUploadHeap::VulkanUploadHeap(RenderDeviceVkImpl& RenderDevice,
std::string HeapName,
VkDeviceSize PageSize) :
// clang-format off
m_RenderDevice {RenderDevice },
m_HeapName {std::move(HeapName)},
m_PageSize {PageSize }
// clang-format on
{
}
VulkanUploadHeap::~VulkanUploadHeap()
{
DEV_CHECK_ERR(m_Pages.empty(), "Upload heap '", m_HeapName, "' not all pages are released");
auto PeakAllocatedPages = m_PeakAllocatedSize / m_PageSize;
LOG_INFO_MESSAGE(m_HeapName, " peak used/allocated frame size: ", FormatMemorySize(m_PeakFrameSize, 2, m_PeakAllocatedSize),
" / ", FormatMemorySize(m_PeakAllocatedSize, 2),
" (", PeakAllocatedPages, (PeakAllocatedPages == 1 ? " page)" : " pages)"));
}
VulkanUploadHeap::UploadPageInfo VulkanUploadHeap::CreateNewPage(VkDeviceSize SizeInBytes) const
{
VkBufferCreateInfo StagingBufferCI = {};
StagingBufferCI.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
StagingBufferCI.pNext = nullptr;
StagingBufferCI.flags = 0; // VK_BUFFER_CREATE_SPARSE_BINDING_BIT, VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT, VK_BUFFER_CREATE_SPARSE_ALIASED_BIT
StagingBufferCI.size = SizeInBytes;
StagingBufferCI.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
StagingBufferCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
StagingBufferCI.queueFamilyIndexCount = 0;
StagingBufferCI.pQueueFamilyIndices = nullptr;
const auto& LogicalDevice = m_RenderDevice.GetLogicalDevice();
const auto& PhysicalDevice = m_RenderDevice.GetPhysicalDevice();
auto& GlobalMemoryMgr = m_RenderDevice.GetGlobalMemoryManager();
auto NewBuffer = LogicalDevice.CreateBuffer(StagingBufferCI, "Upload buffer");
auto MemReqs = LogicalDevice.GetBufferMemoryRequirements(NewBuffer);
auto MemoryTypeIndex = PhysicalDevice.GetMemoryTypeIndex(MemReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
DEV_CHECK_ERR(MemoryTypeIndex != VulkanUtilities::VulkanPhysicalDevice::InvalidMemoryTypeIndex,
"Vulkan spec requires that for a VkBuffer not created with the VK_BUFFER_CREATE_SPARSE_BINDING_BIT "
"bit set, or for a VkImage that was created with a VK_IMAGE_TILING_LINEAR value in the tiling member "
"of the VkImageCreateInfo structure passed to vkCreateImage, the memoryTypeBits member always contains "
"at least one bit set corresponding to a VkMemoryType with a propertyFlags that has both the "
"VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT bit AND the VK_MEMORY_PROPERTY_HOST_COHERENT_BIT bit set. (11.6)");
auto MemAllocation = GlobalMemoryMgr.Allocate(MemReqs.size, MemReqs.alignment, MemoryTypeIndex, true, VkMemoryAllocateFlags{0});
auto AlignedOffset = (MemAllocation.UnalignedOffset + (MemReqs.alignment - 1)) & ~(MemReqs.alignment - 1);
auto err = LogicalDevice.BindBufferMemory(NewBuffer, MemAllocation.Page->GetVkMemory(), AlignedOffset);
DEV_CHECK_ERR(err == VK_SUCCESS, "Failed to bind buffer memory");
(void)err;
auto CPUAddress = reinterpret_cast<Uint8*>(MemAllocation.Page->GetCPUMemory()) + AlignedOffset;
return UploadPageInfo{std::move(MemAllocation), std::move(NewBuffer), CPUAddress};
}
VulkanUploadAllocation VulkanUploadHeap::Allocate(VkDeviceSize SizeInBytes, VkDeviceSize Alignment)
{
VERIFY(IsPowerOfTwo(Alignment), "Alignment (", Alignment, ") must be power of two");
VulkanUploadAllocation Allocation;
if (SizeInBytes >= m_PageSize / 2)
{
// Allocate large chunk directly from the memory manager
auto NewPage = CreateNewPage(SizeInBytes);
Allocation.vkBuffer = NewPage.Buffer;
Allocation.CPUAddress = NewPage.CPUAddress;
Allocation.Size = SizeInBytes;
VERIFY(Alignment < SizeInBytes, "Alignment must be smaller than the page size");
Allocation.AlignedOffset = 0;
m_CurrAllocatedSize += NewPage.MemAllocation.Size;
m_Pages.emplace_back(std::move(NewPage));
}
else
{
auto AlignmentOffset = AlignUp(m_CurrPage.CurrOffset, Alignment) - m_CurrPage.CurrOffset;
if (m_CurrPage.AvailableSize < SizeInBytes + AlignmentOffset)
{
// Allocate new page
auto NewPage = CreateNewPage(m_PageSize);
m_CurrPage.Reset(NewPage, m_PageSize);
m_CurrAllocatedSize += NewPage.MemAllocation.Size;
m_Pages.emplace_back(std::move(NewPage));
VERIFY_EXPR((m_CurrPage.CurrOffset & (Alignment - 1)) == 0);
AlignmentOffset = 0;
}
m_CurrPage.Advance(AlignmentOffset);
VERIFY_EXPR((m_CurrPage.CurrOffset & (Alignment - 1)) == 0);
Allocation.vkBuffer = m_CurrPage.vkBuffer;
Allocation.CPUAddress = m_CurrPage.CurrCPUAddress;
Allocation.Size = SizeInBytes;
Allocation.AlignedOffset = m_CurrPage.CurrOffset;
m_CurrPage.Advance(SizeInBytes);
}
m_CurrFrameSize += SizeInBytes; // Count unaligned size
m_PeakFrameSize = std::max(m_CurrFrameSize, m_PeakFrameSize);
m_PeakAllocatedSize = std::max(m_CurrAllocatedSize, m_PeakAllocatedSize);
VERIFY_EXPR((Allocation.AlignedOffset & (Alignment - 1)) == 0);
return Allocation;
}
void VulkanUploadHeap::ReleaseAllocatedPages(Uint64 CmdQueueMask)
{
// The pages will go into the stale resources queue first, however they will move into the release
// queue rightaway when RenderDeviceVkImpl::FlushStaleResources() is called by the DeviceContextVkImpl::FinishFrame()
for (auto& Page : m_Pages)
{
m_RenderDevice.SafeReleaseDeviceObject(std::move(Page.MemAllocation), CmdQueueMask);
m_RenderDevice.SafeReleaseDeviceObject(std::move(Page.Buffer), CmdQueueMask);
}
m_Pages.clear();
m_CurrPage = CurrPageInfo{};
m_CurrFrameSize = 0;
m_CurrAllocatedSize = 0;
}
} // namespace Diligent
|