[Ganesh] Add support for VK_EXT_frame_boundary.
Clients can now pass in a request to mark a frame boundary when we
submit GPU work to the queue in Vulkan. This is useful for debugging
tools that want to capture all the API calls made during a frame.
Bug: b/367717718
Change-Id: I54c225225224abf6434f0456ad6a1d875a2b3732
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/907486
Commit-Queue: Greg Daniel <egdaniel@google.com>
Reviewed-by: Nicolette Prevost <nicolettep@google.com>
diff --git a/include/gpu/ganesh/GrDirectContext.h b/include/gpu/ganesh/GrDirectContext.h
index 5caf3fe..c60bb4d 100644
--- a/include/gpu/ganesh/GrDirectContext.h
+++ b/include/gpu/ganesh/GrDirectContext.h
@@ -479,9 +479,18 @@
* If it returns false, then those same semaphores will not have been submitted and we will not
* try to submit them again. The caller is free to delete the semaphores at any time.
*
- * If sync flag is GrSyncCpu::kYes, this function will return once the gpu has finished with all
- * submitted work.
+ * If GrSubmitInfo::fSync flag is GrSyncCpu::kYes, this function will return once the gpu has
+ * finished with all submitted work.
+ *
+ * If GrSubmitInfo::fMarkBoundary flag is GrMarkFrameBoundary::kYes and the GPU supports a way
+ * to be notified about frame boundaries, then we will notify the GPU during/after the
+ * submission of work to the GPU. GrSubmitInfo::fFrameID is a frame ID that is passed to the
+ * GPU when marking a boundary. Ideally this value should be unique for each frame. Currently
+ * marking frame boundaries is only supported with the Vulkan backend and only if the
+ * VK_EXT_frame_boudnary extenstion is available.
*/
+ bool submit(const GrSubmitInfo&);
+
bool submit(GrSyncCpu sync = GrSyncCpu::kNo) {
GrSubmitInfo info;
info.fSync = sync;
@@ -489,7 +498,6 @@
return this->submit(info);
}
- bool submit(const GrSubmitInfo&);
/**
* Checks whether any asynchronous work is complete and if so calls related callbacks.
diff --git a/include/gpu/ganesh/GrTypes.h b/include/gpu/ganesh/GrTypes.h
index 7943e91..0b23a72 100644
--- a/include/gpu/ganesh/GrTypes.h
+++ b/include/gpu/ganesh/GrTypes.h
@@ -174,18 +174,15 @@
kYes = true,
};
-// TODO: Add the ability to mark frame boundaries during submit
-/**
enum class GrMarkFrameBoundary : bool {
kNo = false,
kYes = true,
};
-*/
struct GrSubmitInfo {
GrSyncCpu fSync = GrSyncCpu::kNo;
- // TODO: Add this functionality
- // GrMarkFrameBoundary fSync = GrMarkFrameBoundary::kNo;
+ GrMarkFrameBoundary fMarkBoundary = GrMarkFrameBoundary::kNo;
+ uint64_t fFrameID = 0;
};
#endif
diff --git a/src/gpu/ganesh/vk/GrVkCaps.cpp b/src/gpu/ganesh/vk/GrVkCaps.cpp
index e268229..b32d696 100644
--- a/src/gpu/ganesh/vk/GrVkCaps.cpp
+++ b/src/gpu/ganesh/vk/GrVkCaps.cpp
@@ -414,6 +414,10 @@
fSupportsDeviceFaultInfo = true;
}
+ if (extensions.hasExtension(VK_EXT_FRAME_BOUNDARY_EXTENSION_NAME, 1)) {
+ fSupportsFrameBoundary = true;
+ }
+
fMaxInputAttachmentDescriptors = properties.limits.maxDescriptorSetInputAttachments;
fMaxSamplerAnisotropy = properties.limits.maxSamplerAnisotropy;
diff --git a/src/gpu/ganesh/vk/GrVkCaps.h b/src/gpu/ganesh/vk/GrVkCaps.h
index fcee64d..c4388e7 100644
--- a/src/gpu/ganesh/vk/GrVkCaps.h
+++ b/src/gpu/ganesh/vk/GrVkCaps.h
@@ -197,6 +197,8 @@
bool supportsDeviceFaultInfo() const { return fSupportsDeviceFaultInfo; }
+ bool supportsFrameBoundary() const { return fSupportsFrameBoundary; }
+
// Returns whether we prefer to record draws directly into a primary command buffer.
bool preferPrimaryOverSecondaryCommandBuffers() const {
return fPreferPrimaryOverSecondaryCommandBuffers;
@@ -485,6 +487,8 @@
bool fSupportsDeviceFaultInfo = false;
+ bool fSupportsFrameBoundary = false;
+
bool fPreferPrimaryOverSecondaryCommandBuffers = true;
bool fMustInvalidatePrimaryCmdBufferStateAfterClearAttachments = false;
diff --git a/src/gpu/ganesh/vk/GrVkCommandBuffer.cpp b/src/gpu/ganesh/vk/GrVkCommandBuffer.cpp
index 868dc79..c3315b3 100644
--- a/src/gpu/ganesh/vk/GrVkCommandBuffer.cpp
+++ b/src/gpu/ganesh/vk/GrVkCommandBuffer.cpp
@@ -556,19 +556,36 @@
const VkCommandBuffer* commandBuffers,
uint32_t signalCount,
const VkSemaphore* signalSemaphores,
- GrProtected protectedContext) {
+ GrProtected protectedContext,
+ const GrSubmitInfo& info) {
+ void* pNext = nullptr;
+
VkProtectedSubmitInfo protectedSubmitInfo;
if (protectedContext == GrProtected::kYes) {
memset(&protectedSubmitInfo, 0, sizeof(VkProtectedSubmitInfo));
protectedSubmitInfo.sType = VK_STRUCTURE_TYPE_PROTECTED_SUBMIT_INFO;
- protectedSubmitInfo.pNext = nullptr;
+ protectedSubmitInfo.pNext = pNext;
protectedSubmitInfo.protectedSubmit = VK_TRUE;
+
+ pNext = &protectedSubmitInfo;
+ }
+
+ VkFrameBoundaryEXT frameBoundary;
+ if (info.fMarkBoundary == GrMarkFrameBoundary::kYes &&
+ gpu->vkCaps().supportsFrameBoundary()) {
+ memset(&frameBoundary, 0, sizeof(VkFrameBoundaryEXT));
+ frameBoundary.sType = VK_STRUCTURE_TYPE_FRAME_BOUNDARY_EXT;
+ frameBoundary.pNext = pNext;
+ frameBoundary.flags = VK_FRAME_BOUNDARY_FRAME_END_BIT_EXT;
+ frameBoundary.frameID = info.fFrameID;
+
+ pNext = &frameBoundary;
}
VkSubmitInfo submitInfo;
memset(&submitInfo, 0, sizeof(VkSubmitInfo));
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
- submitInfo.pNext = protectedContext == GrProtected::kYes ? &protectedSubmitInfo : nullptr;
+ submitInfo.pNext = pNext;
submitInfo.waitSemaphoreCount = waitCount;
submitInfo.pWaitSemaphores = waitSemaphores;
submitInfo.pWaitDstStageMask = waitStages;
@@ -585,7 +602,8 @@
GrVkGpu* gpu,
VkQueue queue,
TArray<GrVkSemaphore::Resource*>& signalSemaphores,
- TArray<GrVkSemaphore::Resource*>& waitSemaphores) {
+ TArray<GrVkSemaphore::Resource*>& waitSemaphores,
+ const GrSubmitInfo& submitInfo) {
SkASSERT(!fIsActive);
VkResult err;
@@ -615,7 +633,7 @@
// queue with no worries.
submitResult = submit_to_queue(
gpu, queue, fSubmitFence, 0, nullptr, nullptr, 1, &fCmdBuffer, 0, nullptr,
- GrProtected(gpu->protectedContext()));
+ GrProtected(gpu->protectedContext()), submitInfo);
} else {
TArray<VkSemaphore> vkSignalSems(signalCount);
for (int i = 0; i < signalCount; ++i) {
@@ -642,7 +660,7 @@
submitResult = submit_to_queue(gpu, queue, fSubmitFence, vkWaitSems.size(),
vkWaitSems.begin(), vkWaitStages.begin(), 1, &fCmdBuffer,
vkSignalSems.size(), vkSignalSems.begin(),
- GrProtected(gpu->protectedContext()));
+ GrProtected(gpu->protectedContext()), submitInfo);
if (submitResult == VK_SUCCESS) {
for (int i = 0; i < signalCount; ++i) {
signalSemaphores[i]->markAsSignaled();
diff --git a/src/gpu/ganesh/vk/GrVkCommandBuffer.h b/src/gpu/ganesh/vk/GrVkCommandBuffer.h
index 57ea754..82ef3b4 100644
--- a/src/gpu/ganesh/vk/GrVkCommandBuffer.h
+++ b/src/gpu/ganesh/vk/GrVkCommandBuffer.h
@@ -33,6 +33,7 @@
class GrVkImage;
class GrVkPipeline;
class GrVkRenderPass;
+struct GrSubmitInfo;
struct SkIRect;
class GrVkCommandBuffer {
@@ -324,9 +325,11 @@
uint32_t regionCount,
const VkImageResolve* regions);
- bool submitToQueue(GrVkGpu* gpu, VkQueue queue,
+ bool submitToQueue(GrVkGpu* gpu,
+ VkQueue queue,
skia_private::TArray<GrVkSemaphore::Resource*>& signalSemaphores,
- skia_private::TArray<GrVkSemaphore::Resource*>& waitSemaphores);
+ skia_private::TArray<GrVkSemaphore::Resource*>& waitSemaphores,
+ const GrSubmitInfo&);
void forceSync(GrVkGpu* gpu);
diff --git a/src/gpu/ganesh/vk/GrVkGpu.cpp b/src/gpu/ganesh/vk/GrVkGpu.cpp
index d35d90b..f1e8eea 100644
--- a/src/gpu/ganesh/vk/GrVkGpu.cpp
+++ b/src/gpu/ganesh/vk/GrVkGpu.cpp
@@ -363,14 +363,14 @@
return fCachedOpsRenderPass.get();
}
-bool GrVkGpu::submitCommandBuffer(SyncQueue sync) {
+bool GrVkGpu::submitCommandBuffer(const GrSubmitInfo& submitInfo) {
TRACE_EVENT0("skia.gpu", TRACE_FUNC);
if (!this->currentCommandBuffer()) {
return false;
}
SkASSERT(!fCachedOpsRenderPass || !fCachedOpsRenderPass->isActive());
- if (!this->currentCommandBuffer()->hasWork() && kForce_SyncQueue != sync &&
+ if (!this->currentCommandBuffer()->hasWork() && submitInfo.fSync == GrSyncCpu::kNo &&
fSemaphoresToSignal.empty() && fSemaphoresToWaitOn.empty()) {
// We may have added finished procs during the flush call. Since there is no actual work
// we are not submitting the command buffer and may never come back around to submit it.
@@ -386,9 +386,9 @@
SkASSERT(fMainCmdPool);
fMainCmdPool->close();
bool didSubmit = fMainCmdBuffer->submitToQueue(this, fQueue, fSemaphoresToSignal,
- fSemaphoresToWaitOn);
+ fSemaphoresToWaitOn, submitInfo);
- if (didSubmit && sync == kForce_SyncQueue) {
+ if (didSubmit && submitInfo.fSync == GrSyncCpu::kYes) {
fMainCmdBuffer->forceSync(this);
}
@@ -492,7 +492,9 @@
VK_ACCESS_HOST_WRITE_BIT,
VK_PIPELINE_STAGE_HOST_BIT,
false);
- if (!this->submitCommandBuffer(kForce_SyncQueue)) {
+ GrSubmitInfo submitInfo;
+ submitInfo.fSync = GrSyncCpu::kYes;
+ if (!this->submitCommandBuffer(submitInfo)) {
return false;
}
}
@@ -2100,7 +2102,9 @@
GrVkImageInfo info;
if (GrBackendRenderTargets::GetVkImageInfo(rt, &info)) {
// something in the command buffer may still be using this, so force submit
- SkAssertResult(this->submitCommandBuffer(kForce_SyncQueue));
+ GrSubmitInfo submitInfo;
+ submitInfo.fSync = GrSyncCpu::kYes;
+ SkAssertResult(this->submitCommandBuffer(submitInfo));
GrVkImage::DestroyImageInfo(this, const_cast<GrVkImageInfo*>(&info));
}
}
@@ -2221,11 +2225,7 @@
}
bool GrVkGpu::onSubmitToGpu(const GrSubmitInfo& info) {
- if (info.fSync == GrSyncCpu::kYes) {
- return this->submitCommandBuffer(kForce_SyncQueue);
- } else {
- return this->submitCommandBuffer(kSkip_SyncQueue);
- }
+ return this->submitCommandBuffer(info);
}
void GrVkGpu::finishOutstandingGpuWork() {
@@ -2588,7 +2588,9 @@
// We need to submit the current command buffer to the Queue and make sure it finishes before
// we can copy the data out of the buffer.
- if (!this->submitCommandBuffer(kForce_SyncQueue)) {
+ GrSubmitInfo submitInfo;
+ submitInfo.fSync = GrSyncCpu::kYes;
+ if (!this->submitCommandBuffer(submitInfo)) {
return false;
}
void* mappedMemory = transferBuffer->map();
diff --git a/src/gpu/ganesh/vk/GrVkGpu.h b/src/gpu/ganesh/vk/GrVkGpu.h
index e566edd..82928bb 100644
--- a/src/gpu/ganesh/vk/GrVkGpu.h
+++ b/src/gpu/ganesh/vk/GrVkGpu.h
@@ -242,11 +242,6 @@
bool checkVkResult(VkResult);
private:
- enum SyncQueue {
- kForce_SyncQueue,
- kSkip_SyncQueue
- };
-
GrVkGpu(GrDirectContext*,
const skgpu::VulkanBackendContext&,
const sk_sp<GrVkCaps> caps,
@@ -385,12 +380,15 @@
void onReportSubmitHistograms() override;
// Ends and submits the current command buffer to the queue and then creates a new command
- // buffer and begins it. If sync is set to kForce_SyncQueue, the function will wait for all
- // work in the queue to finish before returning. If this GrVkGpu object has any semaphores in
- // fSemaphoreToSignal, we will add those signal semaphores to the submission of this command
- // buffer. If this GrVkGpu object has any semaphores in fSemaphoresToWaitOn, we will add those
- // wait semaphores to the submission of this command buffer.
- bool submitCommandBuffer(SyncQueue sync);
+ // buffer and begins it. If fSync in the submitInfo is set to GrSyncCpu::kYes, the function will
+ // wait for all work in the queue to finish before returning. If this GrVkGpu object has any
+ // semaphores in fSemaphoreToSignal, we will add those signal semaphores to the submission of
+ // this command buffer. If this GrVkGpu object has any semaphores in fSemaphoresToWaitOn, we
+ // will add those wait semaphores to the submission of this command buffer.
+ //
+ // If fMarkBoundary in submitInfo is GrMarkFrameBoundary::kYes, then we will mark the end of a
+ // frame if the VK_EXT_frame_boundary extension is available.
+ bool submitCommandBuffer(const GrSubmitInfo& submitInfo);
void copySurfaceAsCopyImage(GrSurface* dst,
GrSurface* src,
diff --git a/tools/gpu/vk/VkTestUtils.cpp b/tools/gpu/vk/VkTestUtils.cpp
index 1cfce1f..edb3267 100644
--- a/tools/gpu/vk/VkTestUtils.cpp
+++ b/tools/gpu/vk/VkTestUtils.cpp
@@ -168,11 +168,12 @@
const char* kValidExtensions[] = {
// single merged layer
VK_EXT_BLEND_OPERATION_ADVANCED_EXTENSION_NAME,
- VK_EXT_DEVICE_FAULT_EXTENSION_NAME,
VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME,
+ VK_EXT_DEVICE_FAULT_EXTENSION_NAME,
+ VK_EXT_FRAME_BOUNDARY_EXTENSION_NAME,
VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME,
- VK_EXT_RGBA10X6_FORMATS_EXTENSION_NAME,
VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME,
+ VK_EXT_RGBA10X6_FORMATS_EXTENSION_NAME,
VK_KHR_BIND_MEMORY_2_EXTENSION_NAME,
VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME,
VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME,