xref: /aosp_15_r20/external/skia/src/gpu/graphite/vk/VulkanCommandBuffer.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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/VulkanCommandBuffer.h"
9 
10 #include "include/gpu/MutableTextureState.h"
11 #include "include/gpu/graphite/BackendSemaphore.h"
12 #include "include/gpu/graphite/vk/VulkanGraphiteTypes.h"
13 #include "include/gpu/vk/VulkanMutableTextureState.h"
14 #include "include/private/base/SkTArray.h"
15 #include "src/gpu/DataUtils.h"
16 #include "src/gpu/graphite/ContextUtils.h"
17 #include "src/gpu/graphite/DescriptorData.h"
18 #include "src/gpu/graphite/Log.h"
19 #include "src/gpu/graphite/RenderPassDesc.h"
20 #include "src/gpu/graphite/Surface_Graphite.h"
21 #include "src/gpu/graphite/TextureProxy.h"
22 #include "src/gpu/graphite/UniformManager.h"
23 #include "src/gpu/graphite/vk/VulkanBuffer.h"
24 #include "src/gpu/graphite/vk/VulkanCaps.h"
25 #include "src/gpu/graphite/vk/VulkanDescriptorSet.h"
26 #include "src/gpu/graphite/vk/VulkanFramebuffer.h"
27 #include "src/gpu/graphite/vk/VulkanGraphiteUtilsPriv.h"
28 #include "src/gpu/graphite/vk/VulkanRenderPass.h"
29 #include "src/gpu/graphite/vk/VulkanSampler.h"
30 #include "src/gpu/graphite/vk/VulkanSharedContext.h"
31 #include "src/gpu/graphite/vk/VulkanTexture.h"
32 #include "src/gpu/vk/VulkanUtilsPriv.h"
33 
34 using namespace skia_private;
35 
36 namespace skgpu::graphite {
37 
38 class VulkanDescriptorSet;
39 
40 /**
41  * Since intrinsic uniforms need to be read in the vertex shader, we cannot use protected buffers
42  * for them when submitting protected work. Thus in order to upload data to them, we need to make
43  * them mappable instead of using commands to copy data to them (would require them to be
44  * protected if we did). This helper class manages rotating through buffers and writing each new
45  * occurrence of a set of intrinsic uniforms into the current buffer.
46  *
47  * Ideally we would remove this class and instead use push constants for all intrinsic uniforms.
48  */
49 class VulkanCommandBuffer::IntrinsicConstantsManager {
50 public:
add(VulkanCommandBuffer * cb,UniformDataBlock intrinsicValues)51     BindBufferInfo add(VulkanCommandBuffer* cb, UniformDataBlock intrinsicValues) {
52         static constexpr int kNumSlots = 8;
53 
54         BindBufferInfo* existing = fCachedIntrinsicValues.find(intrinsicValues);
55         if (existing) {
56             return *existing;
57         }
58 
59         SkASSERT(!cb->fActiveRenderPass);
60 
61         const Caps* caps = cb->fSharedContext->caps();
62         const uint32_t stride =
63                 SkAlignTo(intrinsicValues.size(), caps->requiredUniformBufferAlignment());
64         if (!fCurrentBuffer || fSlotsUsed == kNumSlots) {
65             VulkanResourceProvider* resourceProvider = cb->fResourceProvider;
66             sk_sp<Buffer> buffer = resourceProvider->findOrCreateBuffer(stride * kNumSlots,
67                                                                         BufferType::kUniform,
68                                                                         AccessPattern::kHostVisible,
69                                                                         "IntrinsicConstantBuffer");
70             if (!buffer) {
71                 return {};
72             }
73             VulkanBuffer* ptr = static_cast<VulkanBuffer*>(buffer.release());
74             fCurrentBuffer = sk_sp<VulkanBuffer>(ptr);
75 
76             fSlotsUsed = 0;
77 
78             if (!fCurrentBuffer) {
79                 // If we failed to create a GPU buffer to hold the intrinsic uniforms, we will fail
80                 // the Recording being inserted, so return an empty bind info.
81                 return {};
82             }
83             cb->trackResource(fCurrentBuffer);
84         }
85 
86         SkASSERT(fCurrentBuffer && fSlotsUsed < kNumSlots);
87         void* mapPtr = fCurrentBuffer->map();
88         if (!mapPtr) {
89             return {};
90         }
91         uint32_t offset = (fSlotsUsed++) * stride;
92         mapPtr = SkTAddOffset<void>(mapPtr, static_cast<ptrdiff_t>(offset));
93         memcpy(mapPtr, intrinsicValues.data(), intrinsicValues.size());
94         fCurrentBuffer->unmap();
95         BindBufferInfo binding{
96                 fCurrentBuffer.get(), offset, SkTo<uint32_t>(intrinsicValues.size())};
97         fCachedIntrinsicValues.set(UniformDataBlock::Make(intrinsicValues, &fUniformData), binding);
98         return binding;
99     }
100 
101 private:
102     // The current buffer being filled up, as well as the how much of it has been written to.
103     sk_sp<VulkanBuffer> fCurrentBuffer;
104     int fSlotsUsed = 0;  // in multiples of the intrinsic uniform size and UBO binding requirement
105 
106     // All uploaded intrinsic uniform sets and where they are on the GPU. All uniform sets are
107     // cached for the duration of a CommandBuffer since the maximum number of elements in this
108     // collection will equal the number of render passes and the intrinsic constants aren't that
109     // large. This maximizes the chance for reuse between passes.
110     skia_private::THashMap<UniformDataBlock, BindBufferInfo, UniformDataBlock::Hash>
111             fCachedIntrinsicValues;
112     SkArenaAlloc fUniformData{0};
113 };
114 
115 
Make(const VulkanSharedContext * sharedContext,VulkanResourceProvider * resourceProvider,Protected isProtected)116 std::unique_ptr<VulkanCommandBuffer> VulkanCommandBuffer::Make(
117         const VulkanSharedContext* sharedContext,
118         VulkanResourceProvider* resourceProvider,
119         Protected isProtected) {
120     // Create VkCommandPool
121     VkCommandPoolCreateFlags cmdPoolCreateFlags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
122     if (isProtected == Protected::kYes) {
123         cmdPoolCreateFlags |= VK_COMMAND_POOL_CREATE_PROTECTED_BIT;
124     }
125 
126     const VkCommandPoolCreateInfo cmdPoolInfo = {
127             VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,  // sType
128             nullptr,                                     // pNext
129             cmdPoolCreateFlags,                          // CmdPoolCreateFlags
130             sharedContext->queueIndex(),                 // queueFamilyIndex
131     };
132     VkResult result;
133     VkCommandPool pool;
134     VULKAN_CALL_RESULT(sharedContext,
135                        result,
136                        CreateCommandPool(sharedContext->device(), &cmdPoolInfo, nullptr, &pool));
137     if (result != VK_SUCCESS) {
138         return nullptr;
139     }
140 
141     const VkCommandBufferAllocateInfo cmdInfo = {
142         VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,   // sType
143         nullptr,                                          // pNext
144         pool,                                             // commandPool
145         VK_COMMAND_BUFFER_LEVEL_PRIMARY,                  // level
146         1                                                 // bufferCount
147     };
148 
149     VkCommandBuffer primaryCmdBuffer;
150     VULKAN_CALL_RESULT(
151             sharedContext,
152             result,
153             AllocateCommandBuffers(sharedContext->device(), &cmdInfo, &primaryCmdBuffer));
154     if (result != VK_SUCCESS) {
155         VULKAN_CALL(sharedContext->interface(),
156                     DestroyCommandPool(sharedContext->device(), pool, nullptr));
157         return nullptr;
158     }
159 
160     return std::unique_ptr<VulkanCommandBuffer>(new VulkanCommandBuffer(pool,
161                                                                         primaryCmdBuffer,
162                                                                         sharedContext,
163                                                                         resourceProvider,
164                                                                         isProtected));
165 }
166 
VulkanCommandBuffer(VkCommandPool pool,VkCommandBuffer primaryCommandBuffer,const VulkanSharedContext * sharedContext,VulkanResourceProvider * resourceProvider,Protected isProtected)167 VulkanCommandBuffer::VulkanCommandBuffer(VkCommandPool pool,
168                                          VkCommandBuffer primaryCommandBuffer,
169                                          const VulkanSharedContext* sharedContext,
170                                          VulkanResourceProvider* resourceProvider,
171                                          Protected isProtected)
172         : CommandBuffer(isProtected)
173         , fPool(pool)
174         , fPrimaryCommandBuffer(primaryCommandBuffer)
175         , fSharedContext(sharedContext)
176         , fResourceProvider(resourceProvider) {
177     // When making a new command buffer, we automatically begin the command buffer
178     this->begin();
179 }
180 
~VulkanCommandBuffer()181 VulkanCommandBuffer::~VulkanCommandBuffer() {
182     if (fActive) {
183         // Need to end command buffer before deleting it
184         VULKAN_CALL(fSharedContext->interface(), EndCommandBuffer(fPrimaryCommandBuffer));
185         fActive = false;
186     }
187 
188     if (VK_NULL_HANDLE != fSubmitFence) {
189         VULKAN_CALL(fSharedContext->interface(),
190                     DestroyFence(fSharedContext->device(), fSubmitFence, nullptr));
191     }
192     // This should delete any command buffers as well.
193     VULKAN_CALL(fSharedContext->interface(),
194                 DestroyCommandPool(fSharedContext->device(), fPool, nullptr));
195 }
196 
onResetCommandBuffer()197 void VulkanCommandBuffer::onResetCommandBuffer() {
198     SkASSERT(!fActive);
199     VULKAN_CALL_ERRCHECK(fSharedContext, ResetCommandPool(fSharedContext->device(), fPool, 0));
200     fActiveGraphicsPipeline = nullptr;
201     fIntrinsicConstants = nullptr;
202     fBindUniformBuffers = true;
203     fBoundIndexBuffer = VK_NULL_HANDLE;
204     fBoundIndexBufferOffset = 0;
205     fBoundIndirectBuffer = VK_NULL_HANDLE;
206     fBoundIndirectBufferOffset = 0;
207     fTextureSamplerDescSetToBind = VK_NULL_HANDLE;
208     fNumTextureSamplers = 0;
209     fUniformBuffersToBind.fill({});
210     for (int i = 0; i < 4; ++i) {
211         fCachedBlendConstant[i] = -1.0;
212     }
213     for (auto& boundInputBuffer : fBoundInputBuffers) {
214         boundInputBuffer = VK_NULL_HANDLE;
215     }
216     for (auto& boundInputOffset : fBoundInputBufferOffsets) {
217         boundInputOffset = 0;
218     }
219 }
220 
setNewCommandBufferResources()221 bool VulkanCommandBuffer::setNewCommandBufferResources() {
222     this->begin();
223     return true;
224 }
225 
begin()226 void VulkanCommandBuffer::begin() {
227     SkASSERT(!fActive);
228     VkCommandBufferBeginInfo cmdBufferBeginInfo;
229     memset(&cmdBufferBeginInfo, 0, sizeof(VkCommandBufferBeginInfo));
230     cmdBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
231     cmdBufferBeginInfo.pNext = nullptr;
232     cmdBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
233     cmdBufferBeginInfo.pInheritanceInfo = nullptr;
234 
235     VULKAN_CALL_ERRCHECK(fSharedContext,
236                          BeginCommandBuffer(fPrimaryCommandBuffer, &cmdBufferBeginInfo));
237     fIntrinsicConstants = std::make_unique<IntrinsicConstantsManager>();
238     fActive = true;
239 }
240 
end()241 void VulkanCommandBuffer::end() {
242     SkASSERT(fActive);
243     SkASSERT(!fActiveRenderPass);
244 
245     this->submitPipelineBarriers();
246 
247     VULKAN_CALL_ERRCHECK(fSharedContext, EndCommandBuffer(fPrimaryCommandBuffer));
248 
249     fActive = false;
250 }
251 
addWaitSemaphores(size_t numWaitSemaphores,const BackendSemaphore * waitSemaphores)252 void VulkanCommandBuffer::addWaitSemaphores(size_t numWaitSemaphores,
253                                             const BackendSemaphore* waitSemaphores) {
254     if (!waitSemaphores) {
255         SkASSERT(numWaitSemaphores == 0);
256         return;
257     }
258 
259     for (size_t i = 0; i < numWaitSemaphores; ++i) {
260         auto& semaphore = waitSemaphores[i];
261         if (semaphore.isValid() && semaphore.backend() == BackendApi::kVulkan) {
262             fWaitSemaphores.push_back(BackendSemaphores::GetVkSemaphore(semaphore));
263         }
264     }
265 }
266 
addSignalSemaphores(size_t numSignalSemaphores,const BackendSemaphore * signalSemaphores)267 void VulkanCommandBuffer::addSignalSemaphores(size_t numSignalSemaphores,
268                                               const BackendSemaphore* signalSemaphores) {
269     if (!signalSemaphores) {
270         SkASSERT(numSignalSemaphores == 0);
271         return;
272     }
273 
274     for (size_t i = 0; i < numSignalSemaphores; ++i) {
275         auto& semaphore = signalSemaphores[i];
276         if (semaphore.isValid() && semaphore.backend() == BackendApi::kVulkan) {
277             fSignalSemaphores.push_back(BackendSemaphores::GetVkSemaphore(semaphore));
278         }
279     }
280 }
281 
prepareSurfaceForStateUpdate(SkSurface * targetSurface,const MutableTextureState * newState)282 void VulkanCommandBuffer::prepareSurfaceForStateUpdate(SkSurface* targetSurface,
283                                                        const MutableTextureState* newState) {
284     TextureProxy* textureProxy = static_cast<Surface*>(targetSurface)->backingTextureProxy();
285     VulkanTexture* texture = static_cast<VulkanTexture*>(textureProxy->texture());
286 
287     // Even though internally we use this helper for getting src access flags and stages they
288     // can also be used for general dst flags since we don't know exactly what the client
289     // plans on using the image for.
290     VkImageLayout newLayout = skgpu::MutableTextureStates::GetVkImageLayout(newState);
291     if (newLayout == VK_IMAGE_LAYOUT_UNDEFINED) {
292         newLayout = texture->currentLayout();
293     }
294     VkPipelineStageFlags dstStage = VulkanTexture::LayoutToPipelineSrcStageFlags(newLayout);
295     VkAccessFlags dstAccess = VulkanTexture::LayoutToSrcAccessMask(newLayout);
296 
297     uint32_t currentQueueFamilyIndex = texture->currentQueueFamilyIndex();
298     uint32_t newQueueFamilyIndex = skgpu::MutableTextureStates::GetVkQueueFamilyIndex(newState);
299     auto isSpecialQueue = [](uint32_t queueFamilyIndex) {
300         return queueFamilyIndex == VK_QUEUE_FAMILY_EXTERNAL ||
301                queueFamilyIndex == VK_QUEUE_FAMILY_FOREIGN_EXT;
302     };
303     if (isSpecialQueue(currentQueueFamilyIndex) && isSpecialQueue(newQueueFamilyIndex)) {
304         // It is illegal to have both the new and old queue be special queue families (i.e. external
305         // or foreign).
306         return;
307     }
308 
309     texture->setImageLayoutAndQueueIndex(this,
310                                          newLayout,
311                                          dstAccess,
312                                          dstStage,
313                                          false,
314                                          newQueueFamilyIndex);
315 }
316 
submit_to_queue(const VulkanSharedContext * sharedContext,VkQueue queue,VkFence fence,uint32_t waitCount,const VkSemaphore * waitSemaphores,const VkPipelineStageFlags * waitStages,uint32_t commandBufferCount,const VkCommandBuffer * commandBuffers,uint32_t signalCount,const VkSemaphore * signalSemaphores,Protected protectedContext)317 static VkResult submit_to_queue(const VulkanSharedContext* sharedContext,
318                                 VkQueue queue,
319                                 VkFence fence,
320                                 uint32_t waitCount,
321                                 const VkSemaphore* waitSemaphores,
322                                 const VkPipelineStageFlags* waitStages,
323                                 uint32_t commandBufferCount,
324                                 const VkCommandBuffer* commandBuffers,
325                                 uint32_t signalCount,
326                                 const VkSemaphore* signalSemaphores,
327                                 Protected protectedContext) {
328     VkProtectedSubmitInfo protectedSubmitInfo;
329     if (protectedContext == Protected::kYes) {
330         memset(&protectedSubmitInfo, 0, sizeof(VkProtectedSubmitInfo));
331         protectedSubmitInfo.sType = VK_STRUCTURE_TYPE_PROTECTED_SUBMIT_INFO;
332         protectedSubmitInfo.pNext = nullptr;
333         protectedSubmitInfo.protectedSubmit = VK_TRUE;
334     }
335 
336     VkSubmitInfo submitInfo;
337     memset(&submitInfo, 0, sizeof(VkSubmitInfo));
338     submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
339     submitInfo.pNext = protectedContext == Protected::kYes ? &protectedSubmitInfo : nullptr;
340     submitInfo.waitSemaphoreCount = waitCount;
341     submitInfo.pWaitSemaphores = waitSemaphores;
342     submitInfo.pWaitDstStageMask = waitStages;
343     submitInfo.commandBufferCount = commandBufferCount;
344     submitInfo.pCommandBuffers = commandBuffers;
345     submitInfo.signalSemaphoreCount = signalCount;
346     submitInfo.pSignalSemaphores = signalSemaphores;
347     VkResult result;
348     VULKAN_CALL_RESULT(sharedContext, result, QueueSubmit(queue, 1, &submitInfo, fence));
349     return result;
350 }
351 
submit(VkQueue queue)352 bool VulkanCommandBuffer::submit(VkQueue queue) {
353     this->end();
354 
355     auto device = fSharedContext->device();
356     VkResult err;
357 
358     if (fSubmitFence == VK_NULL_HANDLE) {
359         VkFenceCreateInfo fenceInfo;
360         memset(&fenceInfo, 0, sizeof(VkFenceCreateInfo));
361         fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
362         VULKAN_CALL_RESULT(
363                 fSharedContext, err, CreateFence(device, &fenceInfo, nullptr, &fSubmitFence));
364         if (err) {
365             fSubmitFence = VK_NULL_HANDLE;
366             return false;
367         }
368     } else {
369         // This cannot return DEVICE_LOST so we assert we succeeded.
370         VULKAN_CALL_RESULT(fSharedContext, err, ResetFences(device, 1, &fSubmitFence));
371         SkASSERT(err == VK_SUCCESS);
372     }
373 
374     SkASSERT(fSubmitFence != VK_NULL_HANDLE);
375     int waitCount = fWaitSemaphores.size();
376     TArray<VkPipelineStageFlags> vkWaitStages(waitCount);
377     for (int i = 0; i < waitCount; ++i) {
378         vkWaitStages.push_back(VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
379                                VK_PIPELINE_STAGE_TRANSFER_BIT);
380     }
381 
382     VkResult submitResult = submit_to_queue(fSharedContext,
383                                             queue,
384                                             fSubmitFence,
385                                             waitCount,
386                                             fWaitSemaphores.data(),
387                                             vkWaitStages.data(),
388                                             /*commandBufferCount*/ 1,
389                                             &fPrimaryCommandBuffer,
390                                             fSignalSemaphores.size(),
391                                             fSignalSemaphores.data(),
392                                             this->isProtected());
393     fWaitSemaphores.clear();
394     fSignalSemaphores.clear();
395     if (submitResult != VK_SUCCESS) {
396         // If we failed to submit because of a device lost, we still need to wait for the fence to
397         // signal before deleting. However, there is an ARM bug (b/359822580) where the driver early
398         // outs on the fence wait if in a device lost state and thus we can't wait on it. Instead,
399         // we just wait on the queue to finish. We're already in a state that's going to cause us to
400         // restart the whole device, so waiting on the queue shouldn't have any performance impact.
401         if (submitResult == VK_ERROR_DEVICE_LOST) {
402             VULKAN_CALL(fSharedContext->interface(), QueueWaitIdle(queue));
403         } else {
404             SkASSERT(submitResult == VK_ERROR_OUT_OF_HOST_MEMORY ||
405                      submitResult == VK_ERROR_OUT_OF_DEVICE_MEMORY);
406         }
407 
408         VULKAN_CALL(fSharedContext->interface(), DestroyFence(device, fSubmitFence, nullptr));
409         fSubmitFence = VK_NULL_HANDLE;
410         return false;
411     }
412     return true;
413 }
414 
isFinished()415 bool VulkanCommandBuffer::isFinished() {
416     SkASSERT(!fActive);
417     if (VK_NULL_HANDLE == fSubmitFence) {
418         return true;
419     }
420 
421     VkResult err;
422     VULKAN_CALL_RESULT_NOCHECK(fSharedContext->interface(), err,
423                                GetFenceStatus(fSharedContext->device(), fSubmitFence));
424     switch (err) {
425         case VK_SUCCESS:
426         case VK_ERROR_DEVICE_LOST:
427             return true;
428 
429         case VK_NOT_READY:
430             return false;
431 
432         default:
433             SKGPU_LOG_F("Error calling vkGetFenceStatus. Error: %d", err);
434             SK_ABORT("Got an invalid fence status");
435             return false;
436     }
437 }
438 
waitUntilFinished()439 void VulkanCommandBuffer::waitUntilFinished() {
440     if (fSubmitFence == VK_NULL_HANDLE) {
441         return;
442     }
443     VULKAN_CALL_ERRCHECK(fSharedContext,
444                          WaitForFences(fSharedContext->device(),
445                                        1,
446                                        &fSubmitFence,
447                                        /*waitAll=*/true,
448                                        /*timeout=*/UINT64_MAX));
449 }
450 
updateIntrinsicUniforms(SkIRect viewport)451 bool VulkanCommandBuffer::updateIntrinsicUniforms(SkIRect viewport) {
452     SkASSERT(fActive && !fActiveRenderPass);
453 
454     // The SkSL has declared these as a top-level interface block, which will use std140 in Vulkan.
455     // If we switch to supporting push constants here, it would be std430 instead.
456     UniformManager intrinsicValues{Layout::kStd140};
457     CollectIntrinsicUniforms(fSharedContext->caps(), viewport, fDstCopyBounds, &intrinsicValues);
458     BindBufferInfo binding =
459             fIntrinsicConstants->add(this, UniformDataBlock::Wrap(&intrinsicValues));
460     if (!binding) {
461         return false;
462     } else if (binding ==
463                fUniformBuffersToBind[VulkanGraphicsPipeline::kIntrinsicUniformBufferIndex]) {
464         return true;  // no binding change needed
465     }
466 
467     fUniformBuffersToBind[VulkanGraphicsPipeline::kIntrinsicUniformBufferIndex] = binding;
468     return true;
469 }
470 
onAddRenderPass(const RenderPassDesc & renderPassDesc,SkIRect renderPassBounds,const Texture * colorTexture,const Texture * resolveTexture,const Texture * depthStencilTexture,SkIRect viewport,const DrawPassList & drawPasses)471 bool VulkanCommandBuffer::onAddRenderPass(const RenderPassDesc& renderPassDesc,
472                                           SkIRect renderPassBounds,
473                                           const Texture* colorTexture,
474                                           const Texture* resolveTexture,
475                                           const Texture* depthStencilTexture,
476                                           SkIRect viewport,
477                                           const DrawPassList& drawPasses) {
478     for (const auto& drawPass : drawPasses) {
479         // Our current implementation of setting texture image layouts does not allow layout changes
480         // once we have already begun a render pass, so prior to any other commands, set the layout
481         // of all sampled textures from the drawpass so they can be sampled from the shader.
482         const skia_private::TArray<sk_sp<TextureProxy>>& sampledTextureProxies =
483                 drawPass->sampledTextures();
484         for (const sk_sp<TextureProxy>& textureProxy : sampledTextureProxies) {
485             VulkanTexture* vulkanTexture = const_cast<VulkanTexture*>(
486                                            static_cast<const VulkanTexture*>(
487                                            textureProxy->texture()));
488             vulkanTexture->setImageLayout(this,
489                                           VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
490                                           VK_ACCESS_SHADER_READ_BIT,
491                                           VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
492                                           false);
493         }
494     }
495     if (fDstCopy.first) {
496         VulkanTexture* vulkanTexture =
497                 const_cast<VulkanTexture*>(static_cast<const VulkanTexture*>(fDstCopy.first));
498         vulkanTexture->setImageLayout(this,
499                                       VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
500                                       VK_ACCESS_SHADER_READ_BIT,
501                                       VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
502                                       false);
503     }
504 
505     if (!this->updateIntrinsicUniforms(viewport)) {
506         return false;
507     }
508     this->setViewport(viewport);
509 
510     if (!this->beginRenderPass(renderPassDesc,
511                                renderPassBounds,
512                                colorTexture,
513                                resolveTexture,
514                                depthStencilTexture)) {
515         return false;
516     }
517 
518     for (const auto& drawPass : drawPasses) {
519         this->addDrawPass(drawPass.get());
520     }
521 
522     this->endRenderPass();
523     return true;
524 }
525 
updateAndBindLoadMSAAInputAttachment(const VulkanTexture & resolveTexture)526 bool VulkanCommandBuffer::updateAndBindLoadMSAAInputAttachment(const VulkanTexture& resolveTexture)
527 {
528     // Fetch a descriptor set that contains one input attachment
529     STArray<1, DescriptorData> inputDescriptors =
530             {VulkanGraphicsPipeline::kInputAttachmentDescriptor};
531     sk_sp<VulkanDescriptorSet> set = fResourceProvider->findOrCreateDescriptorSet(
532             SkSpan<DescriptorData>{&inputDescriptors.front(), inputDescriptors.size()});
533     if (!set) {
534         return false;
535     }
536 
537     VkDescriptorImageInfo textureInfo;
538     memset(&textureInfo, 0, sizeof(VkDescriptorImageInfo));
539     textureInfo.sampler = VK_NULL_HANDLE;
540     textureInfo.imageView =
541             resolveTexture.getImageView(VulkanImageView::Usage::kAttachment)->imageView();
542     textureInfo.imageLayout = resolveTexture.currentLayout();
543 
544     VkWriteDescriptorSet writeInfo;
545     memset(&writeInfo, 0, sizeof(VkWriteDescriptorSet));
546     writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
547     writeInfo.pNext = nullptr;
548     writeInfo.dstSet = *set->descriptorSet();
549     writeInfo.dstBinding = VulkanGraphicsPipeline::kInputAttachmentBindingIndex;
550     writeInfo.dstArrayElement = 0;
551     writeInfo.descriptorCount = 1;
552     writeInfo.descriptorType = DsTypeEnumToVkDs(DescriptorType::kInputAttachment);
553     writeInfo.pImageInfo = &textureInfo;
554     writeInfo.pBufferInfo = nullptr;
555     writeInfo.pTexelBufferView = nullptr;
556 
557     VULKAN_CALL(fSharedContext->interface(),
558                 UpdateDescriptorSets(fSharedContext->device(),
559                                      /*descriptorWriteCount=*/1,
560                                      &writeInfo,
561                                      /*descriptorCopyCount=*/0,
562                                      /*pDescriptorCopies=*/nullptr));
563 
564     VULKAN_CALL(fSharedContext->interface(),
565                 CmdBindDescriptorSets(fPrimaryCommandBuffer,
566                                       VK_PIPELINE_BIND_POINT_GRAPHICS,
567                                       fActiveGraphicsPipeline->layout(),
568                                       VulkanGraphicsPipeline::kInputAttachmentDescSetIndex,
569                                       /*setCount=*/1,
570                                       set->descriptorSet(),
571                                       /*dynamicOffsetCount=*/0,
572                                       /*dynamicOffsets=*/nullptr));
573 
574     this->trackResource(std::move(set));
575     return true;
576 }
577 
loadMSAAFromResolve(const RenderPassDesc & renderPassDesc,VulkanTexture & resolveTexture,SkISize dstDimensions,const SkIRect nativeDrawBounds)578 bool VulkanCommandBuffer::loadMSAAFromResolve(const RenderPassDesc& renderPassDesc,
579                                               VulkanTexture& resolveTexture,
580                                               SkISize dstDimensions,
581                                               const SkIRect nativeDrawBounds) {
582     sk_sp<VulkanGraphicsPipeline> loadPipeline =
583             fResourceProvider->findOrCreateLoadMSAAPipeline(renderPassDesc);
584     if (!loadPipeline) {
585         SKGPU_LOG_E("Unable to create pipeline to load resolve texture into MSAA attachment");
586         return false;
587     }
588 
589     this->bindGraphicsPipeline(loadPipeline.get());
590     // Make sure we do not attempt to bind uniform or texture/sampler descriptors because we do
591     // not use them for loading MSAA from resolve.
592     fBindUniformBuffers = false;
593     fBindTextureSamplers = false;
594 
595     this->setScissor(SkIRect::MakeXYWH(0, 0, dstDimensions.width(), dstDimensions.height()));
596 
597     if (!this->updateAndBindLoadMSAAInputAttachment(resolveTexture)) {
598         SKGPU_LOG_E("Unable to update and bind an input attachment descriptor for loading MSAA "
599                     "from resolve");
600         return false;
601     }
602 
603     // Update and bind uniform descriptor set
604     int w = nativeDrawBounds.width();
605     int h = nativeDrawBounds.height();
606 
607     // dst rect edges in NDC (-1 to 1)
608     int dw = dstDimensions.width();
609     int dh = dstDimensions.height();
610     float dx0 = 2.f * nativeDrawBounds.fLeft / dw - 1.f;
611     float dx1 = 2.f * (nativeDrawBounds.fLeft + w) / dw - 1.f;
612     float dy0 = 2.f * nativeDrawBounds.fTop / dh - 1.f;
613     float dy1 = 2.f * (nativeDrawBounds.fTop + h) / dh - 1.f;
614     float uniData[] = {dx1 - dx0, dy1 - dy0, dx0, dy0};  // posXform
615 
616     this->pushConstants(VK_SHADER_STAGE_VERTEX_BIT,
617                         /*offset=*/0,
618                         /*size=*/sizeof(uniData),
619                         uniData);
620 
621     this->draw(PrimitiveType::kTriangleStrip, /*baseVertex=*/0, /*vertexCount=*/4);
622     this->nextSubpass();
623 
624     // If we loaded the resolve attachment, then we would have set the image layout to be
625     // VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL so that it could be used at the start as an
626     // input attachment. However, when we switched to the main subpass it will transition the
627     // layout internally to VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL. Thus we need to update our
628     // tracking of the layout to match the new layout.
629     resolveTexture.updateImageLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
630 
631     // After using a distinct descriptor set layout for loading MSAA from resolve, we will need to
632     // (re-)bind any descriptor sets.
633     fBindUniformBuffers = true;
634     fBindTextureSamplers = true;
635     return true;
636 }
637 
638 namespace {
setup_texture_layouts(VulkanCommandBuffer * cmdBuf,VulkanTexture * colorTexture,VulkanTexture * resolveTexture,VulkanTexture * depthStencilTexture,bool loadMSAAFromResolve)639 void setup_texture_layouts(VulkanCommandBuffer* cmdBuf,
640                            VulkanTexture* colorTexture,
641                            VulkanTexture* resolveTexture,
642                            VulkanTexture* depthStencilTexture,
643                            bool loadMSAAFromResolve) {
644     if (colorTexture) {
645         colorTexture->setImageLayout(cmdBuf,
646                                      VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
647                                      VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
648                                      VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
649                                      VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
650                                      /*byRegion=*/false);
651         if (resolveTexture) {
652             if (loadMSAAFromResolve) {
653                 // When loading MSAA from resolve, the texture is used in the first subpass as an
654                 // input attachment. Subsequent subpass(es) need the resolve texture to provide read
655                 // access to the color attachment (for use cases such as blending), so add access
656                 // and pipeline stage flags for both usages.
657                 resolveTexture->setImageLayout(cmdBuf,
658                                                VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
659                                                VK_ACCESS_INPUT_ATTACHMENT_READ_BIT |
660                                                VK_ACCESS_COLOR_ATTACHMENT_READ_BIT,
661                                                VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
662                                                VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
663                                                /*byRegion=*/false);
664             } else {
665                 resolveTexture->setImageLayout(cmdBuf,
666                                                VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
667                                                VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
668                                                VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
669                                                VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
670                                                /*byRegion=*/false);
671             }
672         }
673     }
674     if (depthStencilTexture) {
675         depthStencilTexture->setImageLayout(cmdBuf,
676                                             VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
677                                             VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT,
678                                             VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
679                                             VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
680                                             /*byRegion=*/false);
681     }
682 }
683 
track_attachments(VulkanCommandBuffer * cmdBuf,VulkanTexture * colorTexture,VulkanTexture * resolveTexture,VulkanTexture * depthStencilTexture)684 void track_attachments(VulkanCommandBuffer* cmdBuf,
685                        VulkanTexture* colorTexture,
686                        VulkanTexture* resolveTexture,
687                        VulkanTexture* depthStencilTexture) {
688     if (colorTexture) {
689         cmdBuf->trackResource(sk_ref_sp(colorTexture));
690     }
691     if (resolveTexture){
692         cmdBuf->trackResource(sk_ref_sp(resolveTexture));
693     }
694     if (depthStencilTexture) {
695         cmdBuf->trackResource(sk_ref_sp(depthStencilTexture));
696     }
697 }
698 
gather_attachment_views(skia_private::TArray<VkImageView> & attachmentViews,VulkanTexture * colorTexture,VulkanTexture * resolveTexture,VulkanTexture * depthStencilTexture)699 void gather_attachment_views(skia_private::TArray<VkImageView>& attachmentViews,
700                              VulkanTexture* colorTexture,
701                              VulkanTexture* resolveTexture,
702                              VulkanTexture* depthStencilTexture) {
703     if (colorTexture) {
704         VkImageView& colorAttachmentView = attachmentViews.push_back();
705         colorAttachmentView =
706                 colorTexture->getImageView(VulkanImageView::Usage::kAttachment)->imageView();
707 
708         if (resolveTexture) {
709             VkImageView& resolveView = attachmentViews.push_back();
710             resolveView =
711                     resolveTexture->getImageView(VulkanImageView::Usage::kAttachment)->imageView();
712         }
713     }
714 
715     if (depthStencilTexture) {
716         VkImageView& stencilView = attachmentViews.push_back();
717         stencilView =
718                 depthStencilTexture->getImageView(VulkanImageView::Usage::kAttachment)->imageView();
719     }
720 }
721 
gather_clear_values(STArray<VulkanRenderPass::kMaxExpectedAttachmentCount,VkClearValue> & clearValues,const RenderPassDesc & renderPassDesc,VulkanTexture * colorTexture,VulkanTexture * depthStencilTexture,int depthStencilAttachmentIdx)722 void gather_clear_values(
723         STArray<VulkanRenderPass::kMaxExpectedAttachmentCount, VkClearValue>& clearValues,
724         const RenderPassDesc& renderPassDesc,
725         VulkanTexture* colorTexture,
726         VulkanTexture* depthStencilTexture,
727         int depthStencilAttachmentIdx) {
728     clearValues.push_back_n(VulkanRenderPass::kMaxExpectedAttachmentCount);
729     if (colorTexture) {
730         VkClearValue& colorAttachmentClear =
731                 clearValues.at(VulkanRenderPass::kColorAttachmentIdx);
732         memset(&colorAttachmentClear, 0, sizeof(VkClearValue));
733         colorAttachmentClear.color = {{renderPassDesc.fClearColor[0],
734                                        renderPassDesc.fClearColor[1],
735                                        renderPassDesc.fClearColor[2],
736                                        renderPassDesc.fClearColor[3]}};
737     }
738     // Resolve texture does not have a clear value
739     if (depthStencilTexture) {
740         VkClearValue& depthStencilAttachmentClear = clearValues.at(depthStencilAttachmentIdx);
741         memset(&depthStencilAttachmentClear, 0, sizeof(VkClearValue));
742         depthStencilAttachmentClear.depthStencil = {renderPassDesc.fClearDepth,
743                                                     renderPassDesc.fClearStencil};
744     }
745 }
746 
747 // The RenderArea bounds we pass into BeginRenderPass must have a start x value that is a multiple
748 // of the granularity. The width must also be a multiple of the granularity or equal to the width
749 // of the entire attachment. Similar requirements apply to the y and height components.
get_render_area(const SkIRect & srcBounds,const VkExtent2D & granularity,int maxWidth,int maxHeight)750 VkRect2D get_render_area(const SkIRect& srcBounds,
751                          const VkExtent2D& granularity,
752                          int maxWidth,
753                          int maxHeight) {
754     SkIRect dstBounds;
755     // Adjust Width
756     if (granularity.width == 0 || granularity.width == 1) {
757         dstBounds.fLeft = srcBounds.fLeft;
758         dstBounds.fRight = srcBounds.fRight;
759     } else {
760         // Start with the right side of rect so we know if we end up going past the maxWidth.
761         int rightAdj = srcBounds.fRight % granularity.width;
762         if (rightAdj != 0) {
763             rightAdj = granularity.width - rightAdj;
764         }
765         dstBounds.fRight = srcBounds.fRight + rightAdj;
766         if (dstBounds.fRight > maxWidth) {
767             dstBounds.fRight = maxWidth;
768             dstBounds.fLeft = 0;
769         } else {
770             dstBounds.fLeft = srcBounds.fLeft - srcBounds.fLeft % granularity.width;
771         }
772     }
773 
774     if (granularity.height == 0 || granularity.height == 1) {
775         dstBounds.fTop = srcBounds.fTop;
776         dstBounds.fBottom = srcBounds.fBottom;
777     } else {
778         // Start with the bottom side of rect so we know if we end up going past the maxHeight.
779         int bottomAdj = srcBounds.fBottom % granularity.height;
780         if (bottomAdj != 0) {
781             bottomAdj = granularity.height - bottomAdj;
782         }
783         dstBounds.fBottom = srcBounds.fBottom + bottomAdj;
784         if (dstBounds.fBottom > maxHeight) {
785             dstBounds.fBottom = maxHeight;
786             dstBounds.fTop = 0;
787         } else {
788             dstBounds.fTop = srcBounds.fTop - srcBounds.fTop % granularity.height;
789         }
790     }
791 
792     VkRect2D renderArea;
793     renderArea.offset = { dstBounds.fLeft , dstBounds.fTop };
794     renderArea.extent = { (uint32_t)dstBounds.width(), (uint32_t)dstBounds.height() };
795     return renderArea;
796 }
797 
798 } // anonymous namespace
799 
beginRenderPass(const RenderPassDesc & renderPassDesc,SkIRect renderPassBounds,const Texture * colorTexture,const Texture * resolveTexture,const Texture * depthStencilTexture)800 bool VulkanCommandBuffer::beginRenderPass(const RenderPassDesc& renderPassDesc,
801                                           SkIRect renderPassBounds,
802                                           const Texture* colorTexture,
803                                           const Texture* resolveTexture,
804                                           const Texture* depthStencilTexture) {
805     // TODO: Check that Textures match RenderPassDesc
806     VulkanTexture* vulkanColorTexture =
807             const_cast<VulkanTexture*>(static_cast<const VulkanTexture*>(colorTexture));
808     VulkanTexture* vulkanResolveTexture =
809             const_cast<VulkanTexture*>(static_cast<const VulkanTexture*>(resolveTexture));
810     VulkanTexture* vulkanDepthStencilTexture =
811             const_cast<VulkanTexture*>(static_cast<const VulkanTexture*>(depthStencilTexture));
812 
813     SkASSERT(resolveTexture ? renderPassDesc.fColorResolveAttachment.fStoreOp == StoreOp::kStore
814                             : true);
815 
816     // Determine if we need to load MSAA from resolve, and if so, make certain that key conditions
817     // are met before proceeding.
818     bool loadMSAAFromResolve = renderPassDesc.fColorResolveAttachment.fTextureInfo.isValid() &&
819                                renderPassDesc.fColorResolveAttachment.fLoadOp == LoadOp::kLoad;
820     if (loadMSAAFromResolve && (!vulkanResolveTexture || !vulkanColorTexture ||
821                                 !vulkanResolveTexture->supportsInputAttachmentUsage())) {
822         SKGPU_LOG_E("Cannot begin render pass. In order to load MSAA from resolve, the color "
823                     "attachment must have input attachment usage and both the color and resolve "
824                     "attachments must be valid.");
825         return false;
826     }
827 
828     track_attachments(this, vulkanColorTexture, vulkanResolveTexture, vulkanDepthStencilTexture);
829 
830     // Before beginning a renderpass, set all textures to the appropriate image layout.
831     setup_texture_layouts(this,
832                           vulkanColorTexture,
833                           vulkanResolveTexture,
834                           vulkanDepthStencilTexture,
835                           loadMSAAFromResolve);
836 
837     static constexpr int kMaxNumAttachments = 3;
838     // Gather attachment views neeeded for frame buffer creation.
839     skia_private::TArray<VkImageView> attachmentViews;
840     gather_attachment_views(
841             attachmentViews, vulkanColorTexture, vulkanResolveTexture, vulkanDepthStencilTexture);
842 
843     // Gather clear values needed for RenderPassBeginInfo. Indexed by attachment number.
844     STArray<kMaxNumAttachments, VkClearValue> clearValues;
845     // The depth/stencil attachment can be at attachment index 1 or 2 depending on whether there is
846     // a resolve texture attachment for this renderpass.
847     int depthStencilAttachmentIndex = resolveTexture ? 2 : 1;
848     gather_clear_values(clearValues,
849                         renderPassDesc,
850                         vulkanColorTexture,
851                         vulkanDepthStencilTexture,
852                         depthStencilAttachmentIndex);
853 
854     sk_sp<VulkanRenderPass> vulkanRenderPass =
855             fResourceProvider->findOrCreateRenderPass(renderPassDesc, /*compatibleOnly=*/false);
856     if (!vulkanRenderPass) {
857         SKGPU_LOG_W("Could not create Vulkan RenderPass");
858         return false;
859     }
860     this->submitPipelineBarriers();
861     this->trackResource(vulkanRenderPass);
862 
863     int frameBufferWidth = 0;
864     int frameBufferHeight = 0;
865     if (colorTexture) {
866         frameBufferWidth = colorTexture->dimensions().width();
867         frameBufferHeight = colorTexture->dimensions().height();
868     } else if (depthStencilTexture) {
869         frameBufferWidth = depthStencilTexture->dimensions().width();
870         frameBufferHeight = depthStencilTexture->dimensions().height();
871     }
872     sk_sp<VulkanFramebuffer> framebuffer = fResourceProvider->createFramebuffer(fSharedContext,
873                                                                                 attachmentViews,
874                                                                                 *vulkanRenderPass,
875                                                                                 frameBufferWidth,
876                                                                                 frameBufferHeight);
877     if (!framebuffer) {
878         SKGPU_LOG_W("Could not create Vulkan Framebuffer");
879         return false;
880     }
881 
882     VkExtent2D granularity;
883     // Get granularity for this render pass
884     VULKAN_CALL(fSharedContext->interface(),
885                 GetRenderAreaGranularity(fSharedContext->device(),
886                                          vulkanRenderPass->renderPass(),
887                                          &granularity));
888 
889     bool useFullBounds = loadMSAAFromResolve &&
890                          fSharedContext->vulkanCaps().mustLoadFullImageForMSAA();
891 
892     VkRect2D renderArea = get_render_area(useFullBounds ? SkIRect::MakeWH(frameBufferWidth,
893                                                                           frameBufferHeight)
894                                                         : renderPassBounds,
895                                           granularity,
896                                           frameBufferWidth,
897                                           frameBufferHeight);
898 
899     VkRenderPassBeginInfo beginInfo;
900     memset(&beginInfo, 0, sizeof(VkRenderPassBeginInfo));
901     beginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
902     beginInfo.pNext = nullptr;
903     beginInfo.renderPass = vulkanRenderPass->renderPass();
904     beginInfo.framebuffer = framebuffer->framebuffer();
905     beginInfo.renderArea = renderArea;
906     beginInfo.clearValueCount = clearValues.size();
907     beginInfo.pClearValues = clearValues.begin();
908 
909     // Submit pipeline barriers to ensure any image layout transitions are recorded prior to
910     // beginning the render pass.
911     this->submitPipelineBarriers();
912     // TODO: If we add support for secondary command buffers, dynamically determine subpass contents
913     VULKAN_CALL(fSharedContext->interface(),
914                 CmdBeginRenderPass(fPrimaryCommandBuffer,
915                                    &beginInfo,
916                                    VK_SUBPASS_CONTENTS_INLINE));
917     fActiveRenderPass = true;
918 
919     SkIRect nativeBounds = SkIRect::MakeXYWH(renderArea.offset.x,
920                                              renderArea.offset.y,
921                                              renderArea.extent.width,
922                                              renderArea.extent.height);
923     if (loadMSAAFromResolve && !this->loadMSAAFromResolve(renderPassDesc,
924                                                           *vulkanResolveTexture,
925                                                           vulkanColorTexture->dimensions(),
926                                                           nativeBounds)) {
927         SKGPU_LOG_E("Failed to load MSAA from resolve");
928         this->endRenderPass();
929         return false;
930     }
931 
932     // Once we have an active render pass, the command buffer should hold on to a frame buffer ref.
933     this->trackResource(std::move(framebuffer));
934     return true;
935 }
936 
endRenderPass()937 void VulkanCommandBuffer::endRenderPass() {
938     SkASSERT(fActive);
939     VULKAN_CALL(fSharedContext->interface(), CmdEndRenderPass(fPrimaryCommandBuffer));
940     fActiveRenderPass = false;
941 }
942 
addDrawPass(const DrawPass * drawPass)943 void VulkanCommandBuffer::addDrawPass(const DrawPass* drawPass) {
944     drawPass->addResourceRefs(this);
945     for (auto [type, cmdPtr] : drawPass->commands()) {
946         switch (type) {
947             case DrawPassCommands::Type::kBindGraphicsPipeline: {
948                 auto bgp = static_cast<DrawPassCommands::BindGraphicsPipeline*>(cmdPtr);
949                 this->bindGraphicsPipeline(drawPass->getPipeline(bgp->fPipelineIndex));
950                 break;
951             }
952             case DrawPassCommands::Type::kSetBlendConstants: {
953                 auto sbc = static_cast<DrawPassCommands::SetBlendConstants*>(cmdPtr);
954                 this->setBlendConstants(sbc->fBlendConstants);
955                 break;
956             }
957             case DrawPassCommands::Type::kBindUniformBuffer: {
958                 auto bub = static_cast<DrawPassCommands::BindUniformBuffer*>(cmdPtr);
959                 this->recordBufferBindingInfo(bub->fInfo, bub->fSlot);
960                 break;
961             }
962             case DrawPassCommands::Type::kBindDrawBuffers: {
963                 auto bdb = static_cast<DrawPassCommands::BindDrawBuffers*>(cmdPtr);
964                 this->bindDrawBuffers(
965                         bdb->fVertices, bdb->fInstances, bdb->fIndices, bdb->fIndirect);
966                 break;
967             }
968             case DrawPassCommands::Type::kBindTexturesAndSamplers: {
969                 auto bts = static_cast<DrawPassCommands::BindTexturesAndSamplers*>(cmdPtr);
970                 this->recordTextureAndSamplerDescSet(drawPass, bts);
971                 break;
972             }
973             case DrawPassCommands::Type::kSetScissor: {
974                 auto ss = static_cast<DrawPassCommands::SetScissor*>(cmdPtr);
975                 this->setScissor(ss->fScissor);
976                 break;
977             }
978             case DrawPassCommands::Type::kDraw: {
979                 auto draw = static_cast<DrawPassCommands::Draw*>(cmdPtr);
980                 this->draw(draw->fType, draw->fBaseVertex, draw->fVertexCount);
981                 break;
982             }
983             case DrawPassCommands::Type::kDrawIndexed: {
984                 auto draw = static_cast<DrawPassCommands::DrawIndexed*>(cmdPtr);
985                 this->drawIndexed(
986                         draw->fType, draw->fBaseIndex, draw->fIndexCount, draw->fBaseVertex);
987                 break;
988             }
989             case DrawPassCommands::Type::kDrawInstanced: {
990                 auto draw = static_cast<DrawPassCommands::DrawInstanced*>(cmdPtr);
991                 this->drawInstanced(draw->fType,
992                                     draw->fBaseVertex,
993                                     draw->fVertexCount,
994                                     draw->fBaseInstance,
995                                     draw->fInstanceCount);
996                 break;
997             }
998             case DrawPassCommands::Type::kDrawIndexedInstanced: {
999                 auto draw = static_cast<DrawPassCommands::DrawIndexedInstanced*>(cmdPtr);
1000                 this->drawIndexedInstanced(draw->fType,
1001                                            draw->fBaseIndex,
1002                                            draw->fIndexCount,
1003                                            draw->fBaseVertex,
1004                                            draw->fBaseInstance,
1005                                            draw->fInstanceCount);
1006                 break;
1007             }
1008             case DrawPassCommands::Type::kDrawIndirect: {
1009                 auto draw = static_cast<DrawPassCommands::DrawIndirect*>(cmdPtr);
1010                 this->drawIndirect(draw->fType);
1011                 break;
1012             }
1013             case DrawPassCommands::Type::kDrawIndexedIndirect: {
1014                 auto draw = static_cast<DrawPassCommands::DrawIndexedIndirect*>(cmdPtr);
1015                 this->drawIndexedIndirect(draw->fType);
1016                 break;
1017             }
1018         }
1019     }
1020 }
1021 
bindGraphicsPipeline(const GraphicsPipeline * graphicsPipeline)1022 void VulkanCommandBuffer::bindGraphicsPipeline(const GraphicsPipeline* graphicsPipeline) {
1023     SkASSERT(fActiveRenderPass);
1024     fActiveGraphicsPipeline = static_cast<const VulkanGraphicsPipeline*>(graphicsPipeline);
1025     VULKAN_CALL(fSharedContext->interface(), CmdBindPipeline(fPrimaryCommandBuffer,
1026                                                              VK_PIPELINE_BIND_POINT_GRAPHICS,
1027                                                              fActiveGraphicsPipeline->pipeline()));
1028     // TODO(b/293924877): Compare pipeline layouts. If 2 pipelines have the same pipeline layout,
1029     // then descriptor sets do not need to be re-bound. For now, simply force a re-binding of
1030     // descriptor sets with any new bindGraphicsPipeline DrawPassCommand.
1031     fBindUniformBuffers = true;
1032 
1033     if (graphicsPipeline->dstReadRequirement() == DstReadRequirement::kTextureCopy &&
1034         graphicsPipeline->numFragTexturesAndSamplers() == 1) {
1035         // The only texture-sampler that the pipeline declares must be the dstCopy, which means
1036         // there are no other textures that will trigger BindTextureAndSampler commands in a
1037         // DrawPass (e.g. solid-color + dst-read-requiring blend). Configure the texture binding
1038         // up front in this case.
1039         this->recordTextureAndSamplerDescSet(/*drawPass=*/nullptr, /*command=*/nullptr);
1040     }
1041 }
1042 
setBlendConstants(float * blendConstants)1043 void VulkanCommandBuffer::setBlendConstants(float* blendConstants) {
1044     SkASSERT(fActive);
1045     if (0 != memcmp(blendConstants, fCachedBlendConstant, 4 * sizeof(float))) {
1046         VULKAN_CALL(fSharedContext->interface(),
1047                     CmdSetBlendConstants(fPrimaryCommandBuffer, blendConstants));
1048         memcpy(fCachedBlendConstant, blendConstants, 4 * sizeof(float));
1049     }
1050 }
1051 
recordBufferBindingInfo(const BindBufferInfo & info,UniformSlot slot)1052 void VulkanCommandBuffer::recordBufferBindingInfo(const BindBufferInfo& info, UniformSlot slot) {
1053     unsigned int bufferIndex = 0;
1054     switch (slot) {
1055         case UniformSlot::kRenderStep:
1056             bufferIndex = VulkanGraphicsPipeline::kRenderStepUniformBufferIndex;
1057             break;
1058         case UniformSlot::kPaint:
1059             bufferIndex = VulkanGraphicsPipeline::kPaintUniformBufferIndex;
1060             break;
1061         case UniformSlot::kGradient:
1062             bufferIndex = VulkanGraphicsPipeline::kGradientBufferIndex;
1063             break;
1064         default:
1065             SkASSERT(false);
1066     }
1067 
1068     fUniformBuffersToBind[bufferIndex] = info;
1069     fBindUniformBuffers = true;
1070 }
1071 
syncDescriptorSets()1072 void VulkanCommandBuffer::syncDescriptorSets() {
1073     if (fBindUniformBuffers) {
1074         this->bindUniformBuffers();
1075         // Changes to descriptor sets in lower slot numbers disrupt later set bindings. Currently,
1076         // the descriptor set which houses uniform buffers is at a lower slot than the texture /
1077         // sampler set, so rebinding uniform buffers necessitates re-binding any texture/samplers.
1078         fBindTextureSamplers = true;
1079     }
1080     if (fBindTextureSamplers) {
1081         this->bindTextureSamplers();
1082     }
1083 }
1084 
bindUniformBuffers()1085 void VulkanCommandBuffer::bindUniformBuffers() {
1086     fBindUniformBuffers = false;
1087 
1088     // We always bind at least one uniform buffer descriptor for intrinsic uniforms, but can bind
1089     // up to three (one for render step uniforms, one for paint uniforms).
1090     STArray<VulkanGraphicsPipeline::kNumUniformBuffers, DescriptorData> descriptors;
1091     descriptors.push_back(VulkanGraphicsPipeline::kIntrinsicUniformBufferDescriptor);
1092 
1093     DescriptorType uniformBufferType = fSharedContext->caps()->storageBufferSupport()
1094                                             ? DescriptorType::kStorageBuffer
1095                                             : DescriptorType::kUniformBuffer;
1096     if (fActiveGraphicsPipeline->hasStepUniforms() &&
1097         fUniformBuffersToBind[VulkanGraphicsPipeline::kRenderStepUniformBufferIndex].fBuffer) {
1098         descriptors.push_back({
1099             uniformBufferType,
1100             /*count=*/1,
1101             VulkanGraphicsPipeline::kRenderStepUniformBufferIndex,
1102             PipelineStageFlags::kVertexShader | PipelineStageFlags::kFragmentShader});
1103     }
1104     if (fActiveGraphicsPipeline->hasPaintUniforms() &&
1105         fUniformBuffersToBind[VulkanGraphicsPipeline::kPaintUniformBufferIndex].fBuffer) {
1106         descriptors.push_back({
1107             uniformBufferType,
1108             /*count=*/1,
1109             VulkanGraphicsPipeline::kPaintUniformBufferIndex,
1110             PipelineStageFlags::kFragmentShader});
1111     }
1112     if (fActiveGraphicsPipeline->hasGradientBuffer() &&
1113         fUniformBuffersToBind[VulkanGraphicsPipeline::kGradientBufferIndex].fBuffer) {
1114         SkASSERT(fSharedContext->caps()->gradientBufferSupport() &&
1115                  fSharedContext->caps()->storageBufferSupport());
1116         descriptors.push_back({
1117             DescriptorType::kStorageBuffer,
1118             /*count=*/1,
1119             VulkanGraphicsPipeline::kGradientBufferIndex,
1120             PipelineStageFlags::kFragmentShader});
1121     }
1122 
1123     sk_sp<VulkanDescriptorSet> descSet = fResourceProvider->findOrCreateUniformBuffersDescriptorSet(
1124             descriptors, fUniformBuffersToBind);
1125     if (!descSet) {
1126         SKGPU_LOG_E("Unable to find or create uniform descriptor set");
1127         return;
1128     }
1129     skia_private::AutoSTMalloc<VulkanGraphicsPipeline::kNumUniformBuffers, uint32_t>
1130             dynamicOffsets(descriptors.size());
1131     for (int i = 0; i < descriptors.size(); i++) {
1132         int descriptorBindingIndex = descriptors[i].fBindingIndex;
1133         SkASSERT(static_cast<unsigned long>(descriptorBindingIndex) < fUniformBuffersToBind.size());
1134         const auto& bindInfo = fUniformBuffersToBind[descriptorBindingIndex];
1135 #ifdef SK_DEBUG
1136         if (descriptors[i].fPipelineStageFlags & PipelineStageFlags::kVertexShader) {
1137             // TODO (b/356874190): Renable once we fix the intrinsic uniform buffer to not be
1138             // protected.
1139             //SkASSERT(bindInfo.fBuffer->isProtected() == Protected::kNo);
1140         }
1141 #endif
1142         dynamicOffsets[i] = bindInfo.fOffset;
1143     }
1144 
1145     VULKAN_CALL(fSharedContext->interface(),
1146                 CmdBindDescriptorSets(fPrimaryCommandBuffer,
1147                                       VK_PIPELINE_BIND_POINT_GRAPHICS,
1148                                       fActiveGraphicsPipeline->layout(),
1149                                       VulkanGraphicsPipeline::kUniformBufferDescSetIndex,
1150                                       /*setCount=*/1,
1151                                       descSet->descriptorSet(),
1152                                       descriptors.size(),
1153                                       dynamicOffsets.get()));
1154     this->trackResource(std::move(descSet));
1155 }
1156 
bindDrawBuffers(const BindBufferInfo & vertices,const BindBufferInfo & instances,const BindBufferInfo & indices,const BindBufferInfo & indirect)1157 void VulkanCommandBuffer::bindDrawBuffers(const BindBufferInfo& vertices,
1158                                           const BindBufferInfo& instances,
1159                                           const BindBufferInfo& indices,
1160                                           const BindBufferInfo& indirect) {
1161     this->bindVertexBuffers(vertices.fBuffer,
1162                             vertices.fOffset,
1163                             instances.fBuffer,
1164                             instances.fOffset);
1165     this->bindIndexBuffer(indices.fBuffer, indices.fOffset);
1166     this->bindIndirectBuffer(indirect.fBuffer, indirect.fOffset);
1167 }
1168 
bindVertexBuffers(const Buffer * vertexBuffer,size_t vertexOffset,const Buffer * instanceBuffer,size_t instanceOffset)1169 void VulkanCommandBuffer::bindVertexBuffers(const Buffer* vertexBuffer,
1170                                             size_t vertexOffset,
1171                                             const Buffer* instanceBuffer,
1172                                             size_t instanceOffset) {
1173     this->bindInputBuffer(vertexBuffer, vertexOffset,
1174                           VulkanGraphicsPipeline::kVertexBufferIndex);
1175     this->bindInputBuffer(instanceBuffer, instanceOffset,
1176                           VulkanGraphicsPipeline::kInstanceBufferIndex);
1177 }
1178 
bindInputBuffer(const Buffer * buffer,VkDeviceSize offset,uint32_t binding)1179 void VulkanCommandBuffer::bindInputBuffer(const Buffer* buffer, VkDeviceSize offset,
1180                                           uint32_t binding) {
1181     if (buffer) {
1182         SkASSERT(buffer->isProtected() == Protected::kNo);
1183         VkBuffer vkBuffer = static_cast<const VulkanBuffer*>(buffer)->vkBuffer();
1184         SkASSERT(vkBuffer != VK_NULL_HANDLE);
1185         if (vkBuffer != fBoundInputBuffers[binding] ||
1186             offset != fBoundInputBufferOffsets[binding]) {
1187             VULKAN_CALL(fSharedContext->interface(),
1188                         CmdBindVertexBuffers(fPrimaryCommandBuffer,
1189                                              binding,
1190                                              /*bindingCount=*/1,
1191                                              &vkBuffer,
1192                                              &offset));
1193             fBoundInputBuffers[binding] = vkBuffer;
1194             fBoundInputBufferOffsets[binding] = offset;
1195             this->trackResource(sk_ref_sp(buffer));
1196         }
1197     }
1198 }
1199 
bindIndexBuffer(const Buffer * indexBuffer,size_t offset)1200 void VulkanCommandBuffer::bindIndexBuffer(const Buffer* indexBuffer, size_t offset) {
1201     if (indexBuffer) {
1202         SkASSERT(indexBuffer->isProtected() == Protected::kNo);
1203         VkBuffer vkBuffer = static_cast<const VulkanBuffer*>(indexBuffer)->vkBuffer();
1204         SkASSERT(vkBuffer != VK_NULL_HANDLE);
1205         if (vkBuffer != fBoundIndexBuffer || offset != fBoundIndexBufferOffset) {
1206             VULKAN_CALL(fSharedContext->interface(), CmdBindIndexBuffer(fPrimaryCommandBuffer,
1207                                                                         vkBuffer,
1208                                                                         offset,
1209                                                                         VK_INDEX_TYPE_UINT16));
1210             fBoundIndexBuffer = vkBuffer;
1211             fBoundIndexBufferOffset = offset;
1212             this->trackResource(sk_ref_sp(indexBuffer));
1213         }
1214     } else {
1215         fBoundIndexBuffer = VK_NULL_HANDLE;
1216         fBoundIndexBufferOffset = 0;
1217     }
1218 }
1219 
bindIndirectBuffer(const Buffer * indirectBuffer,size_t offset)1220 void VulkanCommandBuffer::bindIndirectBuffer(const Buffer* indirectBuffer, size_t offset) {
1221     // Indirect buffers are not bound via the command buffer, but specified in the draw cmd.
1222     if (indirectBuffer) {
1223         SkASSERT(indirectBuffer->isProtected() == Protected::kNo);
1224         fBoundIndirectBuffer = static_cast<const VulkanBuffer*>(indirectBuffer)->vkBuffer();
1225         fBoundIndirectBufferOffset = offset;
1226         this->trackResource(sk_ref_sp(indirectBuffer));
1227     } else {
1228         fBoundIndirectBuffer = VK_NULL_HANDLE;
1229         fBoundIndirectBufferOffset = 0;
1230     }
1231 }
1232 
recordTextureAndSamplerDescSet(const DrawPass * drawPass,const DrawPassCommands::BindTexturesAndSamplers * command)1233 void VulkanCommandBuffer::recordTextureAndSamplerDescSet(
1234         const DrawPass* drawPass, const DrawPassCommands::BindTexturesAndSamplers* command) {
1235     SkASSERT(SkToBool(drawPass) == SkToBool(command));
1236     SkASSERT(fActiveGraphicsPipeline);
1237     // Add one extra texture for dst copies, which is not included in the command itself.
1238     int numTexSamplers = command ? command->fNumTexSamplers : 0;
1239     if (fActiveGraphicsPipeline->dstReadRequirement() == DstReadRequirement::kTextureCopy) {
1240         numTexSamplers++;
1241     }
1242 
1243     if (numTexSamplers == 0) {
1244         fNumTextureSamplers = 0;
1245         fTextureSamplerDescSetToBind = VK_NULL_HANDLE;
1246         fBindTextureSamplers = false;
1247         return;
1248     }
1249 
1250     // Query resource provider to obtain a descriptor set for the texture/samplers
1251     TArray<DescriptorData> descriptors(numTexSamplers);
1252     if (command) {
1253         for (int i = 0; i < command->fNumTexSamplers; i++) {
1254             auto sampler = static_cast<const VulkanSampler*>(
1255                     drawPass->getSampler(command->fSamplerIndices[i]));
1256 
1257             const Sampler* immutableSampler = (sampler && sampler->ycbcrConversion()) ? sampler
1258                                                                                       : nullptr;
1259             descriptors.push_back({DescriptorType::kCombinedTextureSampler,
1260                                    /*count=*/1,
1261                                    /*bindingIdx=*/i,
1262                                    PipelineStageFlags::kFragmentShader,
1263                                    immutableSampler});
1264         }
1265     }
1266     // If required the dst copy texture+sampler is the last one in the descriptor set
1267     if (fActiveGraphicsPipeline->dstReadRequirement() == DstReadRequirement::kTextureCopy) {
1268         descriptors.push_back({DescriptorType::kCombinedTextureSampler,
1269                                /*count=*/1,
1270                                /*bindingIdx=*/numTexSamplers-1,
1271                                PipelineStageFlags::kFragmentShader,
1272                                /*immutableSampler=*/nullptr});
1273     }
1274     SkASSERT(descriptors.size() == numTexSamplers);
1275     sk_sp<VulkanDescriptorSet> set = fResourceProvider->findOrCreateDescriptorSet(
1276             SkSpan<DescriptorData>{&descriptors.front(), descriptors.size()});
1277 
1278     if (!set) {
1279         SKGPU_LOG_E("Unable to find or create descriptor set");
1280         fNumTextureSamplers = 0;
1281         fTextureSamplerDescSetToBind = VK_NULL_HANDLE;
1282         fBindTextureSamplers = false;
1283         return;
1284     }
1285     // Populate the descriptor set with texture/sampler descriptors
1286     TArray<VkWriteDescriptorSet> writeDescriptorSets(numTexSamplers);
1287     TArray<VkDescriptorImageInfo> descriptorImageInfos(numTexSamplers);
1288     auto appendTextureSampler = [&](const VulkanTexture* texture, const VulkanSampler* sampler) {
1289         if (!texture || !sampler) {
1290             // TODO(b/294198324): Investigate the root cause for null texture or samplers on
1291             // Ubuntu QuadP400 GPU
1292             SKGPU_LOG_E("Texture and sampler must not be null");
1293             fNumTextureSamplers = 0;
1294             fTextureSamplerDescSetToBind = VK_NULL_HANDLE;
1295             fBindTextureSamplers = false;
1296             return false;
1297         }
1298 
1299         VkDescriptorImageInfo& textureInfo = descriptorImageInfos.push_back();
1300         memset(&textureInfo, 0, sizeof(VkDescriptorImageInfo));
1301         textureInfo.sampler = sampler->ycbcrConversion() ? VK_NULL_HANDLE : sampler->vkSampler();
1302         textureInfo.imageView =
1303                 texture->getImageView(VulkanImageView::Usage::kShaderInput)->imageView();
1304         textureInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1305 
1306         VkWriteDescriptorSet& writeInfo = writeDescriptorSets.push_back();
1307         memset(&writeInfo, 0, sizeof(VkWriteDescriptorSet));
1308         writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
1309         writeInfo.pNext = nullptr;
1310         writeInfo.dstSet = *set->descriptorSet();
1311         writeInfo.dstBinding = writeDescriptorSets.size() - 1;
1312         writeInfo.dstArrayElement = 0;
1313         writeInfo.descriptorCount = 1;
1314         writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1315         writeInfo.pImageInfo = &textureInfo;
1316         writeInfo.pBufferInfo = nullptr;
1317         writeInfo.pTexelBufferView = nullptr;
1318 
1319         return true;
1320     };
1321 
1322     if (command) {
1323         for (int i = 0; i < command->fNumTexSamplers; ++i) {
1324             auto texture = static_cast<const VulkanTexture*>(
1325                     drawPass->getTexture(command->fTextureIndices[i]));
1326             auto sampler = static_cast<const VulkanSampler*>(
1327                     drawPass->getSampler(command->fSamplerIndices[i]));
1328             if (!appendTextureSampler(texture, sampler)) {
1329                 return;
1330             }
1331         }
1332     }
1333     if (fActiveGraphicsPipeline->dstReadRequirement() == DstReadRequirement::kTextureCopy) {
1334         auto texture = static_cast<const VulkanTexture*>(fDstCopy.first);
1335         auto sampler = static_cast<const VulkanSampler*>(fDstCopy.second);
1336         if (!appendTextureSampler(texture, sampler)) {
1337             return;
1338         }
1339     }
1340 
1341     SkASSERT(writeDescriptorSets.size() == numTexSamplers &&
1342              descriptorImageInfos.size() == numTexSamplers);
1343     VULKAN_CALL(fSharedContext->interface(), UpdateDescriptorSets(fSharedContext->device(),
1344                                                                   numTexSamplers,
1345                                                                   &writeDescriptorSets[0],
1346                                                                   /*descriptorCopyCount=*/0,
1347                                                                   /*pDescriptorCopies=*/nullptr));
1348 
1349     // Store the updated descriptor set to be actually bound later on. This avoids binding and
1350     // potentially having to re-bind in cases where earlier descriptor sets change while going
1351     // through drawpass commands.
1352     fTextureSamplerDescSetToBind = *set->descriptorSet();
1353     fBindTextureSamplers = true;
1354     fNumTextureSamplers = numTexSamplers;
1355     this->trackResource(std::move(set));
1356 }
1357 
bindTextureSamplers()1358 void VulkanCommandBuffer::bindTextureSamplers() {
1359     fBindTextureSamplers = false;
1360     if (fTextureSamplerDescSetToBind != VK_NULL_HANDLE &&
1361         fActiveGraphicsPipeline->numFragTexturesAndSamplers() == fNumTextureSamplers) {
1362         VULKAN_CALL(fSharedContext->interface(),
1363                     CmdBindDescriptorSets(fPrimaryCommandBuffer,
1364                                           VK_PIPELINE_BIND_POINT_GRAPHICS,
1365                                           fActiveGraphicsPipeline->layout(),
1366                                           VulkanGraphicsPipeline::kTextureBindDescSetIndex,
1367                                           /*setCount=*/1,
1368                                           &fTextureSamplerDescSetToBind,
1369                                           /*dynamicOffsetCount=*/0,
1370                                           /*dynamicOffsets=*/nullptr));
1371     }
1372 }
1373 
setScissor(const Scissor & scissor)1374 void VulkanCommandBuffer::setScissor(const Scissor& scissor) {
1375     this->setScissor(scissor.getRect(fReplayTranslation, fReplayClip));
1376 }
1377 
setScissor(const SkIRect & rect)1378 void VulkanCommandBuffer::setScissor(const SkIRect& rect) {
1379     VkRect2D scissor = {
1380             {rect.x(), rect.y()},
1381             {static_cast<unsigned int>(rect.width()), static_cast<unsigned int>(rect.height())}};
1382     VULKAN_CALL(fSharedContext->interface(),
1383                 CmdSetScissor(fPrimaryCommandBuffer,
1384                               /*firstScissor=*/0,
1385                               /*scissorCount=*/1,
1386                               &scissor));
1387 }
1388 
draw(PrimitiveType,unsigned int baseVertex,unsigned int vertexCount)1389 void VulkanCommandBuffer::draw(PrimitiveType,
1390                                unsigned int baseVertex,
1391                                unsigned int vertexCount) {
1392     SkASSERT(fActiveRenderPass);
1393     this->syncDescriptorSets();
1394     // TODO: set primitive type via dynamic state if available
1395     VULKAN_CALL(fSharedContext->interface(),
1396                 CmdDraw(fPrimaryCommandBuffer,
1397                         vertexCount,
1398                         /*instanceCount=*/1,
1399                         baseVertex,
1400                         /*firstInstance=*/0));
1401 }
1402 
drawIndexed(PrimitiveType,unsigned int baseIndex,unsigned int indexCount,unsigned int baseVertex)1403 void VulkanCommandBuffer::drawIndexed(PrimitiveType,
1404                                       unsigned int baseIndex,
1405                                       unsigned int indexCount,
1406                                       unsigned int baseVertex) {
1407     SkASSERT(fActiveRenderPass);
1408     this->syncDescriptorSets();
1409     // TODO: set primitive type via dynamic state if available
1410     VULKAN_CALL(fSharedContext->interface(),
1411                 CmdDrawIndexed(fPrimaryCommandBuffer,
1412                                indexCount,
1413                                /*instanceCount=*/1,
1414                                baseIndex,
1415                                baseVertex,
1416                                /*firstInstance=*/0));
1417 }
1418 
drawInstanced(PrimitiveType,unsigned int baseVertex,unsigned int vertexCount,unsigned int baseInstance,unsigned int instanceCount)1419 void VulkanCommandBuffer::drawInstanced(PrimitiveType,
1420                                         unsigned int baseVertex,
1421                                         unsigned int vertexCount,
1422                                         unsigned int baseInstance,
1423                                         unsigned int instanceCount) {
1424     SkASSERT(fActiveRenderPass);
1425     this->syncDescriptorSets();
1426     // TODO: set primitive type via dynamic state if available
1427     VULKAN_CALL(fSharedContext->interface(),
1428                 CmdDraw(fPrimaryCommandBuffer,
1429                         vertexCount,
1430                         instanceCount,
1431                         baseVertex,
1432                         baseInstance));
1433 }
1434 
drawIndexedInstanced(PrimitiveType,unsigned int baseIndex,unsigned int indexCount,unsigned int baseVertex,unsigned int baseInstance,unsigned int instanceCount)1435 void VulkanCommandBuffer::drawIndexedInstanced(PrimitiveType,
1436                                                unsigned int baseIndex,
1437                                                unsigned int indexCount,
1438                                                unsigned int baseVertex,
1439                                                unsigned int baseInstance,
1440                                                unsigned int instanceCount) {
1441     SkASSERT(fActiveRenderPass);
1442     this->syncDescriptorSets();
1443     // TODO: set primitive type via dynamic state if available
1444     VULKAN_CALL(fSharedContext->interface(),
1445                 CmdDrawIndexed(fPrimaryCommandBuffer,
1446                                indexCount,
1447                                instanceCount,
1448                                baseIndex,
1449                                baseVertex,
1450                                baseInstance));
1451 }
1452 
drawIndirect(PrimitiveType)1453 void VulkanCommandBuffer::drawIndirect(PrimitiveType) {
1454     SkASSERT(fActiveRenderPass);
1455     this->syncDescriptorSets();
1456     // TODO: set primitive type via dynamic state if available
1457     // Currently we can only support doing one indirect draw operation at a time,
1458     // so stride is irrelevant.
1459     VULKAN_CALL(fSharedContext->interface(),
1460                 CmdDrawIndirect(fPrimaryCommandBuffer,
1461                                 fBoundIndirectBuffer,
1462                                 fBoundIndirectBufferOffset,
1463                                 /*drawCount=*/1,
1464                                 /*stride=*/0));
1465 }
1466 
drawIndexedIndirect(PrimitiveType)1467 void VulkanCommandBuffer::drawIndexedIndirect(PrimitiveType) {
1468     SkASSERT(fActiveRenderPass);
1469     this->syncDescriptorSets();
1470     // TODO: set primitive type via dynamic state if available
1471     // Currently we can only support doing one indirect draw operation at a time,
1472     // so stride is irrelevant.
1473     VULKAN_CALL(fSharedContext->interface(),
1474                 CmdDrawIndexedIndirect(fPrimaryCommandBuffer,
1475                                        fBoundIndirectBuffer,
1476                                        fBoundIndirectBufferOffset,
1477                                        /*drawCount=*/1,
1478                                        /*stride=*/0));
1479 }
1480 
onAddComputePass(DispatchGroupSpan)1481 bool VulkanCommandBuffer::onAddComputePass(DispatchGroupSpan) { return false; }
1482 
onCopyBufferToBuffer(const Buffer * srcBuffer,size_t srcOffset,const Buffer * dstBuffer,size_t dstOffset,size_t size)1483 bool VulkanCommandBuffer::onCopyBufferToBuffer(const Buffer* srcBuffer,
1484                                                size_t srcOffset,
1485                                                const Buffer* dstBuffer,
1486                                                size_t dstOffset,
1487                                                size_t size) {
1488     auto vkSrcBuffer = static_cast<const VulkanBuffer*>(srcBuffer);
1489     auto vkDstBuffer = static_cast<const VulkanBuffer*>(dstBuffer);
1490 
1491     SkASSERT(vkSrcBuffer->bufferUsageFlags() & VK_BUFFER_USAGE_TRANSFER_SRC_BIT);
1492     SkASSERT(vkDstBuffer->bufferUsageFlags() & VK_BUFFER_USAGE_TRANSFER_DST_BIT);
1493 
1494     VkBufferCopy region;
1495     memset(&region, 0, sizeof(VkBufferCopy));
1496     region.srcOffset = srcOffset;
1497     region.dstOffset = dstOffset;
1498     region.size = size;
1499 
1500     this->submitPipelineBarriers();
1501 
1502     VULKAN_CALL(fSharedContext->interface(),
1503                 CmdCopyBuffer(fPrimaryCommandBuffer,
1504                               vkSrcBuffer->vkBuffer(),
1505                               vkDstBuffer->vkBuffer(),
1506                               /*regionCount=*/1,
1507                               &region));
1508 
1509     return true;
1510 }
1511 
onCopyTextureToBuffer(const Texture * texture,SkIRect srcRect,const Buffer * buffer,size_t bufferOffset,size_t bufferRowBytes)1512 bool VulkanCommandBuffer::onCopyTextureToBuffer(const Texture* texture,
1513                                                 SkIRect srcRect,
1514                                                 const Buffer* buffer,
1515                                                 size_t bufferOffset,
1516                                                 size_t bufferRowBytes) {
1517     const VulkanTexture* srcTexture = static_cast<const VulkanTexture*>(texture);
1518     auto dstBuffer = static_cast<const VulkanBuffer*>(buffer);
1519     SkASSERT(dstBuffer->bufferUsageFlags() & VK_BUFFER_USAGE_TRANSFER_DST_BIT);
1520 
1521     // Obtain the VkFormat of the source texture so we can determine bytes per block.
1522     VulkanTextureInfo srcTextureInfo;
1523     SkAssertResult(TextureInfos::GetVulkanTextureInfo(texture->textureInfo(), &srcTextureInfo));
1524     size_t bytesPerBlock = VkFormatBytesPerBlock(srcTextureInfo.fFormat);
1525 
1526     // Set up copy region
1527     VkBufferImageCopy region;
1528     memset(&region, 0, sizeof(VkBufferImageCopy));
1529     region.bufferOffset = bufferOffset;
1530     // Vulkan expects bufferRowLength in texels, not bytes.
1531     region.bufferRowLength = (uint32_t)(bufferRowBytes/bytesPerBlock);
1532     region.bufferImageHeight = 0; // Tightly packed
1533     region.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, /*mipLevel=*/0, 0, 1 };
1534     region.imageOffset = { srcRect.left(), srcRect.top(), /*z=*/0 };
1535     region.imageExtent = { (uint32_t)srcRect.width(), (uint32_t)srcRect.height(), /*depth=*/1 };
1536 
1537     // Enable editing of the source texture so we can change its layout so it can be copied from.
1538     const_cast<VulkanTexture*>(srcTexture)->setImageLayout(this,
1539                                                            VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
1540                                                            VK_ACCESS_TRANSFER_READ_BIT,
1541                                                            VK_PIPELINE_STAGE_TRANSFER_BIT,
1542                                                            false);
1543     // Set current access mask for buffer
1544     const_cast<VulkanBuffer*>(dstBuffer)->setBufferAccess(this,
1545                                                           VK_ACCESS_TRANSFER_WRITE_BIT,
1546                                                           VK_PIPELINE_STAGE_TRANSFER_BIT);
1547 
1548     this->submitPipelineBarriers();
1549 
1550     VULKAN_CALL(fSharedContext->interface(),
1551                 CmdCopyImageToBuffer(fPrimaryCommandBuffer,
1552                                      srcTexture->vkImage(),
1553                                      VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
1554                                      dstBuffer->vkBuffer(),
1555                                      /*regionCount=*/1,
1556                                      &region));
1557     return true;
1558 }
1559 
onCopyBufferToTexture(const Buffer * buffer,const Texture * texture,const BufferTextureCopyData * copyData,int count)1560 bool VulkanCommandBuffer::onCopyBufferToTexture(const Buffer* buffer,
1561                                                 const Texture* texture,
1562                                                 const BufferTextureCopyData* copyData,
1563                                                 int count) {
1564     auto srcBuffer = static_cast<const VulkanBuffer*>(buffer);
1565     SkASSERT(srcBuffer->bufferUsageFlags() & VK_BUFFER_USAGE_TRANSFER_SRC_BIT);
1566     const VulkanTexture* dstTexture = static_cast<const VulkanTexture*>(texture);
1567 
1568     // Obtain the VkFormat of the destination texture so we can determine bytes per block.
1569     VulkanTextureInfo dstTextureInfo;
1570     SkAssertResult(TextureInfos::GetVulkanTextureInfo(dstTexture->textureInfo(), &dstTextureInfo));
1571     size_t bytesPerBlock = VkFormatBytesPerBlock(dstTextureInfo.fFormat);
1572     SkISize oneBlockDims = CompressedDimensions(dstTexture->textureInfo().compressionType(),
1573                                                 {1, 1});
1574 
1575     // Set up copy regions.
1576     TArray<VkBufferImageCopy> regions(count);
1577     for (int i = 0; i < count; ++i) {
1578         VkBufferImageCopy& region = regions.push_back();
1579         memset(&region, 0, sizeof(VkBufferImageCopy));
1580         region.bufferOffset = copyData[i].fBufferOffset;
1581         // copyData provides row length in bytes, but Vulkan expects bufferRowLength in texels.
1582         // For compressed this is the number of logical pixels not the number of blocks.
1583         region.bufferRowLength =
1584                 (uint32_t)((copyData[i].fBufferRowBytes/bytesPerBlock) * oneBlockDims.fWidth);
1585         region.bufferImageHeight = 0; // Tightly packed
1586         region.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, copyData[i].fMipLevel, 0, 1 };
1587         region.imageOffset = { copyData[i].fRect.left(),
1588                                copyData[i].fRect.top(),
1589                                /*z=*/0 };
1590         region.imageExtent = { (uint32_t)copyData[i].fRect.width(),
1591                                (uint32_t)copyData[i].fRect.height(),
1592                                /*depth=*/1 };
1593     }
1594 
1595     // Enable editing of the destination texture so we can change its layout so it can be copied to.
1596     const_cast<VulkanTexture*>(dstTexture)->setImageLayout(this,
1597                                                            VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1598                                                            VK_ACCESS_TRANSFER_WRITE_BIT,
1599                                                            VK_PIPELINE_STAGE_TRANSFER_BIT,
1600                                                            false);
1601 
1602     this->submitPipelineBarriers();
1603 
1604     VULKAN_CALL(fSharedContext->interface(),
1605             CmdCopyBufferToImage(fPrimaryCommandBuffer,
1606                                  srcBuffer->vkBuffer(),
1607                                  dstTexture->vkImage(),
1608                                  VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1609                                  regions.size(),
1610                                  regions.begin()));
1611     return true;
1612 }
1613 
onCopyTextureToTexture(const Texture * src,SkIRect srcRect,const Texture * dst,SkIPoint dstPoint,int mipLevel)1614 bool VulkanCommandBuffer::onCopyTextureToTexture(const Texture* src,
1615                                                  SkIRect srcRect,
1616                                                  const Texture* dst,
1617                                                  SkIPoint dstPoint,
1618                                                  int mipLevel) {
1619     const VulkanTexture* srcTexture = static_cast<const VulkanTexture*>(src);
1620     const VulkanTexture* dstTexture = static_cast<const VulkanTexture*>(dst);
1621 
1622     VkImageCopy copyRegion;
1623     memset(&copyRegion, 0, sizeof(VkImageCopy));
1624     copyRegion.srcSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
1625     copyRegion.srcOffset = { srcRect.fLeft, srcRect.fTop, 0 };
1626     copyRegion.dstSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, (uint32_t)mipLevel, 0, 1 };
1627     copyRegion.dstOffset = { dstPoint.fX, dstPoint.fY, 0 };
1628     copyRegion.extent = { (uint32_t)srcRect.width(), (uint32_t)srcRect.height(), 1 };
1629 
1630     // Enable editing of the src texture so we can change its layout so it can be copied from.
1631     const_cast<VulkanTexture*>(srcTexture)->setImageLayout(this,
1632                                                            VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
1633                                                            VK_ACCESS_TRANSFER_READ_BIT,
1634                                                            VK_PIPELINE_STAGE_TRANSFER_BIT,
1635                                                            false);
1636     // Enable editing of the destination texture so we can change its layout so it can be copied to.
1637     const_cast<VulkanTexture*>(dstTexture)->setImageLayout(this,
1638                                                            VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1639                                                            VK_ACCESS_TRANSFER_WRITE_BIT,
1640                                                            VK_PIPELINE_STAGE_TRANSFER_BIT,
1641                                                            false);
1642 
1643     this->submitPipelineBarriers();
1644 
1645     VULKAN_CALL(fSharedContext->interface(),
1646                 CmdCopyImage(fPrimaryCommandBuffer,
1647                              srcTexture->vkImage(),
1648                              VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
1649                              dstTexture->vkImage(),
1650                              VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1651                              /*regionCount=*/1,
1652                              &copyRegion));
1653 
1654     return true;
1655 }
1656 
1657 
pushConstants(VkShaderStageFlags stageFlags,uint32_t offset,uint32_t size,const void * values)1658 bool VulkanCommandBuffer::pushConstants(VkShaderStageFlags stageFlags,
1659                                         uint32_t offset,
1660                                         uint32_t size,
1661                                         const void* values) {
1662     SkASSERT(fActiveGraphicsPipeline);
1663     // offset and size must be a multiple of 4
1664     SkASSERT(!SkToBool(offset & 0x3));
1665     SkASSERT(!SkToBool(size & 0x3));
1666 
1667     VULKAN_CALL(fSharedContext->interface(),
1668                 CmdPushConstants(fPrimaryCommandBuffer,
1669                                  fActiveGraphicsPipeline->layout(),
1670                                  stageFlags,
1671                                  offset,
1672                                  size,
1673                                  values));
1674     return true;
1675 }
1676 
onSynchronizeBufferToCpu(const Buffer * buffer,bool * outDidResultInWork)1677 bool VulkanCommandBuffer::onSynchronizeBufferToCpu(const Buffer* buffer, bool* outDidResultInWork) {
1678     static_cast<const VulkanBuffer*>(buffer)->setBufferAccess(this,
1679                                                               VK_ACCESS_HOST_READ_BIT,
1680                                                               VK_PIPELINE_STAGE_HOST_BIT);
1681 
1682     *outDidResultInWork = true;
1683     return true;
1684 }
1685 
onClearBuffer(const Buffer *,size_t offset,size_t size)1686 bool VulkanCommandBuffer::onClearBuffer(const Buffer*, size_t offset, size_t size) {
1687     return false;
1688 }
1689 
addBufferMemoryBarrier(const Resource * resource,VkPipelineStageFlags srcStageMask,VkPipelineStageFlags dstStageMask,VkBufferMemoryBarrier * barrier)1690 void VulkanCommandBuffer::addBufferMemoryBarrier(const Resource* resource,
1691                                                  VkPipelineStageFlags srcStageMask,
1692                                                  VkPipelineStageFlags dstStageMask,
1693                                                  VkBufferMemoryBarrier* barrier) {
1694     SkASSERT(resource);
1695     this->pipelineBarrier(resource,
1696                           srcStageMask,
1697                           dstStageMask,
1698                           /*byRegion=*/false,
1699                           kBufferMemory_BarrierType,
1700                           barrier);
1701 }
1702 
addBufferMemoryBarrier(VkPipelineStageFlags srcStageMask,VkPipelineStageFlags dstStageMask,VkBufferMemoryBarrier * barrier)1703 void VulkanCommandBuffer::addBufferMemoryBarrier(VkPipelineStageFlags srcStageMask,
1704                                                  VkPipelineStageFlags dstStageMask,
1705                                                  VkBufferMemoryBarrier* barrier) {
1706     // We don't pass in a resource here to the command buffer. The command buffer only is using it
1707     // to hold a ref, but every place where we add a buffer memory barrier we are doing some other
1708     // command with the buffer on the command buffer. Thus those other commands will already cause
1709     // the command buffer to be holding a ref to the buffer.
1710     this->pipelineBarrier(/*resource=*/nullptr,
1711                           srcStageMask,
1712                           dstStageMask,
1713                           /*byRegion=*/false,
1714                           kBufferMemory_BarrierType,
1715                           barrier);
1716 }
1717 
addImageMemoryBarrier(const Resource * resource,VkPipelineStageFlags srcStageMask,VkPipelineStageFlags dstStageMask,bool byRegion,VkImageMemoryBarrier * barrier)1718 void VulkanCommandBuffer::addImageMemoryBarrier(const Resource* resource,
1719                                                 VkPipelineStageFlags srcStageMask,
1720                                                 VkPipelineStageFlags dstStageMask,
1721                                                 bool byRegion,
1722                                                 VkImageMemoryBarrier* barrier) {
1723     SkASSERT(resource);
1724     this->pipelineBarrier(resource,
1725                           srcStageMask,
1726                           dstStageMask,
1727                           byRegion,
1728                           kImageMemory_BarrierType,
1729                           barrier);
1730 }
1731 
pipelineBarrier(const Resource * resource,VkPipelineStageFlags srcStageMask,VkPipelineStageFlags dstStageMask,bool byRegion,BarrierType barrierType,void * barrier)1732 void VulkanCommandBuffer::pipelineBarrier(const Resource* resource,
1733                                           VkPipelineStageFlags srcStageMask,
1734                                           VkPipelineStageFlags dstStageMask,
1735                                           bool byRegion,
1736                                           BarrierType barrierType,
1737                                           void* barrier) {
1738     // TODO: Do we need to handle wrapped command buffers?
1739     // SkASSERT(!this->isWrapped());
1740     SkASSERT(fActive);
1741 #ifdef SK_DEBUG
1742     // For images we can have barriers inside of render passes but they require us to add more
1743     // support in subpasses which need self dependencies to have barriers inside them. Also, we can
1744     // never have buffer barriers inside of a render pass. For now we will just assert that we are
1745     // not in a render pass.
1746     bool isValidSubpassBarrier = false;
1747     if (barrierType == kImageMemory_BarrierType) {
1748         VkImageMemoryBarrier* imgBarrier = static_cast<VkImageMemoryBarrier*>(barrier);
1749         isValidSubpassBarrier = (imgBarrier->newLayout == imgBarrier->oldLayout) &&
1750             (imgBarrier->srcQueueFamilyIndex == VK_QUEUE_FAMILY_IGNORED) &&
1751             (imgBarrier->dstQueueFamilyIndex == VK_QUEUE_FAMILY_IGNORED) &&
1752             byRegion;
1753     }
1754     SkASSERT(!fActiveRenderPass || isValidSubpassBarrier);
1755 #endif
1756 
1757     if (barrierType == kBufferMemory_BarrierType) {
1758         const VkBufferMemoryBarrier* barrierPtr = static_cast<VkBufferMemoryBarrier*>(barrier);
1759         fBufferBarriers.push_back(*barrierPtr);
1760     } else {
1761         SkASSERT(barrierType == kImageMemory_BarrierType);
1762         const VkImageMemoryBarrier* barrierPtr = static_cast<VkImageMemoryBarrier*>(barrier);
1763         // We need to check if we are adding a pipeline barrier that covers part of the same
1764         // subresource range as a barrier that is already in current batch. If it does, then we must
1765         // submit the first batch because the vulkan spec does not define a specific ordering for
1766         // barriers submitted in the same batch.
1767         // TODO: Look if we can gain anything by merging barriers together instead of submitting
1768         // the old ones.
1769         for (int i = 0; i < fImageBarriers.size(); ++i) {
1770             VkImageMemoryBarrier& currentBarrier = fImageBarriers[i];
1771             if (barrierPtr->image == currentBarrier.image) {
1772                 const VkImageSubresourceRange newRange = barrierPtr->subresourceRange;
1773                 const VkImageSubresourceRange oldRange = currentBarrier.subresourceRange;
1774                 SkASSERT(newRange.aspectMask == oldRange.aspectMask);
1775                 SkASSERT(newRange.baseArrayLayer == oldRange.baseArrayLayer);
1776                 SkASSERT(newRange.layerCount == oldRange.layerCount);
1777                 uint32_t newStart = newRange.baseMipLevel;
1778                 uint32_t newEnd = newRange.baseMipLevel + newRange.levelCount - 1;
1779                 uint32_t oldStart = oldRange.baseMipLevel;
1780                 uint32_t oldEnd = oldRange.baseMipLevel + oldRange.levelCount - 1;
1781                 if (std::max(newStart, oldStart) <= std::min(newEnd, oldEnd)) {
1782                     this->submitPipelineBarriers();
1783                     break;
1784                 }
1785             }
1786         }
1787         fImageBarriers.push_back(*barrierPtr);
1788     }
1789     fBarriersByRegion |= byRegion;
1790     fSrcStageMask = fSrcStageMask | srcStageMask;
1791     fDstStageMask = fDstStageMask | dstStageMask;
1792 
1793     if (resource) {
1794         this->trackResource(sk_ref_sp(resource));
1795     }
1796     if (fActiveRenderPass) {
1797         this->submitPipelineBarriers(true);
1798     }
1799 }
1800 
submitPipelineBarriers(bool forSelfDependency)1801 void VulkanCommandBuffer::submitPipelineBarriers(bool forSelfDependency) {
1802     SkASSERT(fActive);
1803 
1804     // TODO: Do we need to handle SecondaryCommandBuffers as well?
1805 
1806     // Currently we never submit a pipeline barrier without at least one buffer or image barrier.
1807     if (!fBufferBarriers.empty() || !fImageBarriers.empty()) {
1808         // For images we can have barriers inside of render passes but they require us to add more
1809         // support in subpasses which need self dependencies to have barriers inside them. Also, we
1810         // can never have buffer barriers inside of a render pass. For now we will just assert that
1811         // we are not in a render pass.
1812         SkASSERT(!fActiveRenderPass || forSelfDependency);
1813         // TODO: Do we need to handle wrapped CommandBuffers?
1814         //  SkASSERT(!this->isWrapped());
1815         SkASSERT(fSrcStageMask && fDstStageMask);
1816 
1817         VkDependencyFlags dependencyFlags = fBarriersByRegion ? VK_DEPENDENCY_BY_REGION_BIT : 0;
1818         VULKAN_CALL(fSharedContext->interface(),
1819                     CmdPipelineBarrier(fPrimaryCommandBuffer, fSrcStageMask, fDstStageMask,
1820                                        dependencyFlags,
1821                                        /*memoryBarrierCount=*/0, /*pMemoryBarrier=*/nullptr,
1822                                        fBufferBarriers.size(), fBufferBarriers.begin(),
1823                                        fImageBarriers.size(), fImageBarriers.begin()));
1824         fBufferBarriers.clear();
1825         fImageBarriers.clear();
1826         fBarriersByRegion = false;
1827         fSrcStageMask = 0;
1828         fDstStageMask = 0;
1829     }
1830     SkASSERT(fBufferBarriers.empty());
1831     SkASSERT(fImageBarriers.empty());
1832     SkASSERT(!fBarriersByRegion);
1833     SkASSERT(!fSrcStageMask);
1834     SkASSERT(!fDstStageMask);
1835 }
1836 
updateBuffer(const VulkanBuffer * buffer,const void * data,size_t dataSize,size_t dstOffset)1837 void VulkanCommandBuffer::updateBuffer(const VulkanBuffer* buffer,
1838                                        const void* data,
1839                                        size_t dataSize,
1840                                        size_t dstOffset) {
1841     // vkCmdUpdateBuffer can only be called outside of a render pass.
1842     SkASSERT(fActive && !fActiveRenderPass);
1843     if (!buffer || buffer->vkBuffer() == VK_NULL_HANDLE) {
1844         SKGPU_LOG_W("VulkanCommandBuffer::updateBuffer requires a valid VulkanBuffer pointer backed"
1845                     "by a valid VkBuffer handle");
1846         return;
1847     }
1848 
1849     // Per the spec, vkCmdUpdateBuffer is treated as a “transfer" operation for the purposes of
1850     // synchronization barriers. Ensure this write operation occurs after any previous read
1851     // operations and without clobbering any other write operations on the same memory in the cache.
1852     buffer->setBufferAccess(this, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
1853     this->submitPipelineBarriers();
1854 
1855     VULKAN_CALL(fSharedContext->interface(), CmdUpdateBuffer(fPrimaryCommandBuffer,
1856                                                              buffer->vkBuffer(),
1857                                                              dstOffset,
1858                                                              dataSize,
1859                                                              data));
1860 }
1861 
nextSubpass()1862 void VulkanCommandBuffer::nextSubpass() {
1863     // TODO: Use VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS if we add secondary cmd buffers
1864     VULKAN_CALL(fSharedContext->interface(),
1865                 CmdNextSubpass(fPrimaryCommandBuffer, VK_SUBPASS_CONTENTS_INLINE));
1866 }
1867 
setViewport(SkIRect viewport)1868 void VulkanCommandBuffer::setViewport(SkIRect viewport) {
1869     VkViewport vkViewport = {
1870         (float) viewport.fLeft,
1871         (float) viewport.fTop,
1872         (float) viewport.width(),
1873         (float) viewport.height(),
1874         0.0f, // minDepth
1875         1.0f, // maxDepth
1876     };
1877     VULKAN_CALL(fSharedContext->interface(),
1878                 CmdSetViewport(fPrimaryCommandBuffer,
1879                                /*firstViewport=*/0,
1880                                /*viewportCount=*/1,
1881                                &vkViewport));
1882 }
1883 
1884 } // namespace skgpu::graphite
1885