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(®ion, 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 ®ion));
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(®ion, 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 ®ion));
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(®ion, 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(©Region, 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 ©Region));
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