• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2022 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 "src/gpu/graphite/vk/VulkanTexture.h"
9 
10 #include "include/gpu/MutableTextureState.h"
11 #include "include/gpu/vk/VulkanMutableTextureState.h"
12 #include "src/core/SkMipmap.h"
13 #include "src/gpu/graphite/Log.h"
14 #include "src/gpu/graphite/vk/VulkanCaps.h"
15 #include "src/gpu/graphite/vk/VulkanCommandBuffer.h"
16 #include "src/gpu/graphite/vk/VulkanGraphiteUtilsPriv.h"
17 #include "src/gpu/graphite/vk/VulkanResourceProvider.h"
18 #include "src/gpu/graphite/vk/VulkanSharedContext.h"
19 #include "src/gpu/vk/VulkanMemory.h"
20 #include "src/gpu/vk/VulkanMutableTextureStatePriv.h"
21 
22 namespace skgpu::graphite {
23 
MakeVkImage(const VulkanSharedContext * sharedContext,SkISize dimensions,const TextureInfo & info,CreatedImageInfo * outInfo)24 bool VulkanTexture::MakeVkImage(const VulkanSharedContext* sharedContext,
25                                 SkISize dimensions,
26                                 const TextureInfo& info,
27                                 CreatedImageInfo* outInfo) {
28     SkASSERT(outInfo);
29     const VulkanCaps& caps = sharedContext->vulkanCaps();
30 
31     if (dimensions.isEmpty()) {
32         SKGPU_LOG_E("Tried to create VkImage with empty dimensions.");
33         return false;
34     }
35     if (dimensions.width() > caps.maxTextureSize() ||
36         dimensions.height() > caps.maxTextureSize()) {
37         SKGPU_LOG_E("Tried to create VkImage with too large a size.");
38         return false;
39     }
40 
41     if ((info.isProtected() == Protected::kYes) != caps.protectedSupport()) {
42         SKGPU_LOG_E("Tried to create %s VkImage in %s Context.",
43                     info.isProtected() == Protected::kYes ? "protected" : "unprotected",
44                     caps.protectedSupport() ? "protected" : "unprotected");
45         return false;
46     }
47 
48     const VulkanTextureSpec& spec = info.vulkanTextureSpec();
49 
50     bool isLinear = spec.fImageTiling == VK_IMAGE_TILING_LINEAR;
51     VkImageLayout initialLayout = isLinear ? VK_IMAGE_LAYOUT_PREINITIALIZED
52                                            : VK_IMAGE_LAYOUT_UNDEFINED;
53 
54     // Create Image
55     VkSampleCountFlagBits vkSamples;
56     if (!SampleCountToVkSampleCount(info.numSamples(), &vkSamples)) {
57         SKGPU_LOG_E("Failed creating VkImage because we could not covert the number of samples: "
58                     "%u to a VkSampleCountFlagBits.", info.numSamples());
59         return false;
60     }
61 
62     SkASSERT(!isLinear || vkSamples == VK_SAMPLE_COUNT_1_BIT);
63 
64     VkImageCreateFlags createflags = 0;
65     if (info.isProtected() == Protected::kYes && caps.protectedSupport()) {
66         createflags |= VK_IMAGE_CREATE_PROTECTED_BIT;
67     }
68 
69     uint32_t numMipLevels = 1;
70     if (info.mipmapped() == Mipmapped::kYes) {
71         numMipLevels = SkMipmap::ComputeLevelCount(dimensions.width(), dimensions.height()) + 1;
72     }
73 
74     uint32_t width = static_cast<uint32_t>(dimensions.fWidth);
75     uint32_t height = static_cast<uint32_t>(dimensions.fHeight);
76 
77     const VkImageCreateInfo imageCreateInfo = {
78         VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // sType
79         nullptr,                             // pNext
80         createflags,                         // VkImageCreateFlags
81         VK_IMAGE_TYPE_2D,                    // VkImageType
82         spec.fFormat,                        // VkFormat
83         { width, height, 1 },                // VkExtent3D
84         numMipLevels,                        // mipLevels
85         1,                                   // arrayLayers
86         vkSamples,                           // samples
87         spec.fImageTiling,                   // VkImageTiling
88         spec.fImageUsageFlags,               // VkImageUsageFlags
89         spec.fSharingMode,                   // VkSharingMode
90         0,                                   // queueFamilyCount
91         nullptr,                             // pQueueFamilyIndices
92         initialLayout                        // initialLayout
93     };
94 
95     auto device = sharedContext->device();
96 
97     VkImage image = VK_NULL_HANDLE;
98     VkResult result;
99     VULKAN_CALL_RESULT(
100             sharedContext, result, CreateImage(device, &imageCreateInfo, nullptr, &image));
101     if (result != VK_SUCCESS) {
102         SKGPU_LOG_E("Failed call to vkCreateImage with error: %d", result);
103         return false;
104     }
105 
106     auto allocator = sharedContext->memoryAllocator();
107     bool forceDedicatedMemory = caps.shouldAlwaysUseDedicatedImageMemory();
108     bool useLazyAllocation =
109             SkToBool(spec.fImageUsageFlags & VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT);
110 
111     auto checkResult = [sharedContext](VkResult result) {
112         return sharedContext->checkVkResult(result);
113     };
114     if (!skgpu::VulkanMemory::AllocImageMemory(allocator,
115                                                image,
116                                                info.isProtected(),
117                                                forceDedicatedMemory,
118                                                useLazyAllocation,
119                                                checkResult,
120                                                &outInfo->fMemoryAlloc)) {
121         VULKAN_CALL(sharedContext->interface(), DestroyImage(device, image, nullptr));
122         return false;
123     }
124 
125     if (useLazyAllocation &&
126         !SkToBool(outInfo->fMemoryAlloc.fFlags & skgpu::VulkanAlloc::kLazilyAllocated_Flag)) {
127         SKGPU_LOG_E("Failed allocate lazy vulkan memory when requested");
128         skgpu::VulkanMemory::FreeImageMemory(allocator, outInfo->fMemoryAlloc);
129         return false;
130     }
131 
132     VULKAN_CALL_RESULT(
133             sharedContext,
134             result,
135             BindImageMemory(
136                     device, image, outInfo->fMemoryAlloc.fMemory, outInfo->fMemoryAlloc.fOffset));
137     if (result != VK_SUCCESS) {
138         skgpu::VulkanMemory::FreeImageMemory(allocator, outInfo->fMemoryAlloc);
139         VULKAN_CALL(sharedContext->interface(), DestroyImage(device, image, nullptr));
140         return false;
141     }
142 
143     outInfo->fImage = image;
144     outInfo->fMutableState = sk_make_sp<MutableTextureState>(
145             skgpu::MutableTextureStates::MakeVulkan(initialLayout, VK_QUEUE_FAMILY_IGNORED));
146     return true;
147 }
148 
Make(const VulkanSharedContext * sharedContext,SkISize dimensions,const TextureInfo & info,skgpu::Budgeted budgeted,sk_sp<VulkanYcbcrConversion> ycbcrConversion)149 sk_sp<Texture> VulkanTexture::Make(const VulkanSharedContext* sharedContext,
150                                    SkISize dimensions,
151                                    const TextureInfo& info,
152                                    skgpu::Budgeted budgeted,
153                                    sk_sp<VulkanYcbcrConversion> ycbcrConversion) {
154     CreatedImageInfo imageInfo;
155     if (!MakeVkImage(sharedContext, dimensions, info, &imageInfo)) {
156         return nullptr;
157     }
158 
159     return sk_sp<Texture>(new VulkanTexture(sharedContext,
160                                             dimensions,
161                                             info,
162                                             std::move(imageInfo.fMutableState),
163                                             imageInfo.fImage,
164                                             imageInfo.fMemoryAlloc,
165                                             Ownership::kOwned,
166                                             budgeted,
167                                             std::move(ycbcrConversion)));
168 }
169 
MakeWrapped(const VulkanSharedContext * sharedContext,SkISize dimensions,const TextureInfo & info,sk_sp<MutableTextureState> mutableState,VkImage image,const VulkanAlloc & alloc,sk_sp<VulkanYcbcrConversion> ycbcrConversion)170 sk_sp<Texture> VulkanTexture::MakeWrapped(const VulkanSharedContext* sharedContext,
171                                           SkISize dimensions,
172                                           const TextureInfo& info,
173                                           sk_sp<MutableTextureState> mutableState,
174                                           VkImage image,
175                                           const VulkanAlloc& alloc,
176                                           sk_sp<VulkanYcbcrConversion> ycbcrConversion) {
177     return sk_sp<Texture>(new VulkanTexture(sharedContext,
178                                             dimensions,
179                                             info,
180                                             std::move(mutableState),
181                                             image,
182                                             alloc,
183                                             Ownership::kWrapped,
184                                             skgpu::Budgeted::kNo,
185                                             std::move(ycbcrConversion)));
186 }
187 
vk_format_to_aspect_flags(VkFormat format)188 VkImageAspectFlags vk_format_to_aspect_flags(VkFormat format) {
189     switch (format) {
190         case VK_FORMAT_S8_UINT:
191             return VK_IMAGE_ASPECT_STENCIL_BIT;
192         case VK_FORMAT_D16_UNORM:
193             [[fallthrough]];
194         case VK_FORMAT_D32_SFLOAT:
195             return VK_IMAGE_ASPECT_DEPTH_BIT;
196         case VK_FORMAT_D24_UNORM_S8_UINT:
197             [[fallthrough]];
198         case VK_FORMAT_D32_SFLOAT_S8_UINT:
199             return VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
200         default:
201             return VK_IMAGE_ASPECT_COLOR_BIT;
202     }
203 }
204 
setImageLayoutAndQueueIndex(VulkanCommandBuffer * cmdBuffer,VkImageLayout newLayout,VkAccessFlags dstAccessMask,VkPipelineStageFlags dstStageMask,bool byRegion,uint32_t newQueueFamilyIndex) const205 void VulkanTexture::setImageLayoutAndQueueIndex(VulkanCommandBuffer* cmdBuffer,
206                                                 VkImageLayout newLayout,
207                                                 VkAccessFlags dstAccessMask,
208                                                 VkPipelineStageFlags dstStageMask,
209                                                 bool byRegion,
210                                                 uint32_t newQueueFamilyIndex) const {
211 
212     SkASSERT(newLayout == this->currentLayout() ||
213              (VK_IMAGE_LAYOUT_UNDEFINED != newLayout &&
214               VK_IMAGE_LAYOUT_PREINITIALIZED != newLayout));
215     VkImageLayout currentLayout = this->currentLayout();
216     uint32_t currentQueueIndex = this->currentQueueFamilyIndex();
217 
218     VulkanTextureInfo textureInfo;
219     this->textureInfo().getVulkanTextureInfo(&textureInfo);
220     auto sharedContext = static_cast<const VulkanSharedContext*>(this->sharedContext());
221 
222     // Enable the following block on new devices to test that their lazy images stay at 0 memory use
223 #if 0
224     auto device = sharedContext->device();
225     if (fAlloc.fFlags & skgpu::VulkanAlloc::kLazilyAllocated_Flag) {
226         VkDeviceSize size;
227         VULKAN_CALL(sharedContext->interface(), GetDeviceMemoryCommitment(device, fAlloc.fMemory, &size));
228 
229         SkDebugf("Lazy Image. This: %p, image: %d, size: %d\n", this, fImage, size);
230     }
231 #endif
232 #ifdef SK_DEBUG
233     if (textureInfo.fSharingMode == VK_SHARING_MODE_CONCURRENT) {
234         if (newQueueFamilyIndex == VK_QUEUE_FAMILY_IGNORED) {
235             SkASSERT(currentQueueIndex == VK_QUEUE_FAMILY_IGNORED ||
236                      currentQueueIndex == VK_QUEUE_FAMILY_EXTERNAL ||
237                      currentQueueIndex == VK_QUEUE_FAMILY_FOREIGN_EXT);
238         } else {
239             SkASSERT(newQueueFamilyIndex == VK_QUEUE_FAMILY_EXTERNAL ||
240                      newQueueFamilyIndex == VK_QUEUE_FAMILY_FOREIGN_EXT);
241             SkASSERT(currentQueueIndex == VK_QUEUE_FAMILY_IGNORED);
242         }
243     } else {
244         SkASSERT(textureInfo.fSharingMode == VK_SHARING_MODE_EXCLUSIVE);
245         if (newQueueFamilyIndex == VK_QUEUE_FAMILY_IGNORED ||
246             currentQueueIndex == sharedContext->queueIndex()) {
247             SkASSERT(currentQueueIndex == VK_QUEUE_FAMILY_IGNORED ||
248                      currentQueueIndex == VK_QUEUE_FAMILY_EXTERNAL ||
249                      currentQueueIndex == VK_QUEUE_FAMILY_FOREIGN_EXT ||
250                      currentQueueIndex == sharedContext->queueIndex());
251         } else if (newQueueFamilyIndex == VK_QUEUE_FAMILY_EXTERNAL ||
252                    newQueueFamilyIndex == VK_QUEUE_FAMILY_FOREIGN_EXT) {
253             SkASSERT(currentQueueIndex == VK_QUEUE_FAMILY_IGNORED ||
254                      currentQueueIndex == sharedContext->queueIndex());
255         }
256     }
257 #endif
258 
259     if (textureInfo.fSharingMode == VK_SHARING_MODE_EXCLUSIVE) {
260         if (newQueueFamilyIndex == VK_QUEUE_FAMILY_IGNORED) {
261             newQueueFamilyIndex = sharedContext->queueIndex();
262         }
263         if (currentQueueIndex == VK_QUEUE_FAMILY_IGNORED) {
264             currentQueueIndex = sharedContext->queueIndex();
265         }
266     }
267 
268     // If the old and new layout are the same and the layout is a read only layout, there is no need
269     // to put in a barrier unless we also need to switch queues.
270     if (newLayout == currentLayout && currentQueueIndex == newQueueFamilyIndex &&
271         (VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL == currentLayout ||
272          VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL == currentLayout ||
273          VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL == currentLayout)) {
274         return;
275     }
276 
277     VkAccessFlags srcAccessMask = VulkanTexture::LayoutToSrcAccessMask(currentLayout);
278     VkPipelineStageFlags srcStageMask = VulkanTexture::LayoutToPipelineSrcStageFlags(currentLayout);
279 
280     VkImageAspectFlags aspectFlags = vk_format_to_aspect_flags(textureInfo.fFormat);
281     uint32_t numMipLevels = 1;
282     SkISize dimensions = this->dimensions();
283     if (this->mipmapped() == Mipmapped::kYes) {
284         numMipLevels = SkMipmap::ComputeLevelCount(dimensions.width(), dimensions.height()) + 1;
285     }
286     VkImageMemoryBarrier imageMemoryBarrier = {
287         VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,          // sType
288         nullptr,                                         // pNext
289         srcAccessMask,                                   // srcAccessMask
290         dstAccessMask,                                   // dstAccessMask
291         currentLayout,                                   // oldLayout
292         newLayout,                                       // newLayout
293         currentQueueIndex,                               // srcQueueFamilyIndex
294         newQueueFamilyIndex,                             // dstQueueFamilyIndex
295         fImage,                                          // image
296         { aspectFlags, 0, numMipLevels, 0, 1 }           // subresourceRange
297     };
298     SkASSERT(srcAccessMask == imageMemoryBarrier.srcAccessMask);
299     cmdBuffer->addImageMemoryBarrier(this, srcStageMask, dstStageMask, byRegion,
300                                      &imageMemoryBarrier);
301 
302     skgpu::MutableTextureStates::SetVkImageLayout(this->mutableState(), newLayout);
303     skgpu::MutableTextureStates::SetVkQueueFamilyIndex(this->mutableState(), newQueueFamilyIndex);
304 }
305 
VulkanTexture(const VulkanSharedContext * sharedContext,SkISize dimensions,const TextureInfo & info,sk_sp<MutableTextureState> mutableState,VkImage image,const VulkanAlloc & alloc,Ownership ownership,skgpu::Budgeted budgeted,sk_sp<VulkanYcbcrConversion> ycbcrConversion)306 VulkanTexture::VulkanTexture(const VulkanSharedContext* sharedContext,
307                              SkISize dimensions,
308                              const TextureInfo& info,
309                              sk_sp<MutableTextureState> mutableState,
310                              VkImage image,
311                              const VulkanAlloc& alloc,
312                              Ownership ownership,
313                              skgpu::Budgeted budgeted,
314                              sk_sp<VulkanYcbcrConversion> ycbcrConversion)
315         : Texture(sharedContext, dimensions, info, std::move(mutableState), ownership, budgeted)
316         , fImage(image)
317         , fMemoryAlloc(alloc)
318         , fYcbcrConversion(std::move(ycbcrConversion)) {}
319 
freeGpuData()320 void VulkanTexture::freeGpuData() {
321     // Need to delete any ImageViews first
322     fImageViews.clear();
323 
324     // If the texture is wrapped we don't own this data
325     if (this->ownership() != Ownership::kWrapped) {
326         auto sharedContext = static_cast<const VulkanSharedContext*>(this->sharedContext());
327         VULKAN_CALL(sharedContext->interface(),
328                     DestroyImage(sharedContext->device(), fImage, nullptr));
329         skgpu::VulkanMemory::FreeImageMemory(sharedContext->memoryAllocator(), fMemoryAlloc);
330     }
331 }
332 
updateImageLayout(VkImageLayout newLayout)333 void VulkanTexture::updateImageLayout(VkImageLayout newLayout) {
334     skgpu::MutableTextureStates::SetVkImageLayout(this->mutableState(), newLayout);
335 }
336 
currentLayout() const337 VkImageLayout VulkanTexture::currentLayout() const {
338     return skgpu::MutableTextureStates::GetVkImageLayout(this->mutableState());
339 }
340 
currentQueueFamilyIndex() const341 uint32_t VulkanTexture::currentQueueFamilyIndex() const {
342     return skgpu::MutableTextureStates::GetVkQueueFamilyIndex(this->mutableState());
343 }
344 
LayoutToPipelineSrcStageFlags(const VkImageLayout layout)345 VkPipelineStageFlags VulkanTexture::LayoutToPipelineSrcStageFlags(const VkImageLayout layout) {
346     if (VK_IMAGE_LAYOUT_GENERAL == layout) {
347         return VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
348     } else if (VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL == layout ||
349                VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL == layout) {
350         return VK_PIPELINE_STAGE_TRANSFER_BIT;
351     } else if (VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL == layout) {
352         return VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
353     } else if (VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL == layout ||
354                VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL == layout) {
355         return VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
356     } else if (VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL == layout) {
357         return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
358     } else if (VK_IMAGE_LAYOUT_PREINITIALIZED == layout) {
359         return VK_PIPELINE_STAGE_HOST_BIT;
360     } else if (VK_IMAGE_LAYOUT_PRESENT_SRC_KHR == layout) {
361         return VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
362     }
363 
364     SkASSERT(VK_IMAGE_LAYOUT_UNDEFINED == layout);
365     return VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
366 }
367 
LayoutToSrcAccessMask(const VkImageLayout layout)368 VkAccessFlags VulkanTexture::LayoutToSrcAccessMask(const VkImageLayout layout) {
369     // Currently we assume we will never being doing any explict shader writes (this doesn't include
370     // color attachment or depth/stencil writes). So we will ignore the
371     // VK_MEMORY_OUTPUT_SHADER_WRITE_BIT.
372 
373     // We can only directly access the host memory if we are in preinitialized or general layout,
374     // and the image is linear.
375     // TODO: Add check for linear here so we are not always adding host to general, and we should
376     //       only be in preinitialized if we are linear
377     VkAccessFlags flags = 0;
378     if (VK_IMAGE_LAYOUT_GENERAL == layout) {
379         flags = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
380                 VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
381                 VK_ACCESS_TRANSFER_WRITE_BIT |
382                 VK_ACCESS_HOST_WRITE_BIT;
383     } else if (VK_IMAGE_LAYOUT_PREINITIALIZED == layout) {
384         flags = VK_ACCESS_HOST_WRITE_BIT;
385     } else if (VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL == layout) {
386         flags = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
387     } else if (VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL == layout) {
388         flags = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
389     } else if (VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL == layout) {
390         flags = VK_ACCESS_TRANSFER_WRITE_BIT;
391     } else if (VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL == layout ||
392                VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL == layout ||
393                VK_IMAGE_LAYOUT_PRESENT_SRC_KHR == layout) {
394         // There are no writes that need to be made available
395         flags = 0;
396     }
397     return flags;
398 }
399 
getImageView(VulkanImageView::Usage usage) const400 const VulkanImageView* VulkanTexture::getImageView(VulkanImageView::Usage usage) const {
401     for (int i = 0; i < fImageViews.size(); ++i) {
402         if (fImageViews[i]->usage() == usage) {
403             return fImageViews[i].get();
404         }
405     }
406 
407     auto sharedContext = static_cast<const VulkanSharedContext*>(this->sharedContext());
408     VulkanTextureInfo vkTexInfo;
409     this->textureInfo().getVulkanTextureInfo(&vkTexInfo);
410     int miplevels = this->textureInfo().mipmapped() == Mipmapped::kYes
411                     ? SkMipmap::ComputeLevelCount(this->dimensions().width(),
412                                                   this->dimensions().height()) + 1
413                     : 1;
414     auto imageView = VulkanImageView::Make(sharedContext,
415                                            fImage,
416                                            vkTexInfo.fFormat,
417                                            usage,
418                                            miplevels,
419                                            fYcbcrConversion);
420     return fImageViews.push_back(std::move(imageView)).get();
421 }
422 
423 
424 } // namespace skgpu::graphite
425