1 // Copyright 2018 The Amber Authors.
2 // Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15
16 #include "src/vulkan/resource.h"
17
18 #include <cstring>
19 #include <limits>
20
21 #include "src/vulkan/command_buffer.h"
22 #include "src/vulkan/device.h"
23
24 namespace amber {
25 namespace vulkan {
26 namespace {
27
28 VkMemoryBarrier kMemoryBarrierForAll = {
29 VK_STRUCTURE_TYPE_MEMORY_BARRIER, nullptr,
30 VK_ACCESS_INDIRECT_COMMAND_READ_BIT | VK_ACCESS_INDEX_READ_BIT |
31 VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_UNIFORM_READ_BIT |
32 VK_ACCESS_INPUT_ATTACHMENT_READ_BIT | VK_ACCESS_SHADER_READ_BIT |
33 VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
34 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
35 VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
36 VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
37 VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT |
38 VK_ACCESS_HOST_READ_BIT | VK_ACCESS_HOST_WRITE_BIT,
39 VK_ACCESS_INDIRECT_COMMAND_READ_BIT | VK_ACCESS_INDEX_READ_BIT |
40 VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_UNIFORM_READ_BIT |
41 VK_ACCESS_INPUT_ATTACHMENT_READ_BIT | VK_ACCESS_SHADER_READ_BIT |
42 VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
43 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
44 VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
45 VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
46 VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT |
47 VK_ACCESS_HOST_READ_BIT | VK_ACCESS_HOST_WRITE_BIT};
48
49 } // namespace
50
Resource(Device * device,uint32_t size_in_bytes)51 Resource::Resource(Device* device, uint32_t size_in_bytes)
52 : device_(device), size_in_bytes_(size_in_bytes) {}
53
54 Resource::~Resource() = default;
55
CreateVkBuffer(VkBuffer * buffer,VkBufferUsageFlags usage)56 Result Resource::CreateVkBuffer(VkBuffer* buffer, VkBufferUsageFlags usage) {
57 if (!buffer)
58 return Result("Vulkan::Given VkBuffer pointer is nullptr");
59
60 VkBufferCreateInfo buffer_info = VkBufferCreateInfo();
61 buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
62 buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
63 buffer_info.size = size_in_bytes_;
64 buffer_info.usage = usage;
65
66 if (device_->GetPtrs()->vkCreateBuffer(device_->GetVkDevice(), &buffer_info,
67 nullptr, buffer) != VK_SUCCESS) {
68 return Result("Vulkan::Calling vkCreateBuffer Fail");
69 }
70
71 return {};
72 }
73
ChooseMemory(uint32_t memory_type_bits,VkMemoryPropertyFlags flags,bool require_flags_found)74 uint32_t Resource::ChooseMemory(uint32_t memory_type_bits,
75 VkMemoryPropertyFlags flags,
76 bool require_flags_found) {
77 // Based on Vulkan spec about VkMemoryRequirements, N th bit of
78 // |memory_type_bits| is 1 where N can be the proper memory type index.
79 // This code is looking for the first non-zero bit whose memory type
80 // VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT property. If not exists,
81 // it returns the first non-zero bit.
82 uint32_t first_non_zero = std::numeric_limits<uint32_t>::max();
83 uint32_t memory_type_index = 0;
84 while (memory_type_bits) {
85 if (memory_type_bits % 2) {
86 if (first_non_zero == std::numeric_limits<uint32_t>::max())
87 first_non_zero = memory_type_index;
88
89 if (device_->HasMemoryFlags(memory_type_index, flags))
90 return memory_type_index;
91 }
92
93 ++memory_type_index;
94 memory_type_bits >>= 1;
95 }
96
97 if (require_flags_found)
98 return std::numeric_limits<uint32_t>::max();
99
100 return first_non_zero;
101 }
AllocateAndBindMemoryToVkBuffer(VkBuffer buffer,VkDeviceMemory * memory,VkMemoryPropertyFlags flags,bool require_flags_found,uint32_t * memory_type_index)102 Result Resource::AllocateAndBindMemoryToVkBuffer(VkBuffer buffer,
103 VkDeviceMemory* memory,
104 VkMemoryPropertyFlags flags,
105 bool require_flags_found,
106 uint32_t* memory_type_index) {
107 if (memory_type_index == nullptr) {
108 return Result(
109 "Vulkan: Resource::AllocateAndBindMemoryToVkBuffer memory_type_index "
110 "is nullptr");
111 }
112
113 *memory_type_index = 0;
114
115 if (buffer == VK_NULL_HANDLE)
116 return Result("Vulkan::Given VkBuffer is VK_NULL_HANDLE");
117 if (memory == nullptr)
118 return Result("Vulkan::Given VkDeviceMemory pointer is nullptr");
119
120 VkMemoryRequirements requirement;
121 device_->GetPtrs()->vkGetBufferMemoryRequirements(device_->GetVkDevice(),
122 buffer, &requirement);
123
124 *memory_type_index =
125 ChooseMemory(requirement.memoryTypeBits, flags, require_flags_found);
126 if (*memory_type_index == std::numeric_limits<uint32_t>::max())
127 return Result("Vulkan::Find Proper Memory Fail");
128
129 Result r = AllocateMemory(memory, requirement.size, *memory_type_index);
130 if (!r.IsSuccess())
131 return r;
132
133 if (device_->GetPtrs()->vkBindBufferMemory(device_->GetVkDevice(), buffer,
134 *memory, 0) != VK_SUCCESS) {
135 return Result("Vulkan::Calling vkBindBufferMemory Fail");
136 }
137
138 return {};
139 }
140
AllocateMemory(VkDeviceMemory * memory,VkDeviceSize size,uint32_t memory_type_index)141 Result Resource::AllocateMemory(VkDeviceMemory* memory,
142 VkDeviceSize size,
143 uint32_t memory_type_index) {
144 VkMemoryAllocateInfo alloc_info = VkMemoryAllocateInfo();
145 VkMemoryAllocateFlagsInfo allocFlagsInfo = VkMemoryAllocateFlagsInfo();
146
147 alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
148 alloc_info.allocationSize = size;
149 alloc_info.memoryTypeIndex = memory_type_index;
150
151 if (memory_allocate_flags_ != 0) {
152 allocFlagsInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO;
153 allocFlagsInfo.pNext = nullptr;
154 allocFlagsInfo.flags = memory_allocate_flags_;
155 allocFlagsInfo.deviceMask = 0u;
156
157 alloc_info.pNext = &allocFlagsInfo;
158 }
159
160 if (device_->GetPtrs()->vkAllocateMemory(device_->GetVkDevice(), &alloc_info,
161 nullptr, memory) != VK_SUCCESS) {
162 return Result("Vulkan::Calling vkAllocateMemory Fail");
163 }
164
165 return {};
166 }
167
MapMemory(VkDeviceMemory memory)168 Result Resource::MapMemory(VkDeviceMemory memory) {
169 if (device_->GetPtrs()->vkMapMemory(device_->GetVkDevice(), memory, 0,
170 VK_WHOLE_SIZE, 0,
171 &memory_ptr_) != VK_SUCCESS) {
172 return Result("Vulkan::Calling vkMapMemory Fail");
173 }
174
175 return {};
176 }
177
UnMapMemory(VkDeviceMemory memory)178 void Resource::UnMapMemory(VkDeviceMemory memory) {
179 device_->GetPtrs()->vkUnmapMemory(device_->GetVkDevice(), memory);
180 }
181
UpdateMemoryWithRawData(const std::vector<uint8_t> & raw_data)182 void Resource::UpdateMemoryWithRawData(const std::vector<uint8_t>& raw_data) {
183 size_t effective_size =
184 raw_data.size() > GetSizeInBytes() ? GetSizeInBytes() : raw_data.size();
185 std::memcpy(HostAccessibleMemoryPtr(), raw_data.data(), effective_size);
186 }
187
MemoryBarrier(CommandBuffer * command_buffer)188 void Resource::MemoryBarrier(CommandBuffer* command_buffer) {
189 // TODO(jaebaek): Current memory barrier is naively implemented.
190 // Update it with the following access flags:
191 // (r = read, w = write)
192 //
193 // Host Device
194 // VertexBuffer host w vertex r
195 // transfer w transfer r
196 //
197 // IndexBuffer host w index r
198 // transfer w transfer r
199 //
200 // FrameBuffer host r color w
201 // depth/stencil w
202 // transfer r transfer w
203 //
204 // ReadWrite Descriptors host r/w shader r/w
205 // transfer r/w transfer r/w
206 //
207 // ReadOnly Descriptors host w shader r
208 // transfer w transfer r
209 device_->GetPtrs()->vkCmdPipelineBarrier(
210 command_buffer->GetVkCommandBuffer(), VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
211 VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 1, &kMemoryBarrierForAll, 0,
212 nullptr, 0, nullptr);
213 }
214
215 } // namespace vulkan
216 } // namespace amber
217