Flutter Engine
The Flutter Engine
VkYcbcrSamplerHelper.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2020 Google Inc.
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
9
10#ifdef SK_VULKAN
11
19
20#if defined(SK_GRAPHITE)
27#endif
28
29int VkYcbcrSamplerHelper::GetExpectedY(int x, int y, int width, int height) {
30 return 16 + (x + y) * 219 / (width + height - 2);
31}
32
33std::pair<int, int> VkYcbcrSamplerHelper::GetExpectedUV(int x, int y, int width, int height) {
34 return { 16 + x * 224 / (width - 1), 16 + y * 224 / (height - 1) };
35}
36
37namespace {
38
39void populate_ycbcr_image_info(VkImageCreateInfo* outImageInfo, uint32_t width, uint32_t height) {
41 outImageInfo->pNext = nullptr;
42 outImageInfo->flags = 0;
43 outImageInfo->imageType = VK_IMAGE_TYPE_2D;
45 outImageInfo->extent = VkExtent3D{width, height, 1};
46 outImageInfo->mipLevels = 1;
47 outImageInfo->arrayLayers = 1;
48 outImageInfo->samples = VK_SAMPLE_COUNT_1_BIT;
49 outImageInfo->tiling = VK_IMAGE_TILING_LINEAR;
53 outImageInfo->queueFamilyIndexCount = 0;
54 outImageInfo->pQueueFamilyIndices = nullptr;
56}
57
58bool find_memory_type_index(const VkPhysicalDeviceMemoryProperties& phyDevMemProps,
59 const VkMemoryRequirements& memoryRequirements,
60 uint32_t* memoryTypeIndex) {
61 for (uint32_t i = 0; i < phyDevMemProps.memoryTypeCount; ++i) {
62 if (memoryRequirements.memoryTypeBits & (1 << i)) {
63 // Map host-visible memory.
65 *memoryTypeIndex = i;
66 return true;
67 }
68 }
69 }
70 return false;
71}
72
73}
74
75#ifdef SK_GRAPHITE
76// TODO(b/339211930): When graphite and ganesh can share a macro for certain Vulkan driver calls,
77// much more code can be shared between this method and createGrBackendTexture.
78bool VkYcbcrSamplerHelper::createBackendTexture(uint32_t width, uint32_t height) {
79 // Create YCbCr image.
80 VkImageCreateInfo vkImageInfo;
81 populate_ycbcr_image_info(&vkImageInfo, width, height);
82 SkASSERT(fImage == VK_NULL_HANDLE);
83
85 VULKAN_CALL_RESULT(fSharedCtxt, result, CreateImage(fSharedCtxt->device(),
86 &vkImageInfo,
87 /*pAllocator=*/nullptr,
88 &fImage));
89 if (result != VK_SUCCESS) {
90 return false;
91 }
92
93 VkMemoryRequirements requirements;
94 VULKAN_CALL(fSharedCtxt->interface(), GetImageMemoryRequirements(fSharedCtxt->device(),
95 fImage,
96 &requirements));
97 uint32_t memoryTypeIndex = 0;
98 const VkPhysicalDeviceMemoryProperties& phyDevMemProps =
99 fSharedCtxt->vulkanCaps().physicalDeviceMemoryProperties2().memoryProperties;
100 if (!find_memory_type_index(phyDevMemProps, requirements, &memoryTypeIndex)) {
101 return false;
102 }
103
104 VkMemoryAllocateInfo allocInfo;
106 allocInfo.pNext = nullptr;
107 allocInfo.allocationSize = requirements.size;
108 allocInfo.memoryTypeIndex = memoryTypeIndex;
109
110 SkASSERT(fImageMemory == VK_NULL_HANDLE);
111 VULKAN_CALL_RESULT(fSharedCtxt, result, AllocateMemory(fSharedCtxt->device(),
112 &allocInfo,
113 nullptr,
114 &fImageMemory));
115 if (result != VK_SUCCESS) {
116 return false;
117 }
118
119 void* mappedBuffer;
120 VULKAN_CALL_RESULT(fSharedCtxt, result, MapMemory(fSharedCtxt->device(),
121 fImageMemory,
122 /*offset=*/0u,
123 requirements.size,
124 /*flags=*/0u,
125 &mappedBuffer));
126 if (result != VK_SUCCESS) {
127 return false;
128 }
129
130 // Write Y channel.
131 VkImageSubresource subresource;
133 subresource.mipLevel = 0;
134 subresource.arrayLayer = 0;
135
136 VkSubresourceLayout yLayout;
137 VULKAN_CALL(fSharedCtxt->interface(),
138 GetImageSubresourceLayout(fSharedCtxt->device(), fImage, &subresource, &yLayout));
139 uint8_t* bufferData = reinterpret_cast<uint8_t*>(mappedBuffer) + yLayout.offset;
140 for (size_t y = 0; y < height; ++y) {
141 for (size_t x = 0; x < width; ++x) {
142 bufferData[y * yLayout.rowPitch + x] = GetExpectedY(x, y, width, height);
143 }
144 }
145
146 // Write UV channels.
148 VkSubresourceLayout uvLayout;
149 VULKAN_CALL(fSharedCtxt->interface(), GetImageSubresourceLayout(fSharedCtxt->device(),
150 fImage,
151 &subresource,
152 &uvLayout));
153 bufferData = reinterpret_cast<uint8_t*>(mappedBuffer) + uvLayout.offset;
154 for (size_t y = 0; y < height / 2; ++y) {
155 for (size_t x = 0; x < width / 2; ++x) {
156 auto [u, v] = GetExpectedUV(2*x, 2*y, width, height);
157 bufferData[y * uvLayout.rowPitch + x * 2] = u;
158 bufferData[y * uvLayout.rowPitch + x * 2 + 1] = v;
159 }
160 }
161
162 VkMappedMemoryRange flushRange;
164 flushRange.pNext = nullptr;
165 flushRange.memory = fImageMemory;
166 flushRange.offset = 0;
167 flushRange.size = VK_WHOLE_SIZE;
168 VULKAN_CALL_RESULT(fSharedCtxt, result, FlushMappedMemoryRanges(fSharedCtxt->device(),
169 /*memoryRangeCount=*/1,
170 &flushRange));
171 if (result != VK_SUCCESS) {
172 return false;
173 }
174 VULKAN_CALL(fSharedCtxt->interface(), UnmapMemory(fSharedCtxt->device(), fImageMemory));
175
176 // Bind image memory.
177 VULKAN_CALL_RESULT(fSharedCtxt, result, BindImageMemory(fSharedCtxt->device(),
178 fImage,
179 fImageMemory,
180 /*memoryOffset=*/0u));
181 if (result != VK_SUCCESS) {
182 return false;
183 }
184
185 // Wrap the image into SkImage.
186 VkFormatProperties formatProperties;
187 SkASSERT(fPhysDev != VK_NULL_HANDLE);
188 VULKAN_CALL(fSharedCtxt->interface(),
189 GetPhysicalDeviceFormatProperties(fPhysDev,
191 &formatProperties));
192 SkDEBUGCODE(auto linFlags = formatProperties.linearTilingFeatures;)
197
198 skgpu::VulkanYcbcrConversionInfo ycbcrInfo = {vkImageInfo.format,
199 /*externalFormat=*/0,
205 false,
206 formatProperties.linearTilingFeatures};
207 skgpu::VulkanAlloc alloc;
208 alloc.fMemory = fImageMemory;
209 alloc.fOffset = 0;
210 alloc.fSize = requirements.size;
211
213 static_cast<uint32_t>(vkImageInfo.samples),
216 vkImageInfo.format,
217 vkImageInfo.tiling,
218 vkImageInfo.usage,
219 vkImageInfo.sharingMode,
221 ycbcrInfo};
222
223 fTexture = skgpu::graphite::BackendTexture{{(int32_t)width, (int32_t)height},
224 imageInfo,
226 /*queueFamilyIndex=*/0,
227 fImage,
228 alloc};
229 return true;
230}
231#endif // SK_GRAPHITE
232
233bool VkYcbcrSamplerHelper::createGrBackendTexture(uint32_t width, uint32_t height) {
234 GrVkGpu* vkGpu = this->vkGpu();
236
237 // Create YCbCr image.
238 VkImageCreateInfo vkImageInfo;
239 populate_ycbcr_image_info(&vkImageInfo, width, height);
240 SkASSERT(fImage == VK_NULL_HANDLE);
241
242 GR_VK_CALL_RESULT(vkGpu, result, CreateImage(vkGpu->device(), &vkImageInfo, nullptr, &fImage));
243 if (result != VK_SUCCESS) {
244 return false;
245 }
246
247 VkMemoryRequirements requirements;
248 GR_VK_CALL(vkGpu->vkInterface(), GetImageMemoryRequirements(vkGpu->device(),
249 fImage,
250 &requirements));
251
252 uint32_t memoryTypeIndex = 0;
254 GR_VK_CALL(vkGpu->vkInterface(), GetPhysicalDeviceMemoryProperties(vkGpu->physicalDevice(),
255 &phyDevMemProps));
256 if (!find_memory_type_index(phyDevMemProps, requirements, &memoryTypeIndex)) {
257 return false;
258 }
259
260 VkMemoryAllocateInfo allocInfo = {};
262 allocInfo.allocationSize = requirements.size;
263 allocInfo.memoryTypeIndex = memoryTypeIndex;
264
265 SkASSERT(fImageMemory == VK_NULL_HANDLE);
266 GR_VK_CALL_RESULT(vkGpu, result, AllocateMemory(vkGpu->device(), &allocInfo,
267 nullptr, &fImageMemory));
268 if (result != VK_SUCCESS) {
269 return false;
270 }
271
272 void* mappedBuffer;
273 GR_VK_CALL_RESULT(vkGpu, result, MapMemory(vkGpu->device(), fImageMemory, 0u,
274 requirements.size, 0u, &mappedBuffer));
275 if (result != VK_SUCCESS) {
276 return false;
277 }
278
279 // Write Y channel.
280 VkImageSubresource subresource;
282 subresource.mipLevel = 0;
283 subresource.arrayLayer = 0;
284
285 VkSubresourceLayout yLayout;
286 GR_VK_CALL(vkGpu->vkInterface(), GetImageSubresourceLayout(vkGpu->device(), fImage,
287 &subresource, &yLayout));
288 uint8_t* bufferData = reinterpret_cast<uint8_t*>(mappedBuffer) + yLayout.offset;
289 for (size_t y = 0; y < height; ++y) {
290 for (size_t x = 0; x < width; ++x) {
291 bufferData[y * yLayout.rowPitch + x] = GetExpectedY(x, y, width, height);
292 }
293 }
294
295 // Write UV channels.
297 VkSubresourceLayout uvLayout;
298 GR_VK_CALL(vkGpu->vkInterface(), GetImageSubresourceLayout(vkGpu->device(), fImage,
299 &subresource, &uvLayout));
300 bufferData = reinterpret_cast<uint8_t*>(mappedBuffer) + uvLayout.offset;
301 for (size_t y = 0; y < height / 2; ++y) {
302 for (size_t x = 0; x < width / 2; ++x) {
303 auto [u, v] = GetExpectedUV(2*x, 2*y, width, height);
304 bufferData[y * uvLayout.rowPitch + x * 2] = u;
305 bufferData[y * uvLayout.rowPitch + x * 2 + 1] = v;
306 }
307 }
308
309 VkMappedMemoryRange flushRange;
311 flushRange.pNext = nullptr;
312 flushRange.memory = fImageMemory;
313 flushRange.offset = 0;
314 flushRange.size = VK_WHOLE_SIZE;
315 GR_VK_CALL_RESULT(vkGpu, result, FlushMappedMemoryRanges(vkGpu->device(), 1, &flushRange));
316 if (result != VK_SUCCESS) {
317 return false;
318 }
319 GR_VK_CALL(vkGpu->vkInterface(), UnmapMemory(vkGpu->device(), fImageMemory));
320
321 // Bind image memory.
322 GR_VK_CALL_RESULT(vkGpu, result, BindImageMemory(vkGpu->device(), fImage, fImageMemory, 0u));
323 if (result != VK_SUCCESS) {
324 return false;
325 }
326
327 // Wrap the image into SkImage.
328 VkFormatProperties formatProperties;
329 GR_VK_CALL(vkGpu->vkInterface(),
330 GetPhysicalDeviceFormatProperties(vkGpu->physicalDevice(),
332 &formatProperties));
333 SkDEBUGCODE(auto linFlags = formatProperties.linearTilingFeatures;)
338
339 skgpu::VulkanYcbcrConversionInfo ycbcrInfo = {vkImageInfo.format,
340 /*externalFormat=*/0,
346 false,
347 formatProperties.linearTilingFeatures,
348 /*fComponents=*/{}};
349 skgpu::VulkanAlloc alloc;
350 alloc.fMemory = fImageMemory;
351 alloc.fOffset = 0;
352 alloc.fSize = requirements.size;
353
354 GrVkImageInfo imageInfo = {fImage,
355 alloc,
358 vkImageInfo.format,
359 vkImageInfo.usage,
360 1 /* sample count */,
361 1 /* levelCount */,
364 ycbcrInfo};
365
366 fGrTexture = GrBackendTextures::MakeVk(width, height, imageInfo);
367 return true;
368}
369
370GrVkGpu* VkYcbcrSamplerHelper::vkGpu() {
371 return (GrVkGpu*) fDContext->priv().getGpu();
372}
373
374VkYcbcrSamplerHelper::VkYcbcrSamplerHelper(GrDirectContext* dContext) : fDContext(dContext) {
376}
377
378VkYcbcrSamplerHelper::~VkYcbcrSamplerHelper() {
379#ifdef SK_GRAPHITE
380 if (fSharedCtxt) {
381 if (fImage != VK_NULL_HANDLE) {
382 VULKAN_CALL(fSharedCtxt->interface(),
383 DestroyImage(fSharedCtxt->device(), fImage, nullptr));
384 fImage = VK_NULL_HANDLE;
385 }
386 if (fImageMemory != VK_NULL_HANDLE) {
387 VULKAN_CALL(fSharedCtxt->interface(),
388 FreeMemory(fSharedCtxt->device(), fImageMemory, nullptr));
389 fImageMemory = VK_NULL_HANDLE;
390 }
391 } else
392#endif // SK_GRAPHITE
393 {
394 GrVkGpu* vkGpu = this->vkGpu();
395
396 if (fImage != VK_NULL_HANDLE) {
397 GR_VK_CALL(vkGpu->vkInterface(), DestroyImage(vkGpu->device(), fImage, nullptr));
398 fImage = VK_NULL_HANDLE;
399 }
400 if (fImageMemory != VK_NULL_HANDLE) {
401 GR_VK_CALL(vkGpu->vkInterface(), FreeMemory(vkGpu->device(), fImageMemory, nullptr));
402 fImageMemory = VK_NULL_HANDLE;
403 }
404 }
405}
406
407bool VkYcbcrSamplerHelper::isYCbCrSupported() {
408 VkFormatProperties formatProperties;
409#ifdef SK_GRAPHITE
410 if (fSharedCtxt) {
411 if (!fSharedCtxt->vulkanCaps().supportsYcbcrConversion()) {
412 return false;
413 }
414
415 SkASSERT(fPhysDev != VK_NULL_HANDLE);
416 VULKAN_CALL(fSharedCtxt->interface(),
417 GetPhysicalDeviceFormatProperties(fPhysDev,
419 &formatProperties));
420 } else
421#endif
422 {
423 GrVkGpu* vkGpu = this->vkGpu();
424 if (!vkGpu->vkCaps().supportsYcbcrConversion()) {
425 return false;
426 }
427
428 GR_VK_CALL(vkGpu->vkInterface(),
429 GetPhysicalDeviceFormatProperties(vkGpu->physicalDevice(),
431 &formatProperties));
432 }
433
434 // The createBackendTexture call (which is the point of this helper class) requires linear
435 // support for VK_FORMAT_G8_B8R8_2PLANE_420_UNORM including sampling and cosited chroma.
436 // Verify that the image format is supported.
437 auto linFlags = formatProperties.linearTilingFeatures;
438 if (!(linFlags & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) ||
442 // VK_FORMAT_G8_B8R8_2PLANE_420_UNORM is not supported
443 return false;
444 }
445 return true;
446}
447#endif // SK_VULKAN
for(const auto glyph :glyphs)
Definition: FontMgrTest.cpp:52
#define GR_VK_CALL(IFACE, X)
Definition: GrVkUtil.h:24
#define GR_VK_CALL_RESULT(GPU, RESULT, X)
Definition: GrVkUtil.h:35
#define SkASSERT_RELEASE(cond)
Definition: SkAssert.h:100
#define SkASSERT(cond)
Definition: SkAssert.h:116
SkDEBUGCODE(SK_SPI) SkThreadID SkGetThreadID()
GrDirectContext * fDContext
#define VULKAN_CALL(IFACE, X)
#define VULKAN_CALL_RESULT(SHARED_CONTEXT, RESULT, X)
SK_API GrBackendApi backend() const
GrDirectContextPriv priv()
bool supportsYcbcrConversion() const
Definition: GrVkCaps.h:153
const GrVkCaps & vkCaps() const
Definition: GrVkGpu.h:61
const skgpu::VulkanInterface * vkInterface() const
Definition: GrVkGpu.h:60
VkDevice device() const
Definition: GrVkGpu.h:71
VkPhysicalDevice physicalDevice() const
Definition: GrVkGpu.h:70
GAsyncResult * result
double y
double x
SK_API GrBackendTexture MakeVk(int width, int height, const GrVkImageInfo &, std::string_view label={})
Definition: GpuTools.h:21
int32_t height
int32_t width
VkFormatFeatureFlags linearTilingFeatures
Definition: vulkan_core.h:3013
VkImageCreateFlags flags
Definition: vulkan_core.h:3436
VkSharingMode sharingMode
Definition: vulkan_core.h:3445
VkImageLayout initialLayout
Definition: vulkan_core.h:3448
const void * pNext
Definition: vulkan_core.h:3435
const uint32_t * pQueueFamilyIndices
Definition: vulkan_core.h:3447
uint32_t arrayLayers
Definition: vulkan_core.h:3441
VkSampleCountFlagBits samples
Definition: vulkan_core.h:3442
VkExtent3D extent
Definition: vulkan_core.h:3439
uint32_t queueFamilyIndexCount
Definition: vulkan_core.h:3446
VkImageType imageType
Definition: vulkan_core.h:3437
VkImageTiling tiling
Definition: vulkan_core.h:3443
VkStructureType sType
Definition: vulkan_core.h:3434
VkImageUsageFlags usage
Definition: vulkan_core.h:3444
VkImageAspectFlags aspectMask
Definition: vulkan_core.h:3336
const void * pNext
Definition: vulkan_core.h:3296
VkDeviceSize offset
Definition: vulkan_core.h:3298
VkDeviceMemory memory
Definition: vulkan_core.h:3297
VkDeviceSize size
Definition: vulkan_core.h:3299
VkStructureType sType
Definition: vulkan_core.h:3295
VkStructureType sType
Definition: vulkan_core.h:3303
VkDeviceSize allocationSize
Definition: vulkan_core.h:3305
const void * pNext
Definition: vulkan_core.h:3304
VkMemoryPropertyFlags propertyFlags
Definition: vulkan_core.h:3043
VkMemoryType memoryTypes[VK_MAX_MEMORY_TYPES]
Definition: vulkan_core.h:3216
VkDeviceSize rowPitch
Definition: vulkan_core.h:3454
VkDeviceSize offset
Definition: vulkan_core.h:3452
VkDeviceSize fSize
Definition: VulkanTypes.h:41
VkDeviceMemory fMemory
Definition: VulkanTypes.h:39
VkDeviceSize fOffset
Definition: VulkanTypes.h:40
@ VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT
Definition: vulkan_core.h:2271
@ VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT
Definition: vulkan_core.h:2259
@ VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT
Definition: vulkan_core.h:2280
@ VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT
Definition: vulkan_core.h:2275
@ VK_IMAGE_LAYOUT_UNDEFINED
Definition: vulkan_core.h:1331
@ VK_SHARING_MODE_EXCLUSIVE
Definition: vulkan_core.h:1813
@ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
Definition: vulkan_core.h:2400
@ VK_IMAGE_CREATE_PROTECTED_BIT
Definition: vulkan_core.h:2320
@ VK_IMAGE_TILING_LINEAR
Definition: vulkan_core.h:1768
@ VK_IMAGE_ASPECT_PLANE_0_BIT
Definition: vulkan_core.h:2242
@ VK_IMAGE_ASPECT_PLANE_1_BIT
Definition: vulkan_core.h:2243
@ VK_IMAGE_USAGE_TRANSFER_DST_BIT
Definition: vulkan_core.h:2353
@ VK_IMAGE_USAGE_SAMPLED_BIT
Definition: vulkan_core.h:2354
@ VK_IMAGE_USAGE_TRANSFER_SRC_BIT
Definition: vulkan_core.h:2352
@ VK_SAMPLE_COUNT_1_BIT
Definition: vulkan_core.h:2340
@ VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709
Definition: vulkan_core.h:4948
@ VK_CHROMA_LOCATION_COSITED_EVEN
Definition: vulkan_core.h:4968
@ VK_IMAGE_TYPE_2D
Definition: vulkan_core.h:1775
@ VK_SAMPLER_YCBCR_RANGE_ITU_NARROW
Definition: vulkan_core.h:4961
#define VK_WHOLE_SIZE
Definition: vulkan_core.h:132
@ VK_FILTER_LINEAR
Definition: vulkan_core.h:2102
VkResult
Definition: vulkan_core.h:140
@ VK_SUCCESS
Definition: vulkan_core.h:141
#define VK_NULL_HANDLE
Definition: vulkan_core.h:46
@ VK_FORMAT_G8_B8R8_2PLANE_420_UNORM
Definition: vulkan_core.h:1647
#define VK_QUEUE_FAMILY_IGNORED
Definition: vulkan_core.h:127
@ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO
Definition: vulkan_core.h:216
@ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO
Definition: vulkan_core.h:207
@ VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE
Definition: vulkan_core.h:208