Flutter Engine
The Flutter Engine
VulkanResourceProvider.cpp
Go to the documentation of this file.
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
9
10#include "include/core/SkSpan.h"
34
35#ifdef SK_BUILD_FOR_ANDROID
38#endif
39
40namespace skgpu::graphite {
41
43
45 SingleOwner* singleOwner,
46 uint32_t recorderID,
47 size_t resourceBudget,
48 sk_sp<Buffer> intrinsicConstantUniformBuffer,
49 sk_sp<Buffer> loadMSAAVertexBuffer)
50 : ResourceProvider(sharedContext, singleOwner, recorderID, resourceBudget)
51 , fIntrinsicUniformBuffer(std::move(intrinsicConstantUniformBuffer))
52 , fLoadMSAAVertexBuffer(std::move(loadMSAAVertexBuffer))
53 , fUniformBufferDescSetCache(kMaxNumberOfCachedBufferDescSets) {}
54
56 if (fPipelineCache != VK_NULL_HANDLE) {
57 VULKAN_CALL(this->vulkanSharedContext()->interface(),
58 DestroyPipelineCache(this->vulkanSharedContext()->device(),
59 fPipelineCache,
60 nullptr));
61 }
62 if (fMSAALoadVertShaderModule != VK_NULL_HANDLE) {
63 VULKAN_CALL(this->vulkanSharedContext()->interface(),
64 DestroyShaderModule(this->vulkanSharedContext()->device(),
65 fMSAALoadVertShaderModule,
66 nullptr));
67 }
68 if (fMSAALoadFragShaderModule != VK_NULL_HANDLE) {
69 VULKAN_CALL(this->vulkanSharedContext()->interface(),
70 DestroyShaderModule(this->vulkanSharedContext()->device(),
71 fMSAALoadFragShaderModule,
72 nullptr));
73 }
74 if (fMSAALoadPipelineLayout != VK_NULL_HANDLE) {
75 VULKAN_CALL(this->vulkanSharedContext()->interface(),
76 DestroyPipelineLayout(this->vulkanSharedContext()->device(),
77 fMSAALoadPipelineLayout,
78 nullptr));
79 }
80}
81
82const VulkanSharedContext* VulkanResourceProvider::vulkanSharedContext() const {
83 return static_cast<const VulkanSharedContext*>(fSharedContext);
84}
85
86sk_sp<Texture> VulkanResourceProvider::onCreateWrappedTexture(const BackendTexture& texture) {
87 sk_sp<VulkanYcbcrConversion> ycbcrConversion;
88 if (texture.info().vulkanTextureSpec().fYcbcrConversionInfo.isValid()) {
89 ycbcrConversion = this->findOrCreateCompatibleYcbcrConversion(
90 texture.info().vulkanTextureSpec().fYcbcrConversionInfo);
91 if (!ycbcrConversion) {
92 return nullptr;
93 }
94 }
95
96 return VulkanTexture::MakeWrapped(this->vulkanSharedContext(),
97 texture.dimensions(),
98 texture.info(),
99 texture.getMutableState(),
100 texture.getVkImage(),
101 /*alloc=*/{} /*Skia does not own wrapped texture memory*/,
102 std::move(ycbcrConversion));
103}
104
106 return fIntrinsicUniformBuffer;
107}
108
110 return fLoadMSAAVertexBuffer.get();
111}
112
113sk_sp<GraphicsPipeline> VulkanResourceProvider::createGraphicsPipeline(
114 const RuntimeEffectDictionary* runtimeDict,
115 const GraphicsPipelineDesc& pipelineDesc,
116 const RenderPassDesc& renderPassDesc) {
117 auto compatibleRenderPass =
118 this->findOrCreateRenderPass(renderPassDesc, /*compatibleOnly=*/true);
119 return VulkanGraphicsPipeline::Make(this->vulkanSharedContext(),
120 runtimeDict,
121 pipelineDesc,
122 renderPassDesc,
123 compatibleRenderPass,
124 this->pipelineCache());
125}
126
127sk_sp<ComputePipeline> VulkanResourceProvider::createComputePipeline(const ComputePipelineDesc&) {
128 return nullptr;
129}
130
131sk_sp<Texture> VulkanResourceProvider::createTexture(SkISize size,
132 const TextureInfo& info,
133 skgpu::Budgeted budgeted) {
134 sk_sp<VulkanYcbcrConversion> ycbcrConversion;
135 if (info.vulkanTextureSpec().fYcbcrConversionInfo.isValid()) {
136 ycbcrConversion = this->findOrCreateCompatibleYcbcrConversion(
137 info.vulkanTextureSpec().fYcbcrConversionInfo);
138 if (!ycbcrConversion) {
139 return nullptr;
140 }
141 }
142
143 return VulkanTexture::Make(this->vulkanSharedContext(),
144 size,
145 info,
146 budgeted,
147 std::move(ycbcrConversion));
148}
149
150sk_sp<Buffer> VulkanResourceProvider::createBuffer(size_t size,
152 AccessPattern accessPattern) {
153 return VulkanBuffer::Make(this->vulkanSharedContext(), size, type, accessPattern);
154}
155
156sk_sp<Sampler> VulkanResourceProvider::createSampler(const SamplerDesc& samplerDesc) {
157 sk_sp<VulkanYcbcrConversion> ycbcrConversion = nullptr;
158
159 // Non-zero conversion information means the sampler utilizes a ycbcr conversion.
160 bool usesYcbcrConversion = (samplerDesc.desc() >> SamplerDesc::kImmutableSamplerInfoShift) != 0;
161 if (usesYcbcrConversion) {
162 GraphiteResourceKey ycbcrKey = VulkanYcbcrConversion::GetKeyFromSamplerDesc(samplerDesc);
163 if (Resource* resource = fResourceCache->findAndRefResource(ycbcrKey,
165 ycbcrConversion =
166 sk_sp<VulkanYcbcrConversion>(static_cast<VulkanYcbcrConversion*>(resource));
167 } else {
168 ycbcrConversion = VulkanYcbcrConversion::Make(
169 this->vulkanSharedContext(),
170 static_cast<uint32_t>(
171 samplerDesc.desc() >> SamplerDesc::kImmutableSamplerInfoShift),
172 (uint64_t)(samplerDesc.externalFormatMSBs()) << 32 | samplerDesc.format());
173 SkASSERT(ycbcrConversion);
174
175 ycbcrConversion->setKey(ycbcrKey);
176 fResourceCache->insertResource(ycbcrConversion.get());
177 }
178 }
179
180 return VulkanSampler::Make(this->vulkanSharedContext(),
181 samplerDesc.samplingOptions(),
182 samplerDesc.tileModeX(),
183 samplerDesc.tileModeY(),
184 std::move(ycbcrConversion));
185}
186
187BackendTexture VulkanResourceProvider::onCreateBackendTexture(SkISize dimensions,
188 const TextureInfo& info) {
189 VulkanTextureInfo vkTexInfo;
190 if (!info.getVulkanTextureInfo(&vkTexInfo)) {
191 return {};
192 }
193 VulkanTexture::CreatedImageInfo createdTextureInfo;
194 if (!VulkanTexture::MakeVkImage(this->vulkanSharedContext(), dimensions, info,
195 &createdTextureInfo)) {
196 return {};
197 }
198 return {dimensions,
199 vkTexInfo,
200 skgpu::MutableTextureStates::GetVkImageLayout(createdTextureInfo.fMutableState.get()),
201 skgpu::MutableTextureStates::GetVkQueueFamilyIndex(createdTextureInfo.fMutableState.get()),
202 createdTextureInfo.fImage,
203 createdTextureInfo.fMemoryAlloc};
204}
205
206namespace {
207GraphiteResourceKey build_desc_set_key(const SkSpan<DescriptorData>& requestedDescriptors) {
209
210 const int num32DataCnt = requestedDescriptors.size() + 1;
211
212 GraphiteResourceKey key;
214
215 builder[0] = requestedDescriptors.size();
216 for (int i = 1; i < num32DataCnt; i++) {
217 const auto& currDesc = requestedDescriptors[i - 1];
218 // TODO: Consider making the DescriptorData struct itself just use uint16_t.
219 uint16_t smallerCount = static_cast<uint16_t>(currDesc.fCount);
220 builder[i] = static_cast<uint8_t>(currDesc.fType) << 24 |
221 currDesc.fBindingIndex << 16 |
222 smallerCount;
223 }
224 builder.finish();
225 return key;
226}
227
228sk_sp<VulkanDescriptorSet> add_new_desc_set_to_cache(const VulkanSharedContext* context,
230 const GraphiteResourceKey& descSetKey,
231 ResourceCache* resourceCache) {
233 if (!descSet) {
234 return nullptr;
235 }
236 descSet->setKey(descSetKey);
237 resourceCache->insertResource(descSet.get());
238
239 return descSet;
240}
241} // anonymous namespace
242
243sk_sp<VulkanDescriptorSet> VulkanResourceProvider::findOrCreateDescriptorSet(
244 SkSpan<DescriptorData> requestedDescriptors) {
245 if (requestedDescriptors.empty()) {
246 return nullptr;
247 }
248 // Search for available descriptor sets by assembling a key based upon the set's structure.
249 GraphiteResourceKey key = build_desc_set_key(requestedDescriptors);
250 if (auto descSet = fResourceCache->findAndRefResource(key, skgpu::Budgeted::kYes)) {
251 // A non-null resource pointer indicates we have found an available descriptor set.
252 return sk_sp<VulkanDescriptorSet>(static_cast<VulkanDescriptorSet*>(descSet));
253 }
254
255
256 // If we did not find an existing avilable desc set, allocate sets with the appropriate layout
257 // and add them to the cache.
258 VkDescriptorSetLayout layout;
259 const VulkanSharedContext* context = this->vulkanSharedContext();
260 DescriptorDataToVkDescSetLayout(context, requestedDescriptors, &layout);
261 if (!layout) {
262 return nullptr;
263 }
264 auto pool = VulkanDescriptorPool::Make(context, requestedDescriptors, layout);
265 if (!pool) {
266 VULKAN_CALL(context->interface(), DestroyDescriptorSetLayout(context->device(),
267 layout,
268 nullptr));
269 return nullptr;
270 }
271
272 // Start with allocating one descriptor set. If one cannot be successfully created, then we can
273 // return early before attempting to allocate more. Storing a ptr to the first set also
274 // allows us to return that later without having to perform a find operation on the cache once
275 // all the sets are added.
276 auto firstDescSet =
277 add_new_desc_set_to_cache(context, pool, key, fResourceCache.get());
278 if (!firstDescSet) {
279 return nullptr;
280 }
281
282 // Continue to allocate & cache the maximum number of sets so they can be easily accessed as
283 // they're needed.
284 for (int i = 1; i < VulkanDescriptorPool::kMaxNumSets ; i++) {
285 auto descSet =
286 add_new_desc_set_to_cache(context, pool, key, fResourceCache.get());
287 if (!descSet) {
288 SKGPU_LOG_W("Descriptor set allocation %d of %d was unsuccessful; no more sets will be"
289 "allocated from this pool.", i, VulkanDescriptorPool::kMaxNumSets);
290 break;
291 }
292 }
293
294 return firstDescSet;
295}
296
297namespace {
298UniqueKey make_ubo_bind_group_key(SkSpan<DescriptorData> requestedDescriptors,
299 SkSpan<BindUniformBufferInfo> bindUniformBufferInfo) {
300 static const UniqueKey::Domain kBufferBindGroupDomain = UniqueKey::GenerateDomain();
301
302 UniqueKey uniqueKey;
303 {
304 // Each entry in the bind group needs 2 uint32_t in the key:
305 // - buffer's unique ID: 32 bits.
306 // - buffer's binding size: 32 bits.
307 // We need total of 3 entries in the uniform buffer bind group.
308 // Unused entries will be assigned zero values.
310 &uniqueKey, kBufferBindGroupDomain, 6, "GraphicsPipelineBufferDescSet");
311
312 for (uint32_t i = 0; i < VulkanGraphicsPipeline::kNumUniformBuffers; ++i) {
313 builder[2 * i] = 0;
314 builder[2 * i + 1] = 0;
315 }
316
317 for (uint32_t i = 0; i < requestedDescriptors.size(); ++i) {
318 int descriptorBindingIndex = requestedDescriptors[i].fBindingIndex;
319 SkASSERT(SkTo<unsigned long>(descriptorBindingIndex) < bindUniformBufferInfo.size());
320 SkASSERT(SkTo<unsigned long>(descriptorBindingIndex) <
322 const auto& bindInfo = bindUniformBufferInfo[descriptorBindingIndex];
323 const VulkanBuffer* boundBuffer = static_cast<const VulkanBuffer*>(bindInfo.fBuffer);
324 SkASSERT(boundBuffer);
325 const uint32_t bindingSize = bindInfo.fBindingSize;
326 builder[2 * descriptorBindingIndex] = boundBuffer->uniqueID().asUInt();
327 builder[2 * descriptorBindingIndex + 1] = bindingSize;
328 }
329
330 builder.finish();
331 }
332
333 return uniqueKey;
334}
335
336void update_uniform_descriptor_set(SkSpan<DescriptorData> requestedDescriptors,
337 SkSpan<BindUniformBufferInfo> bindUniformBufferInfo,
338 VkDescriptorSet descSet,
339 const VulkanSharedContext* sharedContext) {
340 for (size_t i = 0; i < requestedDescriptors.size(); i++) {
341 int descriptorBindingIndex = requestedDescriptors[i].fBindingIndex;
342 SkASSERT(SkTo<unsigned long>(descriptorBindingIndex) < bindUniformBufferInfo.size());
343 const auto& bindInfo = bindUniformBufferInfo[descriptorBindingIndex];
344 if (bindInfo.fBuffer) {
345#if defined(SK_DEBUG)
346 static uint64_t maxUniformBufferRange =
347 sharedContext->vulkanCaps().maxUniformBufferRange();
348 SkASSERT(bindInfo.fBindingSize <= maxUniformBufferRange);
349#endif
350 VkDescriptorBufferInfo bufferInfo;
351 memset(&bufferInfo, 0, sizeof(VkDescriptorBufferInfo));
352 auto vulkanBuffer = static_cast<const VulkanBuffer*>(bindInfo.fBuffer);
353 bufferInfo.buffer = vulkanBuffer->vkBuffer();
354 bufferInfo.offset = 0; // We always use dynamic ubos so we set the base offset to 0
355 bufferInfo.range = bindInfo.fBindingSize;
356
357 VkWriteDescriptorSet writeInfo;
358 memset(&writeInfo, 0, sizeof(VkWriteDescriptorSet));
360 writeInfo.pNext = nullptr;
361 writeInfo.dstSet = descSet;
362 writeInfo.dstBinding = descriptorBindingIndex;
363 writeInfo.dstArrayElement = 0;
364 writeInfo.descriptorCount = requestedDescriptors[i].fCount;
365 writeInfo.descriptorType = DsTypeEnumToVkDs(requestedDescriptors[i].fType);
366 writeInfo.pImageInfo = nullptr;
367 writeInfo.pBufferInfo = &bufferInfo;
368 writeInfo.pTexelBufferView = nullptr;
369
370 // TODO(b/293925059): Migrate to updating all the uniform descriptors with one driver
371 // call. Calling UpdateDescriptorSets once to encapsulate updates to all uniform
372 // descriptors would be ideal, but that led to issues with draws where all the UBOs
373 // within that set would unexpectedly be assigned the same offset. Updating them one at
374 // a time within this loop works in the meantime but is suboptimal.
375 VULKAN_CALL(sharedContext->interface(),
376 UpdateDescriptorSets(sharedContext->device(),
377 /*descriptorWriteCount=*/1,
378 &writeInfo,
379 /*descriptorCopyCount=*/0,
380 /*pDescriptorCopies=*/nullptr));
381 }
382 }
383}
384
385} // anonymous namespace
386
387sk_sp<VulkanDescriptorSet> VulkanResourceProvider::findOrCreateUniformBuffersDescriptorSet(
388 SkSpan<DescriptorData> requestedDescriptors,
389 SkSpan<BindUniformBufferInfo> bindUniformBufferInfo) {
391
392 auto key = make_ubo_bind_group_key(requestedDescriptors, bindUniformBufferInfo);
393 auto* existingDescSet = fUniformBufferDescSetCache.find(key);
394 if (existingDescSet) {
395 return *existingDescSet;
396 }
397 sk_sp<VulkanDescriptorSet> newDS = this->findOrCreateDescriptorSet(requestedDescriptors);
398 if (!newDS) {
399 return nullptr;
400 }
401
402 update_uniform_descriptor_set(requestedDescriptors,
403 bindUniformBufferInfo,
404 *newDS->descriptorSet(),
405 this->vulkanSharedContext());
406 return *fUniformBufferDescSetCache.insert(key, newDS);
407}
408
409
410sk_sp<VulkanRenderPass> VulkanResourceProvider::findOrCreateRenderPassWithKnownKey(
411 const RenderPassDesc& renderPassDesc,
412 bool compatibleOnly,
413 const GraphiteResourceKey& rpKey) {
414 if (Resource* resource =
415 fResourceCache->findAndRefResource(rpKey, skgpu::Budgeted::kYes)) {
416 return sk_sp<VulkanRenderPass>(static_cast<VulkanRenderPass*>(resource));
417 }
418
419 sk_sp<VulkanRenderPass> renderPass =
420 VulkanRenderPass::MakeRenderPass(this->vulkanSharedContext(),
421 renderPassDesc,
422 compatibleOnly);
423 if (!renderPass) {
424 return nullptr;
425 }
426
427 renderPass->setKey(rpKey);
428 fResourceCache->insertResource(renderPass.get());
429
430 return renderPass;
431}
432
433sk_sp<VulkanRenderPass> VulkanResourceProvider::findOrCreateRenderPass(
434 const RenderPassDesc& renderPassDesc, bool compatibleOnly) {
435 GraphiteResourceKey rpKey = VulkanRenderPass::MakeRenderPassKey(renderPassDesc, compatibleOnly);
436
437 return this->findOrCreateRenderPassWithKnownKey(renderPassDesc, compatibleOnly, rpKey);
438}
439
440VkPipelineCache VulkanResourceProvider::pipelineCache() {
441 if (fPipelineCache == VK_NULL_HANDLE) {
442 VkPipelineCacheCreateInfo createInfo;
443 memset(&createInfo, 0, sizeof(VkPipelineCacheCreateInfo));
445 createInfo.pNext = nullptr;
446 createInfo.flags = 0;
447 createInfo.initialDataSize = 0;
448 createInfo.pInitialData = nullptr;
450 VULKAN_CALL_RESULT(this->vulkanSharedContext(),
451 result,
452 CreatePipelineCache(this->vulkanSharedContext()->device(),
453 &createInfo,
454 nullptr,
455 &fPipelineCache));
456 if (VK_SUCCESS != result) {
457 fPipelineCache = VK_NULL_HANDLE;
458 }
459 }
460 return fPipelineCache;
461}
462
463sk_sp<VulkanFramebuffer> VulkanResourceProvider::createFramebuffer(
464 const VulkanSharedContext* context,
465 const skia_private::TArray<VkImageView>& attachmentViews,
466 const VulkanRenderPass& renderPass,
467 const int width,
468 const int height) {
469 // TODO: Consider caching these in the future. If we pursue that, it may make more sense to
470 // use a compatible renderpass rather than a full one to make each frame buffer more versatile.
471 VkFramebufferCreateInfo framebufferInfo;
472 memset(&framebufferInfo, 0, sizeof(VkFramebufferCreateInfo));
474 framebufferInfo.pNext = nullptr;
475 framebufferInfo.flags = 0;
476 framebufferInfo.renderPass = renderPass.renderPass();
477 framebufferInfo.attachmentCount = attachmentViews.size();
478 framebufferInfo.pAttachments = attachmentViews.begin();
479 framebufferInfo.width = width;
480 framebufferInfo.height = height;
481 framebufferInfo.layers = 1;
482 return VulkanFramebuffer::Make(context, framebufferInfo);
483}
484
485void VulkanResourceProvider::onDeleteBackendTexture(const BackendTexture& texture) {
486 SkASSERT(texture.isValid());
488
489 VULKAN_CALL(this->vulkanSharedContext()->interface(),
490 DestroyImage(this->vulkanSharedContext()->device(), texture.getVkImage(),
491 /*VkAllocationCallbacks=*/nullptr));
492
493 // Free the image memory used for the BackendTexture's VkImage.
494 //
495 // How we do this is dependent upon on how the image was allocated (via the memory allocator or
496 // with a direct call to the Vulkan driver) . If the VulkanAlloc's fBackendMemory is != 0, then
497 // that means the allocator was used. Otherwise, a direct driver call was used and we should
498 // free the VkDeviceMemory (fMemory).
499 if (texture.getMemoryAlloc()->fBackendMemory) {
500 skgpu::VulkanMemory::FreeImageMemory(this->vulkanSharedContext()->memoryAllocator(),
501 *(texture.getMemoryAlloc()));
502 } else {
503 SkASSERT(texture.getMemoryAlloc()->fMemory != VK_NULL_HANDLE);
504 VULKAN_CALL(this->vulkanSharedContext()->interface(),
505 FreeMemory(this->vulkanSharedContext()->device(),
506 texture.getMemoryAlloc()->fMemory,
507 nullptr));
508 }
509}
510
512 const VulkanYcbcrConversionInfo& ycbcrInfo) const {
513 if (!ycbcrInfo.isValid()) {
514 return nullptr;
515 }
516 GraphiteResourceKey ycbcrConversionKey =
517 VulkanYcbcrConversion::MakeYcbcrConversionKey(this->vulkanSharedContext(), ycbcrInfo);
518
519 if (Resource* resource = fResourceCache->findAndRefResource(ycbcrConversionKey,
522 }
523
524 auto ycbcrConversion = VulkanYcbcrConversion::Make(this->vulkanSharedContext(), ycbcrInfo);
525 if (!ycbcrConversion) {
526 return nullptr;
527 }
528
529 ycbcrConversion->setKey(ycbcrConversionKey);
530 fResourceCache->insertResource(ycbcrConversion.get());
531
532 return ycbcrConversion;
533}
534
535sk_sp<VulkanGraphicsPipeline> VulkanResourceProvider::findOrCreateLoadMSAAPipeline(
536 const RenderPassDesc& renderPassDesc) {
537
538 if (!renderPassDesc.fColorResolveAttachment.fTextureInfo.isValid() ||
539 !renderPassDesc.fColorAttachment.fTextureInfo.isValid()) {
540 SKGPU_LOG_E("Loading MSAA from resolve texture requires valid color & resolve attachment");
541 return nullptr;
542 }
543
544 // Check to see if we already have a suitable pipeline that we can use.
545 GraphiteResourceKey renderPassKey =
546 VulkanRenderPass::MakeRenderPassKey(renderPassDesc, /*compatibleOnly=*/true);
547 for (int i = 0; i < fLoadMSAAPipelines.size(); i++) {
548 if (renderPassKey == fLoadMSAAPipelines.at(i).first) {
549 return fLoadMSAAPipelines.at(i).second;
550 }
551 }
552
553 // If any of the load MSAA pipeline creation structures are null then we need to initialize
554 // those before proceeding. If the creation of one of them fails, all are assigned to null, so
555 // we only need to check one of the structures.
556 if (fMSAALoadVertShaderModule == VK_NULL_HANDLE) {
557 SkASSERT(fMSAALoadFragShaderModule == VK_NULL_HANDLE &&
558 fMSAALoadPipelineLayout == VK_NULL_HANDLE);
560 this->vulkanSharedContext(),
561 &fMSAALoadVertShaderModule,
562 &fMSAALoadFragShaderModule,
563 &fMSAALoadShaderStageInfo[0],
564 &fMSAALoadPipelineLayout)) {
565 SKGPU_LOG_E("Failed to initialize MSAA load pipeline creation structure(s)");
566 return nullptr;
567 }
568 }
569
570 sk_sp<VulkanRenderPass> compatibleRenderPass =
571 this->findOrCreateRenderPassWithKnownKey(renderPassDesc,
572 /*compatibleOnly=*/true,
573 renderPassKey);
574 if (!compatibleRenderPass) {
575 SKGPU_LOG_E("Failed to make compatible render pass for loading MSAA");
576 }
577
579 this->vulkanSharedContext(),
580 fMSAALoadVertShaderModule,
581 fMSAALoadFragShaderModule,
582 &fMSAALoadShaderStageInfo[0],
583 fMSAALoadPipelineLayout,
584 compatibleRenderPass,
585 this->pipelineCache(),
586 renderPassDesc.fColorAttachment.fTextureInfo);
587
588 if (!pipeline) {
589 SKGPU_LOG_E("Failed to create MSAA load pipeline");
590 return nullptr;
591 }
592
593 fLoadMSAAPipelines.push_back(std::make_pair(renderPassKey, pipeline));
594 return pipeline;
595}
596
597#ifdef SK_BUILD_FOR_ANDROID
598
599BackendTexture VulkanResourceProvider::onCreateBackendTexture(AHardwareBuffer* hardwareBuffer,
600 bool isRenderable,
601 bool isProtectedContent,
602 SkISize dimensions,
603 bool fromAndroidWindow) const {
604
605 const VulkanSharedContext* vkContext = this->vulkanSharedContext();
606 VkDevice device = vkContext->device();
607 const VulkanCaps& vkCaps = vkContext->vulkanCaps();
608
611 if (!skgpu::GetAHardwareBufferProperties(
612 &hwbFormatProps, &hwbProps, vkContext->interface(), hardwareBuffer, device)) {
613 return {};
614 }
615
616 bool importAsExternalFormat = hwbFormatProps.format == VK_FORMAT_UNDEFINED;
617
618 // Start to assemble VulkanTextureInfo which is needed later on to create the VkImage but can
619 // sooner help us query VulkanCaps for certain format feature support.
620 // TODO: Allow client to pass in tiling mode. For external formats, this is required to be
621 // optimal. For AHB that have a known Vulkan format, we can query VulkanCaps to determine if
622 // optimal is a valid decision given the format features.
624 VkImageCreateFlags imgCreateflags = isProtectedContent ? VK_IMAGE_CREATE_PROTECTED_BIT : 0;
626 // When importing as an external format the image usage can only be VK_IMAGE_USAGE_SAMPLED_BIT.
627 if (!importAsExternalFormat) {
629 if (isRenderable) {
630 // Renderable attachments can be used as input attachments if we are loading from MSAA.
632 }
633 }
634 VulkanTextureInfo vkTexInfo { VK_SAMPLE_COUNT_1_BIT,
636 imgCreateflags,
637 hwbFormatProps.format,
638 tiling,
639 usageFlags,
642 VulkanYcbcrConversionInfo() };
643
644 if (isRenderable && (importAsExternalFormat || !vkCaps.isRenderable(vkTexInfo))) {
645 SKGPU_LOG_W("Renderable texture requested from an AHardwareBuffer which uses a VkFormat "
646 "that Skia cannot render to (VkFormat: %d).\n", hwbFormatProps.format);
647 return {};
648 }
649
650 if (!importAsExternalFormat && (!vkCaps.isTransferSrc(vkTexInfo) ||
651 !vkCaps.isTransferDst(vkTexInfo) ||
652 !vkCaps.isTexturable(vkTexInfo))) {
653 if (isRenderable) {
654 SKGPU_LOG_W("VkFormat %d is either unfamiliar to Skia or doesn't support the necessary"
655 " format features. Because a renerable texture was requested, we cannot "
656 "fall back to importing with an external format.\n", hwbFormatProps.format);
657 return {};
658 }
659 // If the VkFormat does not support the features we need, then import as an external format.
660 importAsExternalFormat = true;
661 // If we use VkExternalFormatANDROID with an externalFormat != 0, then format must =
662 // VK_FORMAT_UNDEFINED.
663 vkTexInfo.fFormat = VK_FORMAT_UNDEFINED;
664 vkTexInfo.fImageUsageFlags = VK_IMAGE_USAGE_SAMPLED_BIT;
665 }
666
667 VulkanYcbcrConversionInfo ycbcrInfo;
668 VkExternalFormatANDROID externalFormat;
670 externalFormat.pNext = nullptr;
671 externalFormat.externalFormat = 0; // If this is zero it is as if we aren't using this struct.
672 if (importAsExternalFormat) {
673 GetYcbcrConversionInfoFromFormatProps(&ycbcrInfo, hwbFormatProps);
674 if (!ycbcrInfo.isValid()) {
675 SKGPU_LOG_W("Failed to create valid YCbCr conversion information from hardware buffer"
676 "format properties.\n");
677 return {};
678 }
679 externalFormat.externalFormat = hwbFormatProps.externalFormat;
680 }
681 const VkExternalMemoryImageCreateInfo externalMemoryImageInfo{
683 &externalFormat, // pNext
685 };
686
687 SkASSERT(!(vkTexInfo.fFlags & VK_IMAGE_CREATE_PROTECTED_BIT) ||
689
690 const VkImageCreateInfo imageCreateInfo = {
692 &externalMemoryImageInfo, // pNext
693 vkTexInfo.fFlags, // VkImageCreateFlags
694 VK_IMAGE_TYPE_2D, // VkImageType
695 vkTexInfo.fFormat, // VkFormat
696 { (uint32_t)dimensions.fWidth, (uint32_t)dimensions.fHeight, 1 }, // VkExtent3D
697 1, // mipLevels
698 1, // arrayLayers
699 VK_SAMPLE_COUNT_1_BIT, // samples
700 vkTexInfo.fImageTiling, // VkImageTiling
701 vkTexInfo.fImageUsageFlags, // VkImageUsageFlags
702 vkTexInfo.fSharingMode, // VkSharingMode
703 0, // queueFamilyCount
704 nullptr, // pQueueFamilyIndices
705 VK_IMAGE_LAYOUT_UNDEFINED, // initialLayout
706 };
707
709 VkImage image;
710 result = VULKAN_CALL(vkContext->interface(),
711 CreateImage(device, &imageCreateInfo, nullptr, &image));
712 if (result != VK_SUCCESS) {
713 return {};
714 }
715
716 const VkPhysicalDeviceMemoryProperties2& phyDevMemProps =
717 vkContext->vulkanCaps().physicalDeviceMemoryProperties2();
718 VulkanAlloc alloc;
719 if (!AllocateAndBindImageMemory(&alloc, image, phyDevMemProps, hwbProps, hardwareBuffer,
720 vkContext->interface(), device)) {
721 VULKAN_CALL(vkContext->interface(), DestroyImage(device, image, nullptr));
722 return {};
723 }
724
725 return { dimensions, vkTexInfo, VK_IMAGE_LAYOUT_UNDEFINED, VK_QUEUE_FAMILY_FOREIGN_EXT,
726 image, alloc};
727}
728
729#endif // SK_BUILD_FOR_ANDROID
730
731} // namespace skgpu::graphite
AutoreleasePool pool
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
struct AHardwareBuffer AHardwareBuffer
#define SKGPU_LOG_E(fmt,...)
Definition: Log.h:38
#define SKGPU_LOG_W(fmt,...)
Definition: Log.h:40
#define SkASSERT(cond)
Definition: SkAssert.h:116
static SkString resource(SkPDFResourceType type, int index)
#define VULKAN_CALL(IFACE, X)
#define VULKAN_CALL_RESULT(SHARED_CONTEXT, RESULT, X)
GLenum type
V * find(const K &key)
Definition: SkLRUCache.h:49
V * insert(const K &key, V value)
Definition: SkLRUCache.h:62
constexpr bool empty() const
Definition: SkSpan_impl.h:96
constexpr size_t size() const
Definition: SkSpan_impl.h:95
T * get() const
Definition: SkRefCnt.h:303
static Domain GenerateDomain()
Definition: ResourceKey.cpp:27
sk_sp< ResourceCache > fResourceCache
static sk_sp< Buffer > Make(const VulkanSharedContext *, size_t, BufferType, AccessPattern)
static sk_sp< VulkanDescriptorPool > Make(const VulkanSharedContext *, SkSpan< DescriptorData >, VkDescriptorSetLayout)
static sk_sp< VulkanDescriptorSet > Make(const VulkanSharedContext *, const sk_sp< VulkanDescriptorPool > &)
static sk_sp< VulkanFramebuffer > Make(const VulkanSharedContext *, const VkFramebufferCreateInfo &)
static sk_sp< VulkanGraphicsPipeline > Make(const VulkanSharedContext *, const RuntimeEffectDictionary *, const GraphicsPipelineDesc &, const RenderPassDesc &, const sk_sp< VulkanRenderPass > &compatibleRenderPass, VkPipelineCache)
static constexpr unsigned int kNumUniformBuffers
static sk_sp< VulkanGraphicsPipeline > MakeLoadMSAAPipeline(const VulkanSharedContext *, VkShaderModule vsModule, VkShaderModule fsModule, VkPipelineShaderStageCreateInfo *pipelineShaderStages, VkPipelineLayout, sk_sp< VulkanRenderPass > compatibleRenderPass, VkPipelineCache, const TextureInfo &dstColorAttachmentTexInfo)
static bool InitializeMSAALoadPipelineStructs(const VulkanSharedContext *, VkShaderModule *outVertexShaderModule, VkShaderModule *outFragShaderModule, VkPipelineShaderStageCreateInfo *outShaderStageInfo, VkPipelineLayout *outPipelineLayout)
static sk_sp< VulkanRenderPass > MakeRenderPass(const VulkanSharedContext *, const RenderPassDesc &, bool compatibleOnly)
static GraphiteResourceKey MakeRenderPassKey(const RenderPassDesc &, bool compatibleOnly)
sk_sp< VulkanYcbcrConversion > findOrCreateCompatibleYcbcrConversion(const VulkanYcbcrConversionInfo &ycbcrInfo) const
VulkanResourceProvider(SharedContext *sharedContext, SingleOwner *, uint32_t recorderID, size_t resourceBudget, sk_sp< Buffer > intrinsicConstantUniformBuffer, sk_sp< Buffer > loadMSAAVertexBuffer)
static sk_sp< VulkanSampler > Make(const VulkanSharedContext *, const SkSamplingOptions &, SkTileMode xTileMode, SkTileMode yTileMode, sk_sp< VulkanYcbcrConversion > ycbcrConversion=nullptr)
static bool MakeVkImage(const VulkanSharedContext *, SkISize dimensions, const TextureInfo &, CreatedImageInfo *outInfo)
static sk_sp< Texture > Make(const VulkanSharedContext *, SkISize dimensions, const TextureInfo &, skgpu::Budgeted, sk_sp< VulkanYcbcrConversion >)
static sk_sp< Texture > MakeWrapped(const VulkanSharedContext *, SkISize dimensions, const TextureInfo &, sk_sp< MutableTextureState >, VkImage, const VulkanAlloc &, sk_sp< VulkanYcbcrConversion >)
static GraphiteResourceKey GetKeyFromSamplerDesc(const SamplerDesc &samplerDesc)
static sk_sp< VulkanYcbcrConversion > Make(const VulkanSharedContext *, const VulkanYcbcrConversionInfo &)
static GraphiteResourceKey MakeYcbcrConversionKey(const VulkanSharedContext *, const VulkanYcbcrConversionInfo &)
int size() const
Definition: SkTArray.h:421
VkDevice device
Definition: main.cc:53
GAsyncResult * result
FlTexture * texture
sk_sp< const SkImage > image
Definition: SkRecords.h:269
DlVertices::Builder Builder
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
SK_API uint32_t GetVkQueueFamilyIndex(const MutableTextureState &state)
SK_API VkImageLayout GetVkImageLayout(const MutableTextureState &state)
void FreeImageMemory(VulkanMemoryAllocator *, const VulkanAlloc &alloc)
uint32_t ResourceType
VkDescriptorType DsTypeEnumToVkDs(DescriptorType type)
void DescriptorDataToVkDescSetLayout(const VulkanSharedContext *ctxt, const SkSpan< DescriptorData > &requestedDescriptors, VkDescriptorSetLayout *outLayout)
constexpr int kMaxNumberOfCachedBufferDescSets
Budgeted
Definition: GpuTypes.h:35
Definition: ref_ptr.h:256
int32_t height
int32_t width
Definition: SkSize.h:16
int32_t fHeight
Definition: SkSize.h:18
int32_t fWidth
Definition: SkSize.h:17
VkStructureType sType
const VkImageView * pAttachments
Definition: vulkan_core.h:3823
VkStructureType sType
Definition: vulkan_core.h:3818
VkFramebufferCreateFlags flags
Definition: vulkan_core.h:3820
VkPipelineCacheCreateFlags flags
Definition: vulkan_core.h:3488
const VkBufferView * pTexelBufferView
Definition: vulkan_core.h:3797
VkStructureType sType
Definition: vulkan_core.h:3788
const VkDescriptorImageInfo * pImageInfo
Definition: vulkan_core.h:3795
const VkDescriptorBufferInfo * pBufferInfo
Definition: vulkan_core.h:3796
VkDescriptorSet dstSet
Definition: vulkan_core.h:3790
const void * pNext
Definition: vulkan_core.h:3789
VkDescriptorType descriptorType
Definition: vulkan_core.h:3794
AttachmentDesc fColorResolveAttachment
static constexpr int kImmutableSamplerInfoShift
@ VK_IMAGE_LAYOUT_UNDEFINED
Definition: vulkan_core.h:1331
@ VK_SHARING_MODE_EXCLUSIVE
Definition: vulkan_core.h:1813
VkFlags VkImageUsageFlags
Definition: vulkan_core.h:2382
@ VK_IMAGE_CREATE_PROTECTED_BIT
Definition: vulkan_core.h:2320
VkImageTiling
Definition: vulkan_core.h:1766
@ VK_IMAGE_TILING_OPTIMAL
Definition: vulkan_core.h:1767
@ VK_IMAGE_ASPECT_COLOR_BIT
Definition: vulkan_core.h:2238
@ VK_IMAGE_USAGE_TRANSFER_DST_BIT
Definition: vulkan_core.h:2353
@ VK_IMAGE_USAGE_SAMPLED_BIT
Definition: vulkan_core.h:2354
@ VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT
Definition: vulkan_core.h:2359
@ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
Definition: vulkan_core.h:2356
@ VK_IMAGE_USAGE_TRANSFER_SRC_BIT
Definition: vulkan_core.h:2352
@ VK_SAMPLE_COUNT_1_BIT
Definition: vulkan_core.h:2340
@ VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID
Definition: vulkan_core.h:5031
#define VK_QUEUE_FAMILY_FOREIGN_EXT
@ VK_IMAGE_TYPE_2D
Definition: vulkan_core.h:1775
VkFlags VkImageCreateFlags
Definition: vulkan_core.h:2337
VkResult
Definition: vulkan_core.h:140
@ VK_SUCCESS
Definition: vulkan_core.h:141
#define VK_NULL_HANDLE
Definition: vulkan_core.h:46
@ VK_FORMAT_UNDEFINED
Definition: vulkan_core.h:1459
@ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO
Definition: vulkan_core.h:239
@ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO
Definition: vulkan_core.h:216
@ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET
Definition: vulkan_core.h:237
@ VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID
Definition: vulkan_core.h:647
@ VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO
Definition: vulkan_core.h:219
@ VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO
Definition: vulkan_core.h:305