Add Vulkan implementation of transfering from GrSurface to a GrGpuBuffer
Bug: skia:8962
Change-Id: I3796bef5a9e1af741b933afc78e32effb2b57a4a
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/206703
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/vk/GrVkCaps.cpp b/src/gpu/vk/GrVkCaps.cpp
index 3ef6d73..59ac518 100644
--- a/src/gpu/vk/GrVkCaps.cpp
+++ b/src/gpu/vk/GrVkCaps.cpp
@@ -413,6 +413,8 @@
void GrVkCaps::applyDriverCorrectnessWorkarounds(const VkPhysicalDeviceProperties& properties) {
if (kQualcomm_VkVendor == properties.vendorID) {
fMustDoCopiesFromOrigin = true;
+ // Transfer doesn't support this workaround.
+ fTransferBufferSupport = false;
}
#if defined(SK_BUILD_FOR_WIN)
@@ -949,3 +951,30 @@
return GrBackendFormat::MakeVk(format);
}
+bool GrVkCaps::onTransferFromBufferRequirements(GrColorType bufferColorType, int width,
+ size_t* rowBytes, size_t* offsetAlignment) const {
+ // This GrColorType has 32 bpp but the Vulkan pixel format we use for with may have 24bpp
+ // (VK_FORMAT_R8G8B8_...) or may be 32 bpp. We don't support post transforming the pixel data
+ // for transfer-from currently and don't want to have to pass info about the src surface here.
+ if (bufferColorType == GrColorType::kRGB_888x) {
+ return false;
+ }
+ size_t bpp = GrColorTypeBytesPerPixel(bufferColorType);
+ // The VkBufferImageCopy bufferOffset field must be both a multiple of 4 and of a single texel.
+ switch (bpp & 0b11) {
+ case 0: // bpp is a multiple of 4
+ *offsetAlignment = bpp;
+ break;
+ case 2: // bpp is a multiple of 2
+ *offsetAlignment = 2 * bpp;
+ break;
+ case 1:
+ case 3: // bpp neither a multiple of 2 nor 4.
+ *offsetAlignment = 4 * bpp;
+ break;
+ }
+ // The bufferRowLength member of VkBufferImageCopy is in texel units and must be >= the extent
+ // of the src VkImage region.
+ *rowBytes = width * bpp;
+ return true;
+}
diff --git a/src/gpu/vk/GrVkCaps.h b/src/gpu/vk/GrVkCaps.h
index 24e3e6d..34abe70 100644
--- a/src/gpu/vk/GrVkCaps.h
+++ b/src/gpu/vk/GrVkCaps.h
@@ -199,6 +199,8 @@
bool onSurfaceSupportsWritePixels(const GrSurface*) const override;
bool onCanCopySurface(const GrSurfaceProxy* dst, const GrSurfaceProxy* src,
const SkIRect& srcRect, const SkIPoint& dstPoint) const override;
+ bool onTransferFromBufferRequirements(GrColorType bufferColorType, int width, size_t* rowBytes,
+ size_t* offsetAlignment) const override;
struct ConfigInfo {
ConfigInfo() : fOptimalFlags(0), fLinearFlags(0) {}
diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp
index 546a548..f12a1a8 100644
--- a/src/gpu/vk/GrVkGpu.cpp
+++ b/src/gpu/vk/GrVkGpu.cpp
@@ -484,6 +484,88 @@
return true;
}
+size_t GrVkGpu::onTransferPixelsFrom(GrSurface* surface, int left, int top, int width, int height,
+ GrColorType bufferColorType, GrGpuBuffer* transferBuffer,
+ size_t offset) {
+ SkASSERT(surface);
+ SkASSERT(transferBuffer);
+
+ size_t rowBytes;
+ size_t offsetAlignment;
+ if (!this->vkCaps().transferFromBufferRequirements(bufferColorType, width, &rowBytes,
+ &offsetAlignment)) {
+ return 0;
+ }
+
+ if (offset % offsetAlignment || offset + height * rowBytes > transferBuffer->size()) {
+ return 0;
+ }
+
+ GrVkTransferBuffer* vkBuffer = static_cast<GrVkTransferBuffer*>(transferBuffer);
+
+ GrVkImage* srcImage;
+ if (GrVkRenderTarget* rt = static_cast<GrVkRenderTarget*>(surface->asRenderTarget())) {
+ // Reading from render targets that wrap a secondary command buffer is not allowed since
+ // it would require us to know the VkImage, which we don't have, as well as need us to
+ // stop and start the VkRenderPass which we don't have access to.
+ if (rt->wrapsSecondaryCommandBuffer()) {
+ return false;
+ }
+ // resolve the render target if necessary
+ switch (rt->getResolveType()) {
+ case GrVkRenderTarget::kCantResolve_ResolveType:
+ return false;
+ case GrVkRenderTarget::kAutoResolves_ResolveType:
+ break;
+ case GrVkRenderTarget::kCanResolve_ResolveType:
+ this->resolveRenderTargetNoFlush(rt);
+ break;
+ default:
+ SK_ABORT("Unknown resolve type");
+ }
+ srcImage = rt;
+ } else {
+ srcImage = static_cast<GrVkTexture*>(surface->asTexture());
+ }
+
+ // Set up copy region
+ VkBufferImageCopy region;
+ memset(®ion, 0, sizeof(VkBufferImageCopy));
+ region.bufferOffset = offset;
+ // We're assuming that GrVkCaps made the row bytes tight.
+ region.bufferRowLength = 0;
+#ifdef SK_DEBUG
+ int bpp = GrColorTypeBytesPerPixel(bufferColorType);
+ SkASSERT(rowBytes == width * (size_t)bpp);
+#endif
+ region.bufferImageHeight = 0;
+ region.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 };
+ region.imageOffset = { left, top, 0 };
+ region.imageExtent = { (uint32_t)width, (uint32_t)height, 1 };
+
+ srcImage->setImageLayout(this,
+ VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ VK_ACCESS_TRANSFER_READ_BIT,
+ VK_PIPELINE_STAGE_TRANSFER_BIT,
+ false);
+
+ fCurrentCmdBuffer->copyImageToBuffer(this, srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ vkBuffer, 1, ®ion);
+
+ // Make sure the copy to buffer has finished.
+ vkBuffer->addMemoryBarrier(this,
+ VK_ACCESS_TRANSFER_WRITE_BIT,
+ VK_ACCESS_HOST_READ_BIT,
+ VK_PIPELINE_STAGE_TRANSFER_BIT,
+ VK_PIPELINE_STAGE_HOST_BIT,
+ false);
+
+ // The caller is responsible for syncing.
+ this->submitCommandBuffer(kSkip_SyncQueue);
+
+ return rowBytes;
+}
+
void GrVkGpu::resolveImage(GrSurface* dst, GrVkRenderTarget* src, const SkIRect& srcRect,
const SkIPoint& dstPoint) {
SkASSERT(dst);
diff --git a/src/gpu/vk/GrVkGpu.h b/src/gpu/vk/GrVkGpu.h
index 2984e40..007ac17 100644
--- a/src/gpu/vk/GrVkGpu.h
+++ b/src/gpu/vk/GrVkGpu.h
@@ -213,11 +213,8 @@
bool onTransferPixelsTo(GrTexture*, int left, int top, int width, int height, GrColorType,
GrGpuBuffer* transferBuffer, size_t offset, size_t rowBytes) override;
- // TODO(bsalomon)
size_t onTransferPixelsFrom(GrSurface* surface, int left, int top, int width, int height,
- GrColorType, GrGpuBuffer* transferBuffer, size_t offset) override {
- return 0;
- }
+ GrColorType, GrGpuBuffer* transferBuffer, size_t offset) override;
bool onCopySurface(GrSurface* dst, GrSurfaceOrigin dstOrigin, GrSurface* src,
GrSurfaceOrigin srcOrigin, const SkIRect& srcRect,
diff --git a/tests/TransferPixelsTest.cpp b/tests/TransferPixelsTest.cpp
index 9ce9f30..e43af8f 100644
--- a/tests/TransferPixelsTest.cpp
+++ b/tests/TransferPixelsTest.cpp
@@ -349,8 +349,8 @@
basic_transfer_to_test(reporter, ctxInfo.grContext(), GrColorType::kBGRA_8888, true);
}
-// TODO(bsalomon): Vulkan
-DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(TransferPixelsFromTest, reporter, ctxInfo) {
+// TODO(bsalomon): Metal
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(TransferPixelsFromTest, reporter, ctxInfo) {
if (!ctxInfo.grContext()->priv().caps()->transferBufferSupport()) {
return;
}