1 // Copyright 2018 The Amber Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "src/vulkan/transfer_image.h"
16
17 #include <cstring>
18 #include <limits>
19 #include <vector>
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 const VkImageCreateInfo kDefaultImageInfo = {
29 VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, /* sType */
30 nullptr, /* pNext */
31 0, /* flags */
32 VK_IMAGE_TYPE_2D, /* imageType */
33 VK_FORMAT_R8G8B8A8_UNORM, /* format */
34 {250, 250, 1}, /* extent */
35 1, /* mipLevels */
36 1, /* arrayLayers */
37 VK_SAMPLE_COUNT_1_BIT, /* samples */
38 VK_IMAGE_TILING_OPTIMAL, /* tiling */
39 0, /* usage */
40 VK_SHARING_MODE_EXCLUSIVE, /* sharingMode */
41 0, /* queueFamilyIndexCount */
42 nullptr, /* pQueueFamilyIndices */
43 VK_IMAGE_LAYOUT_UNDEFINED, /* initialLayout */
44 };
45
GetVkSampleCount(uint32_t samples)46 VkSampleCountFlagBits GetVkSampleCount(uint32_t samples) {
47 switch (samples) {
48 case 1u:
49 return VK_SAMPLE_COUNT_1_BIT;
50 case 2u:
51 return VK_SAMPLE_COUNT_2_BIT;
52 case 4u:
53 return VK_SAMPLE_COUNT_4_BIT;
54 case 8u:
55 return VK_SAMPLE_COUNT_8_BIT;
56 case 16u:
57 return VK_SAMPLE_COUNT_16_BIT;
58 case 32u:
59 return VK_SAMPLE_COUNT_32_BIT;
60 case 64u:
61 return VK_SAMPLE_COUNT_64_BIT;
62 }
63
64 return VK_SAMPLE_COUNT_FLAG_BITS_MAX_ENUM;
65 }
66
67 } // namespace
68
TransferImage(Device * device,const Format & format,VkImageAspectFlags aspect,VkImageType image_type,uint32_t x,uint32_t y,uint32_t z,uint32_t mip_levels,uint32_t base_mip_level,uint32_t used_mip_levels,uint32_t samples)69 TransferImage::TransferImage(Device* device,
70 const Format& format,
71 VkImageAspectFlags aspect,
72 VkImageType image_type,
73 uint32_t x,
74 uint32_t y,
75 uint32_t z,
76 uint32_t mip_levels,
77 uint32_t base_mip_level,
78 uint32_t used_mip_levels,
79 uint32_t samples)
80 : Resource(
81 device,
82 x * y * z *
83 (format.SizeInBytes() +
84 // D24_UNORM_S8_UINT requires 32bit component for depth when
85 // performing buffer copies. Reserve extra room to handle that.
86 (format.GetFormatType() == FormatType::kD24_UNORM_S8_UINT ? 1
87 : 0))),
88 image_info_(kDefaultImageInfo),
89 aspect_(aspect),
90 mip_levels_(mip_levels),
91 base_mip_level_(base_mip_level),
92 used_mip_levels_(used_mip_levels),
93 samples_(samples) {
94 image_info_.format = device_->GetVkFormat(format);
95 image_info_.imageType = image_type;
96 image_info_.extent = {x, y, z};
97 image_info_.mipLevels = mip_levels;
98 image_info_.samples = GetVkSampleCount(samples);
99 }
100
~TransferImage()101 TransferImage::~TransferImage() {
102 if (view_ != VK_NULL_HANDLE) {
103 device_->GetPtrs()->vkDestroyImageView(device_->GetVkDevice(), view_,
104 nullptr);
105 }
106
107 if (image_ != VK_NULL_HANDLE)
108 device_->GetPtrs()->vkDestroyImage(device_->GetVkDevice(), image_, nullptr);
109
110 if (memory_ != VK_NULL_HANDLE)
111 device_->GetPtrs()->vkFreeMemory(device_->GetVkDevice(), memory_, nullptr);
112
113 if (host_accessible_memory_ != VK_NULL_HANDLE) {
114 UnMapMemory(host_accessible_memory_);
115 device_->GetPtrs()->vkFreeMemory(device_->GetVkDevice(),
116 host_accessible_memory_, nullptr);
117 }
118
119 if (host_accessible_buffer_ != VK_NULL_HANDLE) {
120 device_->GetPtrs()->vkDestroyBuffer(device_->GetVkDevice(),
121 host_accessible_buffer_, nullptr);
122 }
123 }
124
Initialize(VkImageUsageFlags usage)125 Result TransferImage::Initialize(VkImageUsageFlags usage) {
126 if (image_ != VK_NULL_HANDLE)
127 return Result("Vulkan::TransferImage was already initialized");
128
129 image_info_.usage = usage;
130
131 if (device_->GetPtrs()->vkCreateImage(device_->GetVkDevice(), &image_info_,
132 nullptr, &image_) != VK_SUCCESS) {
133 return Result("Vulkan::Calling vkCreateImage Fail");
134 }
135
136 uint32_t memory_type_index = 0;
137 Result r = AllocateAndBindMemoryToVkImage(image_, &memory_,
138 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
139 false, &memory_type_index);
140 if (!r.IsSuccess())
141 return r;
142
143 if (aspect_ & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT) &&
144 !(usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) {
145 // Combined depth/stencil image used as a descriptor. Only one aspect can be
146 // used for the image view.
147 r = CreateVkImageView(VK_IMAGE_ASPECT_DEPTH_BIT);
148 } else {
149 r = CreateVkImageView(aspect_);
150 }
151
152 if (!r.IsSuccess())
153 return r;
154
155 // For images, we always make a secondary buffer. When the tiling of an image
156 // is optimal, read/write data from CPU does not show correct values. We need
157 // a secondary buffer to convert the GPU-optimal data to CPU-readable data
158 // and vice versa.
159 r = CreateVkBuffer(
160 &host_accessible_buffer_,
161 VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
162 if (!r.IsSuccess())
163 return r;
164
165 memory_type_index = 0;
166 r = AllocateAndBindMemoryToVkBuffer(host_accessible_buffer_,
167 &host_accessible_memory_,
168 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
169 VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
170 true, &memory_type_index);
171 if (!r.IsSuccess())
172 return r;
173
174 return MapMemory(host_accessible_memory_);
175 }
176
GetImageViewType() const177 VkImageViewType TransferImage::GetImageViewType() const {
178 // TODO(alan-baker): handle other view types.
179 // 1D-array, 2D-array, Cube, Cube-array.
180 switch (image_info_.imageType) {
181 case VK_IMAGE_TYPE_1D:
182 return VK_IMAGE_VIEW_TYPE_1D;
183 case VK_IMAGE_TYPE_2D:
184 return VK_IMAGE_VIEW_TYPE_2D;
185 case VK_IMAGE_TYPE_3D:
186 return VK_IMAGE_VIEW_TYPE_3D;
187 default:
188 break;
189 }
190
191 // Default to 2D image view.
192 return VK_IMAGE_VIEW_TYPE_2D;
193 }
194
CreateVkImageView(VkImageAspectFlags aspect)195 Result TransferImage::CreateVkImageView(VkImageAspectFlags aspect) {
196 VkImageViewCreateInfo image_view_info = VkImageViewCreateInfo();
197 image_view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
198 image_view_info.image = image_;
199 image_view_info.viewType = GetImageViewType();
200 image_view_info.format = image_info_.format;
201 image_view_info.components = {
202 VK_COMPONENT_SWIZZLE_R,
203 VK_COMPONENT_SWIZZLE_G,
204 VK_COMPONENT_SWIZZLE_B,
205 VK_COMPONENT_SWIZZLE_A,
206 };
207 image_view_info.subresourceRange = {
208 aspect, /* aspectMask */
209 base_mip_level_, /* baseMipLevel */
210 used_mip_levels_, /* levelCount */
211 0, /* baseArrayLayer */
212 1, /* layerCount */
213 };
214
215 if (device_->GetPtrs()->vkCreateImageView(device_->GetVkDevice(),
216 &image_view_info, nullptr,
217 &view_) != VK_SUCCESS) {
218 return Result("Vulkan::Calling vkCreateImageView Fail");
219 }
220
221 return {};
222 }
223
CreateBufferImageCopy(VkImageAspectFlags aspect,uint32_t mip_level)224 VkBufferImageCopy TransferImage::CreateBufferImageCopy(
225 VkImageAspectFlags aspect,
226 uint32_t mip_level) {
227 VkBufferImageCopy copy_region = VkBufferImageCopy();
228 if (aspect == VK_IMAGE_ASPECT_STENCIL_BIT) {
229 // Store stencil data at the end of the buffer after depth data.
230 copy_region.bufferOffset =
231 GetSizeInBytes() - image_info_.extent.width * image_info_.extent.height;
232 } else {
233 copy_region.bufferOffset = 0;
234 }
235 // Row length of 0 results in tight packing of rows, so the row stride
236 // is the number of texels times the texel stride.
237 copy_region.bufferRowLength = 0;
238 copy_region.bufferImageHeight = 0;
239 copy_region.imageSubresource = {
240 aspect, /* aspectMask */
241 mip_level, /* mipLevel */
242 0, /* baseArrayLayer */
243 1, /* layerCount */
244 };
245 copy_region.imageOffset = {0, 0, 0};
246 copy_region.imageExtent = {image_info_.extent.width >> mip_level,
247 image_info_.extent.height >> mip_level,
248 image_info_.extent.depth};
249 return copy_region;
250 }
251
CopyToHost(CommandBuffer * command_buffer)252 void TransferImage::CopyToHost(CommandBuffer* command_buffer) {
253 const VkImageAspectFlagBits aspects[] = {VK_IMAGE_ASPECT_COLOR_BIT,
254 VK_IMAGE_ASPECT_DEPTH_BIT,
255 VK_IMAGE_ASPECT_STENCIL_BIT};
256 // Copy operations don't support multisample images.
257 if (samples_ > 1)
258 return;
259
260 std::vector<VkBufferImageCopy> copy_regions;
261 uint32_t last_mip_level = used_mip_levels_ == VK_REMAINING_MIP_LEVELS
262 ? mip_levels_
263 : base_mip_level_ + used_mip_levels_;
264 for (uint32_t i = base_mip_level_; i < last_mip_level; i++) {
265 for (auto aspect : aspects) {
266 if (aspect_ & aspect) {
267 copy_regions.push_back(CreateBufferImageCopy(aspect, i));
268 }
269 }
270 }
271
272 device_->GetPtrs()->vkCmdCopyImageToBuffer(
273 command_buffer->GetVkCommandBuffer(), image_,
274 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, host_accessible_buffer_,
275 static_cast<uint32_t>(copy_regions.size()), copy_regions.data());
276
277 MemoryBarrier(command_buffer);
278 }
279
CopyToDevice(CommandBuffer * command_buffer)280 void TransferImage::CopyToDevice(CommandBuffer* command_buffer) {
281 // Copy operations don't support multisample images.
282 if (samples_ > 1)
283 return;
284
285 const VkImageAspectFlagBits aspects[] = {VK_IMAGE_ASPECT_COLOR_BIT,
286 VK_IMAGE_ASPECT_DEPTH_BIT,
287 VK_IMAGE_ASPECT_STENCIL_BIT};
288 std::vector<VkBufferImageCopy> copy_regions;
289 uint32_t last_mip_level = used_mip_levels_ == VK_REMAINING_MIP_LEVELS
290 ? mip_levels_
291 : base_mip_level_ + used_mip_levels_;
292 for (uint32_t i = base_mip_level_; i < last_mip_level; i++) {
293 for (auto aspect : aspects) {
294 if (aspect_ & aspect) {
295 copy_regions.push_back(CreateBufferImageCopy(aspect, i));
296 }
297 }
298 }
299
300 device_->GetPtrs()->vkCmdCopyBufferToImage(
301 command_buffer->GetVkCommandBuffer(), host_accessible_buffer_, image_,
302 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
303 static_cast<uint32_t>(copy_regions.size()), copy_regions.data());
304
305 MemoryBarrier(command_buffer);
306 }
307
ImageBarrier(CommandBuffer * command_buffer,VkImageLayout to_layout,VkPipelineStageFlags to_stage)308 void TransferImage::ImageBarrier(CommandBuffer* command_buffer,
309 VkImageLayout to_layout,
310 VkPipelineStageFlags to_stage) {
311 if (to_layout == layout_ && to_stage == stage_)
312 return;
313
314 VkImageMemoryBarrier barrier = VkImageMemoryBarrier();
315 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
316 barrier.oldLayout = layout_;
317 barrier.newLayout = to_layout;
318 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
319 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
320 barrier.image = image_;
321 barrier.subresourceRange = {
322 aspect_, /* aspectMask */
323 0, /* baseMipLevel */
324 VK_REMAINING_MIP_LEVELS, /* levelCount */
325 0, /* baseArrayLayer */
326 1, /* layerCount */
327 };
328
329 switch (layout_) {
330 case VK_IMAGE_LAYOUT_PREINITIALIZED:
331 barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
332 break;
333 case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
334 barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
335 break;
336 case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
337 barrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
338 break;
339 case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
340 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
341 break;
342 case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
343 barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
344 break;
345 default:
346 barrier.srcAccessMask = 0;
347 break;
348 }
349
350 switch (to_layout) {
351 case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
352 barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
353 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
354 break;
355 case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
356 barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
357 VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
358 break;
359 case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL:
360 barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
361 break;
362 case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
363 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
364 break;
365 case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
366 barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
367 break;
368 case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
369 barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
370 break;
371 default:
372 barrier.dstAccessMask = 0;
373 break;
374 }
375
376 device_->GetPtrs()->vkCmdPipelineBarrier(command_buffer->GetVkCommandBuffer(),
377 stage_, to_stage, 0, 0, NULL, 0,
378 NULL, 1, &barrier);
379
380 layout_ = to_layout;
381 stage_ = to_stage;
382 }
383
AllocateAndBindMemoryToVkImage(VkImage image,VkDeviceMemory * memory,VkMemoryPropertyFlags flags,bool force_flags,uint32_t * memory_type_index)384 Result TransferImage::AllocateAndBindMemoryToVkImage(
385 VkImage image,
386 VkDeviceMemory* memory,
387 VkMemoryPropertyFlags flags,
388 bool force_flags,
389 uint32_t* memory_type_index) {
390 if (memory_type_index == nullptr) {
391 return Result(
392 "Vulkan: TransferImage::AllocateAndBindMemoryToVkImage "
393 "memory_type_index is "
394 "nullptr");
395 }
396
397 *memory_type_index = 0;
398
399 if (image == VK_NULL_HANDLE)
400 return Result("Vulkan::Given VkImage is VK_NULL_HANDLE");
401 if (memory == nullptr)
402 return Result("Vulkan::Given VkDeviceMemory pointer is nullptr");
403
404 VkMemoryRequirements requirement;
405 device_->GetPtrs()->vkGetImageMemoryRequirements(device_->GetVkDevice(),
406 image, &requirement);
407
408 *memory_type_index =
409 ChooseMemory(requirement.memoryTypeBits, flags, force_flags);
410 if (*memory_type_index == std::numeric_limits<uint32_t>::max())
411 return Result("Vulkan::Find Proper Memory Fail");
412
413 Result r = AllocateMemory(memory, requirement.size, *memory_type_index);
414 if (!r.IsSuccess())
415 return r;
416
417 if (device_->GetPtrs()->vkBindImageMemory(device_->GetVkDevice(), image,
418 *memory, 0) != VK_SUCCESS) {
419 return Result("Vulkan::Calling vkBindImageMemory Fail");
420 }
421
422 return {};
423 }
424
425 } // namespace vulkan
426 } // namespace amber
427