Add SkSurface::asyncReadPixels()
Initial version. Current limitations: No Metal support, no color space
conversions, for each src color type only one dst color type is legal (
which may or may not be the src color type), no alpha type conversions.
Bug: skia:8962
Change-Id: I6f046a32342b8f5ffb1799d67d7ba15c250ef9bf
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/212981
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Greg Daniel <egdaniel@google.com>
diff --git a/src/gpu/GrGpuCommandBuffer.h b/src/gpu/GrGpuCommandBuffer.h
index 1c4d5f4..abb3c4d 100644
--- a/src/gpu/GrGpuCommandBuffer.h
+++ b/src/gpu/GrGpuCommandBuffer.h
@@ -33,6 +33,9 @@
// GrGpuRenderTargetCommandBuffer.
virtual void copy(GrSurface* src, GrSurfaceOrigin srcOrigin,
const SkIRect& srcRect, const SkIPoint& dstPoint) = 0;
+ // Initiates a transfer from the surface owned by the command buffer to the GrGpuBuffer.
+ virtual void transferFrom(const SkIRect& srcRect, GrColorType bufferColorType,
+ GrGpuBuffer* transferBuffer, size_t offset) = 0;
virtual void insertEventMarker(const char*) = 0;
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index 977e96f..8dac16d 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -5,6 +5,7 @@
* found in the LICENSE file.
*/
+#include "src/gpu/GrRenderTargetContext.h"
#include "include/core/SkDrawable.h"
#include "include/gpu/GrBackendSemaphore.h"
#include "include/gpu/GrRenderTarget.h"
@@ -31,7 +32,6 @@
#include "src/gpu/GrPathRenderer.h"
#include "src/gpu/GrQuad.h"
#include "src/gpu/GrRecordingContextPriv.h"
-#include "src/gpu/GrRenderTargetContext.h"
#include "src/gpu/GrRenderTargetContextPriv.h"
#include "src/gpu/GrResourceProvider.h"
#include "src/gpu/GrShape.h"
@@ -60,6 +60,7 @@
#include "src/gpu/ops/GrStencilPathOp.h"
#include "src/gpu/ops/GrStrokeRectOp.h"
#include "src/gpu/ops/GrTextureOp.h"
+#include "src/gpu/ops/GrTransferFromOp.h"
#include "src/gpu/text/GrTextContext.h"
#include "src/gpu/text/GrTextTarget.h"
@@ -1735,6 +1736,114 @@
this->getRTOpList()->addOp(std::move(op), *this->caps());
}
+bool GrRenderTargetContext::asyncReadPixels(SkColorType ct, SkAlphaType at, sk_sp<SkColorSpace> cs,
+ const SkIRect& srcRect, ReadPixelsCallback callback,
+ ReadPixelsContext context) {
+ auto direct = fContext->priv().asDirectContext();
+ if (!direct) {
+ return false;
+ }
+ if (!this->caps()->transferBufferSupport()) {
+ return false;
+ }
+ if (fRenderTargetProxy->wrapsVkSecondaryCB()) {
+ return false;
+ }
+ // We currently don't know our own alpha type, we assume it's premul if we have an alpha channel
+ // and opaque otherwise.
+ if (!GrPixelConfigIsAlphaOnly(fRenderTargetProxy->config()) && at != kPremul_SkAlphaType) {
+ return false;
+ }
+ // TODO(bsalomon): Enhance support for reading to different color types.
+ auto dstCT = SkColorTypeToGrColorType(ct);
+ auto readCT = this->caps()->supportedReadPixelsColorType(fRenderTargetProxy->config(), dstCT);
+ if (readCT != dstCT) {
+ return false;
+ }
+ if (!this->caps()->transferFromOffsetAlignment(readCT)) {
+ return false;
+ }
+
+ // TODO(bsalomon): Support color space conversion.
+ if (!SkColorSpace::Equals(cs.get(), this->colorSpaceInfo().colorSpace())) {
+ return false;
+ }
+
+ // Insert a draw to a temporary surface if we need to do a y-flip (and in future for a color
+ // space conversion.)
+ if (this->origin() == kBottomLeft_GrSurfaceOrigin) {
+ sk_sp<GrTextureProxy> texProxy = sk_ref_sp(fRenderTargetProxy->asTextureProxy());
+ const auto& backendFormat = fRenderTargetProxy->backendFormat();
+ SkRect srcRectToDraw = SkRect::Make(srcRect);
+ // If the src is not texturable first try to make a copy to a texture.
+ if (!texProxy) {
+ GrSurfaceDesc desc;
+ desc.fWidth = srcRect.width();
+ desc.fHeight = srcRect.height();
+ desc.fConfig = fRenderTargetProxy->config();
+ auto sContext = direct->priv().makeDeferredSurfaceContext(
+ backendFormat, desc, this->origin(), GrMipMapped::kNo, SkBackingFit::kApprox,
+ SkBudgeted::kNo, this->colorSpaceInfo().refColorSpace());
+ if (!sContext) {
+ return false;
+ }
+ if (!sContext->copy(fRenderTargetProxy.get(), srcRect, {0, 0})) {
+ return false;
+ }
+ texProxy = sk_ref_sp(sContext->asTextureProxy());
+ SkASSERT(texProxy);
+ srcRectToDraw = SkRect::MakeWH(srcRect.width(), srcRect.height());
+ }
+ auto rtc = direct->priv().makeDeferredRenderTargetContext(
+ backendFormat, SkBackingFit::kApprox, srcRect.width(), srcRect.height(),
+ fRenderTargetProxy->config(), cs, 1, GrMipMapped::kNo, kTopLeft_GrSurfaceOrigin);
+ if (!rtc) {
+ return false;
+ }
+ rtc->drawTexture(GrNoClip(), std::move(texProxy), GrSamplerState::Filter::kNearest,
+ SkBlendMode::kSrc, SK_PMColor4fWHITE, srcRectToDraw,
+ SkRect::MakeWH(srcRect.width(), srcRect.height()), GrAA::kNo,
+ GrQuadAAFlags::kNone, SkCanvas::kFast_SrcRectConstraint, SkMatrix::I(),
+ /* colorSpaceXform = */ nullptr);
+ return rtc->asyncReadPixels(ct, at, std::move(cs),
+ SkIRect::MakeWH(srcRect.width(), srcRect.height()), callback,
+ context);
+ }
+ size_t rowBytes = GrColorTypeBytesPerPixel(dstCT) * srcRect.width();
+ size_t size = rowBytes * srcRect.height();
+ auto buffer = direct->priv().resourceProvider()->createBuffer(
+ size, GrGpuBufferType::kXferGpuToCpu, GrAccessPattern::kStream_GrAccessPattern);
+ if (!buffer) {
+ return false;
+ }
+ this->getRTOpList()->addOp(GrTransferFromOp::Make(fContext, srcRect, dstCT, buffer, 0),
+ *this->caps());
+ struct FinishContext {
+ ReadPixelsCallback* fClientCallback;
+ ReadPixelsContext fClientContext;
+ sk_sp<GrGpuBuffer> fBuffer;
+ size_t fRowBytes;
+ };
+ // Assumption is that the caller would like to flush. We could take a parameter or require an
+ // explicit flush from the caller. We'd have to have a way to defer attaching the finish
+ // callback to GrGpu until after the next flush that flushes our op list, though.
+ auto* finishContext = new FinishContext{callback, context, buffer, rowBytes};
+ auto finishCallback = [](GrGpuFinishedContext c) {
+ auto context = reinterpret_cast<const FinishContext*>(c);
+ void* data = context->fBuffer->map();
+ (*context->fClientCallback)(context->fClientContext, data, data ? context->fRowBytes : 0);
+ if (data) {
+ context->fBuffer->unmap();
+ }
+ delete context;
+ };
+ GrFlushInfo flushInfo;
+ flushInfo.fFinishedContext = finishContext;
+ flushInfo.fFinishedProc = finishCallback;
+ this->flush(SkSurface::BackendSurfaceAccess::kNoAccess, flushInfo);
+ return true;
+}
+
GrSemaphoresSubmitted GrRenderTargetContext::flush(SkSurface::BackendSurfaceAccess access,
const GrFlushInfo& info) {
ASSERT_SINGLE_OWNER
diff --git a/src/gpu/GrRenderTargetContext.h b/src/gpu/GrRenderTargetContext.h
index 8b467db..6f39325 100644
--- a/src/gpu/GrRenderTargetContext.h
+++ b/src/gpu/GrRenderTargetContext.h
@@ -402,6 +402,11 @@
*/
void drawDrawable(std::unique_ptr<SkDrawable::GpuDrawHandler>, const SkRect& bounds);
+ using ReadPixelsCallback = SkSurface::ReadPixelsCallback;
+ using ReadPixelsContext = SkSurface::ReadPixelsContext;
+ bool asyncReadPixels(SkColorType, SkAlphaType, sk_sp<SkColorSpace>, const SkIRect& srcRect,
+ ReadPixelsCallback, ReadPixelsContext);
+
/**
* After this returns any pending surface IO will be issued to the backend 3D API and
* if the surface has MSAA it will be resolved.
diff --git a/src/gpu/gl/GrGLGpuCommandBuffer.h b/src/gpu/gl/GrGLGpuCommandBuffer.h
index 02c16d7..0f9370f 100644
--- a/src/gpu/gl/GrGLGpuCommandBuffer.h
+++ b/src/gpu/gl/GrGLGpuCommandBuffer.h
@@ -26,6 +26,12 @@
fGpu->copySurface(fTexture, fOrigin, src, srcOrigin, srcRect, dstPoint);
}
+ void transferFrom(const SkIRect& srcRect, GrColorType bufferColorType,
+ GrGpuBuffer* transferBuffer, size_t offset) override {
+ fGpu->transferPixelsFrom(fTexture, srcRect.fLeft, srcRect.fTop, srcRect.width(),
+ srcRect.height(), bufferColorType, transferBuffer, offset);
+ }
+
void insertEventMarker(const char* msg) override {
fGpu->insertEventMarker(msg);
}
@@ -67,6 +73,12 @@
fGpu->copySurface(fRenderTarget, fOrigin, src, srcOrigin, srcRect, dstPoint);
}
+ void transferFrom(const SkIRect& srcRect, GrColorType bufferColorType,
+ GrGpuBuffer* transferBuffer, size_t offset) override {
+ fGpu->transferPixelsFrom(fRenderTarget, srcRect.fLeft, srcRect.fTop, srcRect.width(),
+ srcRect.height(), bufferColorType, transferBuffer, offset);
+ }
+
void set(GrRenderTarget*, GrSurfaceOrigin,
const GrGpuRTCommandBuffer::LoadAndStoreInfo&,
const GrGpuRTCommandBuffer::StencilLoadAndStoreInfo&);
diff --git a/src/gpu/mock/GrMockGpuCommandBuffer.h b/src/gpu/mock/GrMockGpuCommandBuffer.h
index 0a13f42..9e2905e 100644
--- a/src/gpu/mock/GrMockGpuCommandBuffer.h
+++ b/src/gpu/mock/GrMockGpuCommandBuffer.h
@@ -21,6 +21,8 @@
void copy(GrSurface* src, GrSurfaceOrigin srcOrigin, const SkIRect& srcRect,
const SkIPoint& dstPoint) override {}
+ void transferFrom(const SkIRect& srcRect, GrColorType bufferColorType,
+ GrGpuBuffer* transferBuffer, size_t offset) override {}
void insertEventMarker(const char*) override {}
private:
@@ -42,6 +44,8 @@
void end() override {}
void copy(GrSurface* src, GrSurfaceOrigin srcOrigin, const SkIRect& srcRect,
const SkIPoint& dstPoint) override {}
+ void transferFrom(const SkIRect& srcRect, GrColorType bufferColorType,
+ GrGpuBuffer* transferBuffer, size_t offset) override {}
int numDraws() const { return fNumDraws; }
diff --git a/src/gpu/mtl/GrMtlGpuCommandBuffer.h b/src/gpu/mtl/GrMtlGpuCommandBuffer.h
index 5a6bc36..6f85603 100644
--- a/src/gpu/mtl/GrMtlGpuCommandBuffer.h
+++ b/src/gpu/mtl/GrMtlGpuCommandBuffer.h
@@ -33,7 +33,11 @@
const SkIPoint& dstPoint) override {
fGpu->copySurface(fTexture, fOrigin, src, srcOrigin, srcRect, dstPoint);
}
-
+ void transferFrom(const SkIRect& srcRect, GrColorType bufferColorType,
+ GrGpuBuffer* transferBuffer, size_t offset) override {
+ fGpu->transferPixelsFrom(fTexture, srcRect.fLeft, srcRect.fTop, srcRect.width(),
+ srcRect.height(), bufferColorType, transferBuffer, offset);
+ }
void insertEventMarker(const char* msg) override {}
private:
@@ -62,7 +66,8 @@
// TODO: this could be more efficient
state->doUpload(upload);
}
-
+ void transferFrom(const SkIRect& srcRect, GrColorType bufferColorType,
+ GrGpuBuffer* transferBuffer, size_t offset) override;
void copy(GrSurface* src, GrSurfaceOrigin srcOrigin, const SkIRect& srcRect,
const SkIPoint& dstPoint) override;
diff --git a/src/gpu/mtl/GrMtlGpuCommandBuffer.mm b/src/gpu/mtl/GrMtlGpuCommandBuffer.mm
index b20f0ce..7479d19 100644
--- a/src/gpu/mtl/GrMtlGpuCommandBuffer.mm
+++ b/src/gpu/mtl/GrMtlGpuCommandBuffer.mm
@@ -96,6 +96,15 @@
fGpu->copySurface(fRenderTarget, fOrigin, src, srcOrigin, srcRect, dstPoint);
}
+void GrMtlGpuRTCommandBuffer::transferFrom(const SkIRect& srcRect, GrColorType bufferColorType,
+ GrGpuBuffer* transferBuffer, size_t offset) {
+ // We cannot have an active encoder when we call transferFrom since it requires its own
+ // command encoder.
+ SkASSERT(nil == fActiveRenderCmdEncoder);
+ fGpu->transferPixelsFrom(fRenderTarget, srcRect.fLeft, srcRect.fTop, srcRect.width(),
+ srcRect.height(), bufferColorType, transferBuffer, offset);
+}
+
GrMtlPipelineState* GrMtlGpuRTCommandBuffer::prepareDrawState(
const GrPrimitiveProcessor& primProc,
const GrPipeline& pipeline,
diff --git a/src/gpu/ops/GrCopySurfaceOp.h b/src/gpu/ops/GrCopySurfaceOp.h
index 3581031..3c6194d 100644
--- a/src/gpu/ops/GrCopySurfaceOp.h
+++ b/src/gpu/ops/GrCopySurfaceOp.h
@@ -30,12 +30,12 @@
#ifdef SK_DEBUG
SkString dumpInfo() const override {
SkString string;
- string.append(INHERITED::dumpInfo());
- string.printf("srcProxyID: %d,\n"
- "srcRect: [ L: %d, T: %d, R: %d, B: %d ], dstPt: [ X: %d, Y: %d ]\n",
- fSrc.get()->uniqueID().asUInt(),
- fSrcRect.fLeft, fSrcRect.fTop, fSrcRect.fRight, fSrcRect.fBottom,
- fDstPoint.fX, fDstPoint.fY);
+ string = INHERITED::dumpInfo();
+ string.appendf(
+ "srcProxyID: %d,\n"
+ "srcRect: [ L: %d, T: %d, R: %d, B: %d ], dstPt: [ X: %d, Y: %d ]\n",
+ fSrc.get()->uniqueID().asUInt(), fSrcRect.fLeft, fSrcRect.fTop, fSrcRect.fRight,
+ fSrcRect.fBottom, fDstPoint.fX, fDstPoint.fY);
return string;
}
#endif
diff --git a/src/gpu/ops/GrTransferFromOp.cpp b/src/gpu/ops/GrTransferFromOp.cpp
new file mode 100644
index 0000000..e1ddfc5
--- /dev/null
+++ b/src/gpu/ops/GrTransferFromOp.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "src/gpu/ops/GrTransferFromOp.h"
+#include "include/private/GrRecordingContext.h"
+#include "src/gpu/GrCaps.h"
+#include "src/gpu/GrGpuCommandBuffer.h"
+#include "src/gpu/GrMemoryPool.h"
+#include "src/gpu/GrRecordingContextPriv.h"
+
+std::unique_ptr<GrOp> GrTransferFromOp::Make(GrRecordingContext* context,
+ const SkIRect& srcRect,
+ GrColorType dstColorType,
+ sk_sp<GrGpuBuffer> dstBuffer,
+ size_t dstOffset) {
+ SkASSERT(context->priv().caps()->transferFromOffsetAlignment(dstColorType));
+ SkASSERT(dstOffset % context->priv().caps()->transferFromOffsetAlignment(dstColorType) == 0);
+ GrOpMemoryPool* pool = context->priv().opMemoryPool();
+ return pool->allocate<GrTransferFromOp>(srcRect, dstColorType, std::move(dstBuffer), dstOffset);
+}
+
+void GrTransferFromOp::onExecute(GrOpFlushState* state, const SkRect& chainBounds) {
+ state->commandBuffer()->transferFrom(fSrcRect, fDstColorType, fDstBuffer.get(), fDstOffset);
+}
diff --git a/src/gpu/ops/GrTransferFromOp.h b/src/gpu/ops/GrTransferFromOp.h
new file mode 100644
index 0000000..34d8f54
--- /dev/null
+++ b/src/gpu/ops/GrTransferFromOp.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrTransferFromOp_DEFINED
+#define GrTransferFromOp_DEFINED
+
+#include "src/gpu/GrOpFlushState.h"
+#include "src/gpu/ops/GrOp.h"
+
+/**
+ * Does a transfer from the surface context's surface to a transfer buffer. It is assumed
+ * that the caller has checked the GrCaps to ensure this transfer is legal.
+ */
+class GrTransferFromOp final : public GrOp {
+public:
+ DEFINE_OP_CLASS_ID
+
+ static std::unique_ptr<GrOp> Make(GrRecordingContext*,
+ const SkIRect& srcRect,
+ GrColorType dstColorType,
+ sk_sp<GrGpuBuffer> dstBuffer,
+ size_t dstOffset);
+
+ const char* name() const override { return "TransferFromOp"; }
+
+#ifdef SK_DEBUG
+ SkString dumpInfo() const override {
+ SkString string;
+ string = INHERITED::dumpInfo();
+ string.appendf(
+ "bufferID:: %d offset: %zu, color type: %d\n"
+ "srcRect: [ L: %d, T: %d, R: %d, B: %d ]\n",
+ fDstBuffer->uniqueID().asUInt(), fDstOffset, fDstColorType, fSrcRect.fLeft,
+ fSrcRect.fTop, fSrcRect.fRight, fSrcRect.fBottom);
+ return string;
+ }
+#endif
+
+private:
+ friend class GrOpMemoryPool; // for ctor
+
+ GrTransferFromOp(const SkIRect& srcRect,
+ GrColorType dstColorType,
+ sk_sp<GrGpuBuffer> dstBuffer,
+ size_t dstOffset)
+ : INHERITED(ClassID())
+ , fDstBuffer(std::move(dstBuffer))
+ , fDstOffset(dstOffset)
+ , fSrcRect(srcRect)
+ , fDstColorType(dstColorType) {
+ this->setBounds(SkRect::Make(srcRect), HasAABloat::kNo, IsZeroArea::kNo);
+ }
+
+ void onPrepare(GrOpFlushState*) override {}
+
+ void onExecute(GrOpFlushState*, const SkRect& chainBounds) override;
+
+ sk_sp<GrGpuBuffer> fDstBuffer;
+ size_t fDstOffset;
+ SkIRect fSrcRect;
+ GrColorType fDstColorType;
+
+ typedef GrOp INHERITED;
+};
+
+#endif
diff --git a/src/gpu/vk/GrVkCommandBuffer.cpp b/src/gpu/vk/GrVkCommandBuffer.cpp
index 4cad381..315c991 100644
--- a/src/gpu/vk/GrVkCommandBuffer.cpp
+++ b/src/gpu/vk/GrVkCommandBuffer.cpp
@@ -649,7 +649,6 @@
VkResult err = GR_VK_CALL(gpu->vkInterface(), GetFenceStatus(gpu->device(), fSubmitFence));
switch (err) {
case VK_SUCCESS:
- fFinishedProcs.reset();
return true;
case VK_NOT_READY:
@@ -672,6 +671,7 @@
for (int i = 0; i < fSecondaryCommandBuffers.count(); ++i) {
fSecondaryCommandBuffers[i]->releaseResources(gpu);
}
+ fFinishedProcs.reset();
}
void GrVkPrimaryCommandBuffer::recycleSecondaryCommandBuffers() {
diff --git a/src/gpu/vk/GrVkGpuCommandBuffer.cpp b/src/gpu/vk/GrVkGpuCommandBuffer.cpp
index ab2ad06..c95f1e5 100644
--- a/src/gpu/vk/GrVkGpuCommandBuffer.cpp
+++ b/src/gpu/vk/GrVkGpuCommandBuffer.cpp
@@ -36,7 +36,6 @@
public:
InlineUpload(GrOpFlushState* state, const GrDeferredTextureUploadFn& upload)
: fFlushState(state), fUpload(upload) {}
- ~InlineUpload() override = default;
void execute(const Args& args) override { fFlushState->doUpload(fUpload); }
@@ -54,7 +53,6 @@
, fSrcRect(srcRect)
, fDstPoint(dstPoint)
, fShouldDiscardDst(shouldDiscardDst) {}
- ~Copy() override = default;
void execute(const Args& args) override {
args.fGpu->copySurface(args.fSurface, args.fOrigin, fSrc.get(), fSrcOrigin, fSrcRect,
@@ -70,6 +68,28 @@
bool fShouldDiscardDst;
};
+class TransferFrom : public GrVkPrimaryCommandBufferTask {
+public:
+ TransferFrom(const SkIRect& srcRect, GrColorType bufferColorType, GrGpuBuffer* transferBuffer,
+ size_t offset)
+ : fTransferBuffer(sk_ref_sp(transferBuffer))
+ , fOffset(offset)
+ , fSrcRect(srcRect)
+ , fBufferColorType(bufferColorType) {}
+
+ void execute(const Args& args) override {
+ args.fGpu->transferPixelsFrom(args.fSurface, fSrcRect.fLeft, fSrcRect.fTop,
+ fSrcRect.width(), fSrcRect.height(), fBufferColorType,
+ fTransferBuffer.get(), fOffset);
+ }
+
+private:
+ sk_sp<GrGpuBuffer> fTransferBuffer;
+ size_t fOffset;
+ SkIRect fSrcRect;
+ GrColorType fBufferColorType;
+};
+
} // anonymous namespace
/////////////////////////////////////////////////////////////////////////////
@@ -79,6 +99,11 @@
fTasks.emplace<Copy>(src, srcOrigin, srcRect, dstPoint, false);
}
+void GrVkGpuTextureCommandBuffer::transferFrom(const SkIRect& srcRect, GrColorType bufferColorType,
+ GrGpuBuffer* transferBuffer, size_t offset) {
+ fTasks.emplace<TransferFrom>(srcRect, bufferColorType, transferBuffer, offset);
+}
+
void GrVkGpuTextureCommandBuffer::insertEventMarker(const char* msg) {
// TODO: does Vulkan have a correlate?
}
@@ -620,6 +645,16 @@
}
}
+void GrVkGpuRTCommandBuffer::transferFrom(const SkIRect& srcRect, GrColorType bufferColorType,
+ GrGpuBuffer* transferBuffer, size_t offset) {
+ CommandBufferInfo& cbInfo = fCommandBufferInfos[fCurrentCmdInfo];
+ if (!cbInfo.fIsEmpty) {
+ this->addAdditionalRenderPass();
+ }
+ fPreCommandBufferTasks.emplace<TransferFrom>(srcRect, bufferColorType, transferBuffer, offset);
+ ++fCommandBufferInfos[fCurrentCmdInfo].fNumPreCmds;
+}
+
////////////////////////////////////////////////////////////////////////////////
void GrVkGpuRTCommandBuffer::bindGeometry(const GrGpuBuffer* indexBuffer,
diff --git a/src/gpu/vk/GrVkGpuCommandBuffer.h b/src/gpu/vk/GrVkGpuCommandBuffer.h
index 8ab9d7b..76c1284 100644
--- a/src/gpu/vk/GrVkGpuCommandBuffer.h
+++ b/src/gpu/vk/GrVkGpuCommandBuffer.h
@@ -48,6 +48,8 @@
void copy(GrSurface* src, GrSurfaceOrigin srcOrigin, const SkIRect& srcRect,
const SkIPoint& dstPoint) override;
+ void transferFrom(const SkIRect& srcRect, GrColorType bufferColorType,
+ GrGpuBuffer* transferBuffer, size_t offset) override;
void insertEventMarker(const char*) override;
@@ -81,6 +83,8 @@
void copy(GrSurface* src, GrSurfaceOrigin srcOrigin, const SkIRect& srcRect,
const SkIPoint& dstPoint) override;
+ void transferFrom(const SkIRect& srcRect, GrColorType bufferColorType,
+ GrGpuBuffer* transferBuffer, size_t offset) override;
void executeDrawable(std::unique_ptr<SkDrawable::GpuDrawHandler>) override;
diff --git a/src/image/SkSurface.cpp b/src/image/SkSurface.cpp
index dd8ac6f..cd214e8 100644
--- a/src/image/SkSurface.cpp
+++ b/src/image/SkSurface.cpp
@@ -5,12 +5,13 @@
* found in the LICENSE file.
*/
+#include <atomic>
#include "include/core/SkCanvas.h"
#include "include/core/SkFontLCDConfig.h"
#include "include/gpu/GrBackendSurface.h"
+#include "src/core/SkAutoPixmapStorage.h"
#include "src/core/SkImagePriv.h"
#include "src/image/SkSurface_Base.h"
-#include <atomic>
static SkPixelGeometry compute_default_geometry() {
SkFontLCDConfig::LCDOrder order = SkFontLCDConfig::GetSubpixelOrder();
@@ -86,6 +87,19 @@
}
}
+void SkSurface_Base::onAsyncReadPixels(SkColorType ct, SkAlphaType at, sk_sp<SkColorSpace> cs,
+ const SkIRect& rect, ReadPixelsCallback callback,
+ ReadPixelsContext context) {
+ auto info = SkImageInfo::Make(rect.width(), rect.height(), ct, at, std::move(cs));
+ SkAutoPixmapStorage pm;
+ pm.alloc(info);
+ if (this->readPixels(pm, rect.fLeft, rect.fTop)) {
+ callback(context, pm.addr(), pm.rowBytes());
+ } else {
+ callback(context, nullptr, 0);
+ }
+}
+
bool SkSurface_Base::outstandingImageSnapshot() const {
return fCachedImage && !fCachedImage->unique();
}
@@ -207,6 +221,18 @@
return bitmap.peekPixels(&pm) && this->readPixels(pm, srcX, srcY);
}
+void SkSurface::asyncReadPixels(SkColorType ct, SkAlphaType at, sk_sp<SkColorSpace> cs,
+ const SkIRect& srcRect, ReadPixelsCallback callback,
+ ReadPixelsContext context) {
+ auto dstII = SkImageInfo::Make(srcRect.width(), srcRect.height(), ct, at, cs);
+ if (!SkIRect::MakeWH(this->width(), this->height()).contains(srcRect) ||
+ !SkImageInfoIsValid(dstII)) {
+ callback(context, nullptr, 0);
+ return;
+ }
+ asSB(this)->onAsyncReadPixels(ct, at, std::move(cs), srcRect, callback, context);
+}
+
void SkSurface::writePixels(const SkPixmap& pmap, int x, int y) {
if (pmap.addr() == nullptr || pmap.width() <= 0 || pmap.height() <= 0) {
return;
diff --git a/src/image/SkSurface_Base.h b/src/image/SkSurface_Base.h
index b38169b..1c895ee 100644
--- a/src/image/SkSurface_Base.h
+++ b/src/image/SkSurface_Base.h
@@ -46,6 +46,13 @@
virtual void onWritePixels(const SkPixmap&, int x, int y) = 0;
/**
+ * Default implementation does a synchronous read and calls the callback.
+ */
+ virtual void onAsyncReadPixels(SkColorType, SkAlphaType, sk_sp<SkColorSpace>,
+ const SkIRect& srcRect, ReadPixelsCallback callback,
+ ReadPixelsContext context);
+
+ /**
* Default implementation:
*
* image = this->newImageSnapshot();
diff --git a/src/image/SkSurface_Gpu.cpp b/src/image/SkSurface_Gpu.cpp
index 7a3c24a..2690a6d 100644
--- a/src/image/SkSurface_Gpu.cpp
+++ b/src/image/SkSurface_Gpu.cpp
@@ -132,6 +132,19 @@
fDevice->writePixels(src, x, y);
}
+void SkSurface_Gpu::onAsyncReadPixels(SkColorType ct, SkAlphaType at, sk_sp<SkColorSpace> cs,
+ const SkIRect& srcRect, ReadPixelsCallback callback,
+ ReadPixelsContext context) {
+ auto* rtc = fDevice->accessRenderTargetContext();
+ if (!rtc->caps()->transferBufferSupport()) {
+ INHERITED::onAsyncReadPixels(ct, at, cs, srcRect, callback, context);
+ return;
+ }
+ if (!rtc->asyncReadPixels(ct, at, std::move(cs), srcRect, callback, context)) {
+ callback(context, nullptr, 0);
+ }
+}
+
// Create a new render target and, if necessary, copy the contents of the old
// render target into it. Note that this flushes the SkGpuDevice but
// doesn't force an OpenGL flush.
diff --git a/src/image/SkSurface_Gpu.h b/src/image/SkSurface_Gpu.h
index 799de37..d10947c 100644
--- a/src/image/SkSurface_Gpu.h
+++ b/src/image/SkSurface_Gpu.h
@@ -30,6 +30,9 @@
sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override;
sk_sp<SkImage> onNewImageSnapshot(const SkIRect* subset) override;
void onWritePixels(const SkPixmap&, int x, int y) override;
+ void onAsyncReadPixels(SkColorType, SkAlphaType, sk_sp<SkColorSpace>, const SkIRect& rect,
+ ReadPixelsCallback, ReadPixelsContext) override;
+
void onCopyOnWrite(ContentChangeMode) override;
void onDiscard() override;
GrSemaphoresSubmitted onFlush(BackendSurfaceAccess access, const GrFlushInfo& info) override;