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>
This commit is contained in:
parent
6e407986cd
commit
ab32f65653
@ -306,6 +306,8 @@ skia_gpu_sources = [
|
||||
"$_src/gpu/ops/GrTessellatingPathRenderer.h",
|
||||
"$_src/gpu/ops/GrTextureOp.cpp",
|
||||
"$_src/gpu/ops/GrTextureOp.h",
|
||||
"$_src/gpu/ops/GrTransferFromOp.cpp",
|
||||
"$_src/gpu/ops/GrTransferFromOp.h",
|
||||
|
||||
"$_src/gpu/effects/GrCoverageSetOpXP.cpp",
|
||||
"$_src/gpu/effects/GrCoverageSetOpXP.h",
|
||||
|
@ -660,6 +660,33 @@ public:
|
||||
*/
|
||||
bool readPixels(const SkBitmap& dst, int srcX, int srcY);
|
||||
|
||||
/** Makes pixel data available to caller, possibly asynchronously.
|
||||
|
||||
Currently asynchronous reads are only supported on the GPU backend and only when the
|
||||
underlying 3D API supports transfer buffers and CPU/GPU synchronization primitives. In all
|
||||
other cases this operates synchronously.
|
||||
|
||||
When the pixel data is ready the caller's ReadPixelsCallback is called with a pointer to
|
||||
the data in the requested color type, alpha type, and color space. The data pointer is
|
||||
only valid for the duration of the callback.
|
||||
|
||||
Upon failure the the callback is called with nullptr as the data pointer.
|
||||
|
||||
If the src rectangle is not contained by the bounds of the surface then failure occurs.
|
||||
|
||||
@param ct color type of the read data
|
||||
@param at alpha type of the read data
|
||||
@param cs color space of the read data
|
||||
@param srcRect a subrectangle of the surface to read
|
||||
@param callback function to call with result of the read.
|
||||
@param context passed to callback.
|
||||
*/
|
||||
using ReadPixelsContext = void*;
|
||||
using ReadPixelsCallback = void(ReadPixelsContext, const void* data, size_t rowBytes);
|
||||
void asyncReadPixels(SkColorType ct, SkAlphaType at, sk_sp<SkColorSpace> cs,
|
||||
const SkIRect& srcRect, ReadPixelsCallback callback,
|
||||
ReadPixelsContext context);
|
||||
|
||||
/** Copies SkRect of pixels from the src SkPixmap to the SkSurface.
|
||||
|
||||
Source SkRect corners are (0, 0) and (src.width(), src.height()).
|
||||
|
@ -33,6 +33,9 @@ public:
|
||||
// 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;
|
||||
|
||||
|
@ -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 @@ void GrRenderTargetContext::drawDrawable(std::unique_ptr<SkDrawable::GpuDrawHand
|
||||
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
|
||||
|
@ -402,6 +402,11 @@ public:
|
||||
*/
|
||||
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.
|
||||
|
@ -26,6 +26,12 @@ public:
|
||||
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 @@ public:
|
||||
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&);
|
||||
|
@ -21,6 +21,8 @@ public:
|
||||
|
||||
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 @@ public:
|
||||
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; }
|
||||
|
||||
|
@ -33,7 +33,11 @@ public:
|
||||
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 @@ public:
|
||||
// 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;
|
||||
|
||||
|
@ -96,6 +96,15 @@ void GrMtlGpuRTCommandBuffer::copy(GrSurface* src, GrSurfaceOrigin srcOrigin,
|
||||
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,
|
||||
|
@ -30,12 +30,12 @@ public:
|
||||
#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
|
||||
|
28
src/gpu/ops/GrTransferFromOp.cpp
Normal file
28
src/gpu/ops/GrTransferFromOp.cpp
Normal file
@ -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);
|
||||
}
|
70
src/gpu/ops/GrTransferFromOp.h
Normal file
70
src/gpu/ops/GrTransferFromOp.h
Normal file
@ -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
|
@ -649,7 +649,6 @@ bool GrVkPrimaryCommandBuffer::finished(const GrVkGpu* gpu) {
|
||||
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 @@ void GrVkPrimaryCommandBuffer::onReleaseResources(GrVkGpu* gpu) {
|
||||
for (int i = 0; i < fSecondaryCommandBuffers.count(); ++i) {
|
||||
fSecondaryCommandBuffers[i]->releaseResources(gpu);
|
||||
}
|
||||
fFinishedProcs.reset();
|
||||
}
|
||||
|
||||
void GrVkPrimaryCommandBuffer::recycleSecondaryCommandBuffers() {
|
||||
|
@ -36,7 +36,6 @@ class InlineUpload : public GrVkPrimaryCommandBufferTask {
|
||||
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 @@ public:
|
||||
, 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 @@ private:
|
||||
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 @@ void GrVkGpuTextureCommandBuffer::copy(GrSurface* src, GrSurfaceOrigin srcOrigin
|
||||
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::copy(GrSurface* src, GrSurfaceOrigin srcOrigin, con
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
|
@ -48,6 +48,8 @@ public:
|
||||
|
||||
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 @@ public:
|
||||
|
||||
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;
|
||||
|
||||
|
@ -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::onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPa
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ bool SkSurface::readPixels(const SkBitmap& bitmap, int srcX, int srcY) {
|
||||
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;
|
||||
|
@ -45,6 +45,13 @@ public:
|
||||
|
||||
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:
|
||||
*
|
||||
|
@ -132,6 +132,19 @@ void SkSurface_Gpu::onWritePixels(const SkPixmap& src, int x, int y) {
|
||||
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.
|
||||
|
@ -30,6 +30,9 @@ public:
|
||||
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;
|
||||
|
@ -8,20 +8,19 @@
|
||||
#include <initializer_list>
|
||||
#include "include/core/SkCanvas.h"
|
||||
#include "include/core/SkSurface.h"
|
||||
#include "include/gpu/GrContext.h"
|
||||
#include "include/private/SkColorData.h"
|
||||
#include "include/private/SkHalf.h"
|
||||
#include "include/private/SkImageInfoPriv.h"
|
||||
#include "src/core/SkAutoPixmapStorage.h"
|
||||
#include "src/core/SkMathPriv.h"
|
||||
#include "tests/Test.h"
|
||||
|
||||
#include "include/gpu/GrContext.h"
|
||||
#include "src/gpu/GrContextPriv.h"
|
||||
#include "src/gpu/GrProxyProvider.h"
|
||||
#include "src/gpu/SkGr.h"
|
||||
#include "tests/Test.h"
|
||||
#include "tools/gpu/GrContextFactory.h"
|
||||
#include "tools/gpu/ProxyUtils.h"
|
||||
|
||||
|
||||
static const int DEV_W = 100, DEV_H = 100;
|
||||
static const SkIRect DEV_RECT = SkIRect::MakeWH(DEV_W, DEV_H);
|
||||
static const SkRect DEV_RECT_S = SkRect::MakeWH(DEV_W * SK_Scalar1,
|
||||
@ -688,3 +687,84 @@ DEF_TEST(ReadPixels_ValidConversion, reporter) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(AsyncReadPixels, reporter, ctxInfo) {
|
||||
struct Context {
|
||||
SkPixmap* fPixmap = nullptr;
|
||||
bool fSuceeded = false;
|
||||
bool fCalled = false;
|
||||
};
|
||||
auto callback = [](SkSurface::ReleaseContext context, const void* data, size_t rowBytes) {
|
||||
auto* pm = static_cast<Context*>(context)->fPixmap;
|
||||
static_cast<Context*>(context)->fCalled = true;
|
||||
if ((static_cast<Context*>(context)->fSuceeded = SkToBool(data))) {
|
||||
auto dst = static_cast<char*>(pm->writable_addr());
|
||||
const auto* src = static_cast<const char*>(data);
|
||||
for (int y = 0; y < pm->height(); ++y, src += rowBytes, dst += pm->rowBytes()) {
|
||||
memcpy(dst, src, pm->width() * SkColorTypeBytesPerPixel(pm->colorType()));
|
||||
}
|
||||
}
|
||||
};
|
||||
for (auto origin : {kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin}) {
|
||||
static constexpr int kW = 16;
|
||||
static constexpr int kH = 16;
|
||||
for (int c = 0; c <= kLastEnum_SkColorType; ++c) {
|
||||
auto ct = static_cast<SkColorType>(c);
|
||||
auto info = SkImageInfo::Make(kW, kH, ct, kPremul_SkAlphaType, nullptr);
|
||||
auto surf = SkSurface::MakeRenderTarget(ctxInfo.grContext(), SkBudgeted::kNo, info, 1,
|
||||
origin, nullptr);
|
||||
if (!surf) {
|
||||
continue;
|
||||
}
|
||||
float d = std::sqrt((float)surf->width() * surf->width() +
|
||||
(float)surf->height() * surf->height());
|
||||
for (int j = 0; j < surf->height(); ++j) {
|
||||
for (int i = 0; i < surf->width(); ++i) {
|
||||
float r = i / (float)surf->width();
|
||||
float g = 1.f - i / (float)surf->height();
|
||||
float b = std::sqrt((float)i * i + (float)j * j) / d;
|
||||
SkPaint paint;
|
||||
paint.setColor4f(SkColor4f{r, g, b, 1.f}, nullptr);
|
||||
surf->getCanvas()->drawRect(SkRect::MakeXYWH(i, j, 1, 1), paint);
|
||||
}
|
||||
}
|
||||
for (const auto& rect : {SkIRect::MakeWH(kW, kH), // full size
|
||||
SkIRect::MakeLTRB(1, 2, kW - 3, kH - 4), // partial
|
||||
SkIRect::MakeXYWH(1, 1, 0, 0), // zero size - fail
|
||||
SkIRect::MakeWH(kW + 1, kH / 2)}) { // too big - fail
|
||||
SkAutoPixmapStorage pixmap;
|
||||
Context context;
|
||||
context.fPixmap = &pixmap;
|
||||
info = SkImageInfo::Make(rect.width(), rect.height(), ct, kPremul_SkAlphaType,
|
||||
nullptr);
|
||||
pixmap.alloc(info);
|
||||
memset(pixmap.writable_addr(), 0xAB, pixmap.computeByteSize());
|
||||
surf->asyncReadPixels(ct, kPremul_SkAlphaType, nullptr, rect, callback, &context);
|
||||
while (!context.fCalled) {
|
||||
ctxInfo.grContext()->checkAsyncWorkCompletion();
|
||||
}
|
||||
if (rect.isEmpty() || !SkIRect::MakeWH(kW, kH).contains(rect)) {
|
||||
REPORTER_ASSERT(reporter, !context.fSuceeded);
|
||||
}
|
||||
if (context.fSuceeded) {
|
||||
// We use a synchronous read as the source of truth.
|
||||
SkAutoPixmapStorage ref;
|
||||
ref.alloc(info);
|
||||
memset(ref.writable_addr(), 0xCD, ref.computeByteSize());
|
||||
if (!surf->readPixels(ref, rect.fLeft, rect.fTop)) {
|
||||
continue;
|
||||
}
|
||||
const auto* a = static_cast<const char*>(pixmap.addr());
|
||||
const auto* b = static_cast<const char*>(ref.addr());
|
||||
for (int j = 0; j < pixmap.height();
|
||||
++j, a += pixmap.rowBytes(), b += ref.rowBytes()) {
|
||||
if (memcmp(a, b, pixmap.width() * SkColorTypeBytesPerPixel(ct))) {
|
||||
ERRORF(reporter, "Failed. CT: %d, j: %d", ct, j);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user