Add GrGpu buffer transfer function, impl on Metal, add test
Also rename existing transfer buffer alignment cap for surface transfers to distinguish from new one for buffer to buffer transfers. Bug: skia:13278 Bug: skia:12720 Change-Id: Idc4116c72fc2852a01d40feec6584d2bde9eae82 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/550517 Reviewed-by: Jim Van Verth <jvanverth@google.com> Commit-Queue: Brian Salomon <bsalomon@google.com>
This commit is contained in:
parent
62ff766efa
commit
b043dae116
@ -98,6 +98,7 @@ tests_sources = [
|
||||
"$_tests/GrDDLImageTest.cpp",
|
||||
"$_tests/GrFinishedFlushTest.cpp",
|
||||
"$_tests/GrGlyphVectorTest.cpp",
|
||||
"$_tests/GrGpuBufferTest.cpp",
|
||||
"$_tests/GrMemoryPoolTest.cpp",
|
||||
"$_tests/GrOpListFlushTest.cpp",
|
||||
"$_tests/GrPorterDuffTest.cpp",
|
||||
|
@ -51,6 +51,7 @@ GrCaps::GrCaps(const GrContextOptions& options) {
|
||||
fPerformStencilClearsAsDraws = false;
|
||||
fTransferFromBufferToTextureSupport = false;
|
||||
fTransferFromSurfaceToBufferSupport = false;
|
||||
fTransferFromBufferToBufferSupport = false;
|
||||
fWritePixelsRowBytesSupport = false;
|
||||
fTransferPixelsToRowBytesSupport = false;
|
||||
fReadPixelsRowBytesSupport = false;
|
||||
|
@ -217,7 +217,13 @@ public:
|
||||
|
||||
uint32_t maxPushConstantsSize() const { return fMaxPushConstantsSize; }
|
||||
|
||||
size_t transferBufferAlignment() const { return fTransferBufferAlignment; }
|
||||
// Alignment requirement for row bytes in buffer<->texture transfers.
|
||||
size_t transferBufferRowBytesAlignment() const { return fTransferBufferRowBytesAlignment; }
|
||||
|
||||
// Alignment requirement for offsets and size in buffer->buffer transfers.
|
||||
size_t transferFromBufferToBufferAlignment() const {
|
||||
return fTransferFromBufferToBufferAlignment;
|
||||
}
|
||||
|
||||
virtual bool isFormatSRGB(const GrBackendFormat&) const = 0;
|
||||
|
||||
@ -334,6 +340,7 @@ public:
|
||||
|
||||
bool transferFromSurfaceToBufferSupport() const { return fTransferFromSurfaceToBufferSupport; }
|
||||
bool transferFromBufferToTextureSupport() const { return fTransferFromBufferToTextureSupport; }
|
||||
bool transferFromBufferToBufferSupport() const { return fTransferFromBufferToBufferSupport; }
|
||||
|
||||
bool suppressPrints() const { return fSuppressPrints; }
|
||||
|
||||
@ -558,6 +565,7 @@ protected:
|
||||
bool fPerformStencilClearsAsDraws : 1;
|
||||
bool fTransferFromBufferToTextureSupport : 1;
|
||||
bool fTransferFromSurfaceToBufferSupport : 1;
|
||||
bool fTransferFromBufferToBufferSupport : 1;
|
||||
bool fWritePixelsRowBytesSupport : 1;
|
||||
bool fTransferPixelsToRowBytesSupport : 1;
|
||||
bool fReadPixelsRowBytesSupport : 1;
|
||||
@ -598,7 +606,8 @@ protected:
|
||||
int fMaxWindowRectangles;
|
||||
int fInternalMultisampleCount;
|
||||
uint32_t fMaxPushConstantsSize = 0;
|
||||
size_t fTransferBufferAlignment = 1;
|
||||
size_t fTransferBufferRowBytesAlignment = 1;
|
||||
size_t fTransferFromBufferToBufferAlignment = 1;
|
||||
|
||||
GrDriverBugWorkarounds fDriverBugWorkarounds;
|
||||
|
||||
|
@ -392,6 +392,11 @@ sk_sp<GrGpuBuffer> GrGpu::createBuffer(size_t size,
|
||||
GrAccessPattern accessPattern) {
|
||||
TRACE_EVENT0("skia.gpu", TRACE_FUNC);
|
||||
this->handleDirtyContext();
|
||||
if ((intendedType == GrGpuBufferType::kXferCpuToGpu ||
|
||||
intendedType == GrGpuBufferType::kXferGpuToCpu) &&
|
||||
accessPattern == kStatic_GrAccessPattern) {
|
||||
return nullptr;
|
||||
}
|
||||
sk_sp<GrGpuBuffer> buffer = this->onCreateBuffer(size, intendedType, accessPattern);
|
||||
if (!this->caps()->reuseScratchBuffers()) {
|
||||
buffer->resourcePriv().removeScratchKey();
|
||||
@ -483,18 +488,51 @@ bool GrGpu::writePixels(GrSurface* surface,
|
||||
}
|
||||
|
||||
this->handleDirtyContext();
|
||||
if (this->onWritePixels(surface,
|
||||
rect,
|
||||
surfaceColorType,
|
||||
srcColorType,
|
||||
texels,
|
||||
mipLevelCount,
|
||||
prepForTexSampling)) {
|
||||
this->didWriteToSurface(surface, kTopLeft_GrSurfaceOrigin, &rect, mipLevelCount);
|
||||
fStats.incTextureUploads();
|
||||
return true;
|
||||
if (!this->onWritePixels(surface,
|
||||
rect,
|
||||
surfaceColorType,
|
||||
srcColorType,
|
||||
texels,
|
||||
mipLevelCount,
|
||||
prepForTexSampling)) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
this->didWriteToSurface(surface, kTopLeft_GrSurfaceOrigin, &rect, mipLevelCount);
|
||||
fStats.incTextureUploads();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GrGpu::transferFromBufferToBuffer(sk_sp<GrGpuBuffer> src,
|
||||
size_t srcOffset,
|
||||
sk_sp<GrGpuBuffer> dst,
|
||||
size_t dstOffset,
|
||||
size_t size) {
|
||||
SkASSERT(src);
|
||||
SkASSERT(dst);
|
||||
SkASSERT(srcOffset % this->caps()->transferFromBufferToBufferAlignment() == 0);
|
||||
SkASSERT(dstOffset % this->caps()->transferFromBufferToBufferAlignment() == 0);
|
||||
SkASSERT(size % this->caps()->transferFromBufferToBufferAlignment() == 0);
|
||||
SkASSERT(srcOffset + size <= src->size());
|
||||
SkASSERT(dstOffset + size <= dst->size());
|
||||
SkASSERT(src->intendedType() == GrGpuBufferType::kXferCpuToGpu);
|
||||
SkASSERT(dst->intendedType() != GrGpuBufferType::kXferCpuToGpu);
|
||||
SkASSERT(!src->isMapped());
|
||||
SkASSERT(!dst->isMapped());
|
||||
|
||||
this->handleDirtyContext();
|
||||
if (!this->onTransferFromBufferToBuffer(std::move(src),
|
||||
srcOffset,
|
||||
std::move(dst),
|
||||
dstOffset,
|
||||
size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fStats.incBufferTransfers();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GrGpu::transferPixelsTo(GrTexture* texture,
|
||||
@ -507,6 +545,7 @@ bool GrGpu::transferPixelsTo(GrTexture* texture,
|
||||
TRACE_EVENT0("skia.gpu", TRACE_FUNC);
|
||||
SkASSERT(texture);
|
||||
SkASSERT(transferBuffer);
|
||||
SkASSERT(transferBuffer->intendedType() == GrGpuBufferType::kXferCpuToGpu);
|
||||
|
||||
if (texture->readOnly()) {
|
||||
return false;
|
||||
@ -532,19 +571,20 @@ bool GrGpu::transferPixelsTo(GrTexture* texture,
|
||||
}
|
||||
|
||||
this->handleDirtyContext();
|
||||
if (this->onTransferPixelsTo(texture,
|
||||
rect,
|
||||
textureColorType,
|
||||
bufferColorType,
|
||||
std::move(transferBuffer),
|
||||
offset,
|
||||
rowBytes)) {
|
||||
this->didWriteToSurface(texture, kTopLeft_GrSurfaceOrigin, &rect);
|
||||
fStats.incTransfersToTexture();
|
||||
|
||||
return true;
|
||||
if (!this->onTransferPixelsTo(texture,
|
||||
rect,
|
||||
textureColorType,
|
||||
bufferColorType,
|
||||
std::move(transferBuffer),
|
||||
offset,
|
||||
rowBytes)) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
this->didWriteToSurface(texture, kTopLeft_GrSurfaceOrigin, &rect);
|
||||
fStats.incTransfersToTexture();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GrGpu::transferPixelsFrom(GrSurface* surface,
|
||||
@ -556,6 +596,7 @@ bool GrGpu::transferPixelsFrom(GrSurface* surface,
|
||||
TRACE_EVENT0("skia.gpu", TRACE_FUNC);
|
||||
SkASSERT(surface);
|
||||
SkASSERT(transferBuffer);
|
||||
SkASSERT(transferBuffer->intendedType() == GrGpuBufferType::kXferGpuToCpu);
|
||||
SkASSERT(this->caps()->areColorTypeAndFormatCompatible(surfaceColorType,
|
||||
surface->backendFormat()));
|
||||
|
||||
@ -572,16 +613,18 @@ bool GrGpu::transferPixelsFrom(GrSurface* surface,
|
||||
}
|
||||
|
||||
this->handleDirtyContext();
|
||||
if (this->onTransferPixelsFrom(surface,
|
||||
rect,
|
||||
surfaceColorType,
|
||||
bufferColorType,
|
||||
std::move(transferBuffer),
|
||||
offset)) {
|
||||
fStats.incTransfersFromSurface();
|
||||
return true;
|
||||
if (!this->onTransferPixelsFrom(surface,
|
||||
rect,
|
||||
surfaceColorType,
|
||||
bufferColorType,
|
||||
std::move(transferBuffer),
|
||||
offset)) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
fStats.incTransfersFromSurface();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GrGpu::regenerateMipMapLevels(GrTexture* texture) {
|
||||
|
@ -299,6 +299,23 @@ public:
|
||||
prepForTexSampling);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfer bytes from one GPU buffer to another. The src buffer must have type kXferCpuToGpu
|
||||
* and the dst buffer must not. Neither buffer may currently be mapped. The offsets and size
|
||||
* must be aligned to GrCaps::transferFromBufferToBufferAlignment.
|
||||
*
|
||||
* @param src the buffer to read from
|
||||
* @param srcOffset the aligned offset at the src at which the transfer begins.
|
||||
* @param dst the buffer to write to
|
||||
* @param dstOffset the aligned offset in the dst at which the transfer begins
|
||||
* @param size the aligned number of bytes to transfer;
|
||||
*/
|
||||
bool transferFromBufferToBuffer(sk_sp<GrGpuBuffer> src,
|
||||
size_t srcOffset,
|
||||
sk_sp<GrGpuBuffer> dst,
|
||||
size_t dstOffset,
|
||||
size_t size);
|
||||
|
||||
/**
|
||||
* Updates the pixels in a rectangle of a texture using a buffer. If the texture is MIP mapped,
|
||||
* the base level is written to.
|
||||
@ -445,6 +462,9 @@ public:
|
||||
int transfersFromSurface() const { return fTransfersFromSurface; }
|
||||
void incTransfersFromSurface() { fTransfersFromSurface++; }
|
||||
|
||||
void incBufferTransfers() { fBufferTransfers++; }
|
||||
int bufferTransfers() const { return fBufferTransfers; }
|
||||
|
||||
int stencilAttachmentCreates() const { return fStencilAttachmentCreates; }
|
||||
void incStencilAttachmentCreates() { fStencilAttachmentCreates++; }
|
||||
|
||||
@ -481,6 +501,7 @@ public:
|
||||
int fTextureUploads = 0;
|
||||
int fTransfersToTexture = 0;
|
||||
int fTransfersFromSurface = 0;
|
||||
int fBufferTransfers = 0;
|
||||
int fStencilAttachmentCreates = 0;
|
||||
int fMSAAAttachmentCreates = 0;
|
||||
int fNumDraws = 0;
|
||||
@ -500,6 +521,7 @@ public:
|
||||
void incTextureCreates() {}
|
||||
void incTextureUploads() {}
|
||||
void incTransfersToTexture() {}
|
||||
void incBufferTransfers() {}
|
||||
void incTransfersFromSurface() {}
|
||||
void incStencilAttachmentCreates() {}
|
||||
void incMSAAAttachmentCreates() {}
|
||||
@ -748,10 +770,20 @@ private:
|
||||
int mipLevelCount,
|
||||
bool prepForTexSampling) = 0;
|
||||
|
||||
// overridden by backend-specific derived class to perform the buffer transfer
|
||||
virtual bool onTransferFromBufferToBuffer(sk_sp<GrGpuBuffer> src,
|
||||
size_t srcOffset,
|
||||
sk_sp<GrGpuBuffer> dst,
|
||||
size_t dstOffset,
|
||||
size_t size) {
|
||||
// TODO: Make this pure virtual once it is implemented on all subclasses.
|
||||
return false;
|
||||
}
|
||||
|
||||
// overridden by backend-specific derived class to perform the texture transfer
|
||||
virtual bool onTransferPixelsTo(GrTexture*,
|
||||
SkIRect,
|
||||
GrColorType textiueColorType,
|
||||
GrColorType textureColorType,
|
||||
GrColorType bufferColorType,
|
||||
sk_sp<GrGpuBuffer> transferBuffer,
|
||||
size_t offset,
|
||||
|
@ -80,13 +80,14 @@ public:
|
||||
*/
|
||||
bool updateData(const void* src, size_t srcSizeInBytes);
|
||||
|
||||
GrGpuBufferType intendedType() const { return fIntendedType; }
|
||||
|
||||
protected:
|
||||
GrGpuBuffer(GrGpu*,
|
||||
size_t sizeInBytes,
|
||||
GrGpuBufferType,
|
||||
GrAccessPattern,
|
||||
std::string_view label);
|
||||
GrGpuBufferType intendedType() const { return fIntendedType; }
|
||||
|
||||
void* fMapPtr;
|
||||
|
||||
|
@ -763,7 +763,7 @@ void SurfaceContext::asyncReadPixels(GrDirectContext* dContext,
|
||||
callbackContext,
|
||||
rect.size(),
|
||||
colorType,
|
||||
this->caps()->transferBufferAlignment(),
|
||||
this->caps()->transferBufferRowBytesAlignment(),
|
||||
mappedBufferManager,
|
||||
std::move(transferResult)};
|
||||
auto finishCallback = [](GrGpuFinishedContext c) {
|
||||
@ -996,7 +996,7 @@ void SurfaceContext::asyncRescaleAndReadPixelsYUV420(GrDirectContext* dContext,
|
||||
callbackContext,
|
||||
dContext->priv().clientMappedBufferManager(),
|
||||
dstSize,
|
||||
this->caps()->transferBufferAlignment(),
|
||||
this->caps()->transferBufferRowBytesAlignment(),
|
||||
std::move(yTransfer),
|
||||
std::move(uTransfer),
|
||||
std::move(vTransfer)};
|
||||
@ -1265,7 +1265,7 @@ SurfaceContext::PixelTransferResult SurfaceContext::transferPixels(GrColorType d
|
||||
}
|
||||
|
||||
size_t rowBytes = GrColorTypeBytesPerPixel(supportedRead.fColorType) * rect.width();
|
||||
rowBytes = SkAlignTo(rowBytes, this->caps()->transferBufferAlignment());
|
||||
rowBytes = SkAlignTo(rowBytes, this->caps()->transferBufferRowBytesAlignment());
|
||||
size_t size = rowBytes * rect.height();
|
||||
// By using kStream_GrAccessPattern here, we are not able to cache and reuse the buffer for
|
||||
// multiple reads. Switching to kDynamic_GrAccessPattern would allow for this, however doing
|
||||
|
@ -55,7 +55,7 @@ GrD3DCaps::GrD3DCaps(const GrContextOptions& contextOptions, IDXGIAdapter1* adap
|
||||
fMaxRenderTargetSize = 16384; // minimum required by feature level 11_0
|
||||
fMaxTextureSize = 16384; // minimum required by feature level 11_0
|
||||
|
||||
fTransferBufferAlignment = D3D12_TEXTURE_DATA_PITCH_ALIGNMENT;
|
||||
fTransferBufferRowBytesAlignment = D3D12_TEXTURE_DATA_PITCH_ALIGNMENT;
|
||||
|
||||
// TODO: implement
|
||||
fDynamicStateArrayGeometryProcessorTextureSupport = false;
|
||||
|
@ -110,6 +110,14 @@ private:
|
||||
return true;
|
||||
}
|
||||
|
||||
bool onTransferFromBufferToBuffer(sk_sp<GrGpuBuffer> src,
|
||||
size_t srcOffset,
|
||||
sk_sp<GrGpuBuffer> dst,
|
||||
size_t dstOffset,
|
||||
size_t size) override {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool onTransferPixelsTo(GrTexture*,
|
||||
SkIRect,
|
||||
GrColorType surfaceColorType,
|
||||
|
@ -336,7 +336,14 @@ void GrMtlCaps::initGrCaps(id<MTLDevice> device) {
|
||||
fMaxTextureSize = fMaxRenderTargetSize;
|
||||
|
||||
fMaxPushConstantsSize = 4*1024;
|
||||
fTransferBufferAlignment = 1;
|
||||
fTransferBufferRowBytesAlignment = 1;
|
||||
|
||||
// This is documented to be 4 for all Macs. However, on Apple GPUs on Mac it appears there is
|
||||
// no actual alignment requirement
|
||||
// https://developer.apple.com/documentation/metal/mtlblitcommandencoder/1400767-copyfrombuffer
|
||||
if (this->isMac() && fFamilyGroup < 7) {
|
||||
fTransferFromBufferToBufferAlignment = 4;
|
||||
}
|
||||
|
||||
// Init sample counts. All devices support 1 (i.e. 0 in skia).
|
||||
fSampleCounts.push_back(1);
|
||||
@ -372,6 +379,7 @@ void GrMtlCaps::initGrCaps(id<MTLDevice> device) {
|
||||
|
||||
fTransferFromBufferToTextureSupport = true;
|
||||
fTransferFromSurfaceToBufferSupport = true;
|
||||
fTransferFromBufferToBufferSupport = true;
|
||||
|
||||
fTextureBarrierSupport = false; // Need to figure out if we can do this
|
||||
|
||||
|
@ -196,6 +196,12 @@ private:
|
||||
void*,
|
||||
size_t rowBytes) override;
|
||||
|
||||
bool onTransferFromBufferToBuffer(sk_sp<GrGpuBuffer> src,
|
||||
size_t srcOffset,
|
||||
sk_sp<GrGpuBuffer> dst,
|
||||
size_t dstOffset,
|
||||
size_t size) override;
|
||||
|
||||
bool onWritePixels(GrSurface*,
|
||||
SkIRect,
|
||||
GrColorType surfaceColorType,
|
||||
|
@ -1389,6 +1389,36 @@ bool GrMtlGpu::onReadPixels(GrSurface* surface,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GrMtlGpu::onTransferFromBufferToBuffer(sk_sp<GrGpuBuffer> src,
|
||||
size_t srcOffset,
|
||||
sk_sp<GrGpuBuffer> dst,
|
||||
size_t dstOffset,
|
||||
size_t size) {
|
||||
id<MTLBuffer> GR_NORETAIN mtlSrc = static_cast<GrMtlBuffer*>(src.get())->mtlBuffer();
|
||||
id<MTLBuffer> GR_NORETAIN mtlDst = static_cast<GrMtlBuffer*>(dst.get())->mtlBuffer();
|
||||
SkASSERT(mtlSrc);
|
||||
SkASSERT(mtlDst);
|
||||
|
||||
auto cmdBuffer = this->commandBuffer();
|
||||
id<MTLBlitCommandEncoder> GR_NORETAIN blitCmdEncoder = cmdBuffer->getBlitCommandEncoder();
|
||||
if (!blitCmdEncoder) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef SK_ENABLE_MTL_DEBUG_INFO
|
||||
[blitCmdEncoder pushDebugGroup:@"onTransferFromBufferToBuffer"];
|
||||
#endif
|
||||
[blitCmdEncoder copyFromBuffer: mtlSrc
|
||||
sourceOffset: srcOffset
|
||||
toBuffer: mtlDst
|
||||
destinationOffset: dstOffset
|
||||
size: size];
|
||||
#ifdef SK_ENABLE_MTL_DEBUG_INFO
|
||||
[blitCmdEncoder popDebugGroup];
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GrMtlGpu::onTransferPixelsTo(GrTexture* texture,
|
||||
SkIRect rect,
|
||||
GrColorType textureColorType,
|
||||
|
298
tests/GrGpuBufferTest.cpp
Normal file
298
tests/GrGpuBufferTest.cpp
Normal file
@ -0,0 +1,298 @@
|
||||
/*
|
||||
* Copyright 2022 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "include/core/SkColorSpace.h"
|
||||
#include "include/gpu/GrDirectContext.h"
|
||||
#include "src/gpu/ganesh/GrDirectContextPriv.h"
|
||||
#include "src/gpu/ganesh/GrGpu.h"
|
||||
#include "src/gpu/ganesh/GrGpuBuffer.h"
|
||||
#include "src/gpu/ganesh/GrResourceProvider.h"
|
||||
#include "src/gpu/ganesh/glsl/GrGLSLFragmentShaderBuilder.h"
|
||||
#include "src/gpu/ganesh/ops/GrMeshDrawOp.h"
|
||||
#include "src/gpu/ganesh/ops/GrSimpleMeshDrawOpHelper.h"
|
||||
#include "src/gpu/ganesh/v1/SurfaceDrawContext_v1.h"
|
||||
#include "tests/Test.h"
|
||||
#include "tools/gpu/GrContextFactory.h"
|
||||
|
||||
// Simple op that draws a vertex buffer with float2 positions as green triangles. We use this to
|
||||
// draw GrGpuBuffers to test that the buffer contains the expected values as not all contexts will
|
||||
// support buffer mapping.
|
||||
class TestVertexOp final : public GrMeshDrawOp {
|
||||
public:
|
||||
static GrOp::Owner Make(GrRecordingContext* context,
|
||||
sk_sp<GrGpuBuffer> buffer,
|
||||
int baseVertex,
|
||||
int vertexCount,
|
||||
const SkRect& bounds) {
|
||||
return GrOp::Make<TestVertexOp>(context,
|
||||
std::move(buffer),
|
||||
baseVertex,
|
||||
vertexCount,
|
||||
bounds);
|
||||
}
|
||||
|
||||
const char* name() const override { return "TestVertexOp"; }
|
||||
|
||||
FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
|
||||
|
||||
GrProcessorSet::Analysis finalize(const GrCaps& caps,
|
||||
const GrAppliedClip* clip,
|
||||
GrClampType clampType) override {
|
||||
static constexpr SkPMColor4f kGreen{0, 1, 0, 1};
|
||||
SkPMColor4f color = kGreen;
|
||||
return fProcessorSet.finalize(GrProcessorAnalysisColor::Opaque::kYes,
|
||||
GrProcessorAnalysisCoverage::kNone,
|
||||
clip,
|
||||
&GrUserStencilSettings::kUnused,
|
||||
caps,
|
||||
clampType,
|
||||
&color);
|
||||
SkASSERT(color == kGreen);
|
||||
}
|
||||
|
||||
void visitProxies(const GrVisitProxyFunc& func) const override {
|
||||
if (fProgramInfo) {
|
||||
fProgramInfo->visitFPProxies(func);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
DEFINE_OP_CLASS_ID
|
||||
|
||||
TestVertexOp(sk_sp<GrGpuBuffer> buffer,
|
||||
int baseVertex,
|
||||
int vertexCount,
|
||||
const SkRect& bounds)
|
||||
: GrMeshDrawOp(ClassID())
|
||||
, fBuffer(std::move(buffer))
|
||||
, fProcessorSet(SkBlendMode::kSrc)
|
||||
, fBaseVertex(baseVertex)
|
||||
, fVertexCount(vertexCount) {
|
||||
this->setBounds(bounds, HasAABloat::kNo, GrOp::IsHairline::kNo);
|
||||
}
|
||||
|
||||
GrProgramInfo* programInfo() override { return fProgramInfo; }
|
||||
|
||||
void onCreateProgramInfo(const GrCaps* caps,
|
||||
SkArenaAlloc* arena,
|
||||
const GrSurfaceProxyView& writeView,
|
||||
bool usesMSAASurface,
|
||||
GrAppliedClip&& appliedClip,
|
||||
const GrDstProxyView& dstProxyView,
|
||||
GrXferBarrierFlags renderPassXferBarriers,
|
||||
GrLoadOp colorLoadOp) override {
|
||||
fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(
|
||||
caps,
|
||||
arena,
|
||||
writeView,
|
||||
usesMSAASurface,
|
||||
std::move(appliedClip),
|
||||
dstProxyView,
|
||||
&fGP,
|
||||
std::move(fProcessorSet),
|
||||
GrPrimitiveType::kTriangles,
|
||||
renderPassXferBarriers,
|
||||
colorLoadOp,
|
||||
GrPipeline::InputFlags::kNone);
|
||||
}
|
||||
|
||||
class GP : public GrGeometryProcessor {
|
||||
public:
|
||||
GP() : GrGeometryProcessor(kTestFP_ClassID) {
|
||||
this->setVertexAttributesWithImplicitOffsets(&kPos, 1);
|
||||
}
|
||||
|
||||
const char* name() const override { return "TestVertexOp::GP"; }
|
||||
|
||||
std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const override {
|
||||
class Impl : public ProgramImpl {
|
||||
public:
|
||||
void setData(const GrGLSLProgramDataManager&,
|
||||
const GrShaderCaps&,
|
||||
const GrGeometryProcessor&) override {}
|
||||
|
||||
private:
|
||||
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
|
||||
const auto& gp = args.fGeomProc.cast<GP>();
|
||||
args.fVaryingHandler->emitAttributes(gp);
|
||||
args.fFragBuilder->codeAppendf("half4 %s = half4(0, 1, 0, 1);",
|
||||
args.fOutputColor);
|
||||
args.fFragBuilder->codeAppendf("const half4 %s = half4(1);",
|
||||
args.fOutputCoverage);
|
||||
WriteOutputPosition(args.fVertBuilder, gpArgs, kPos.name());
|
||||
}
|
||||
|
||||
UniformHandle fLocalMatrixUni;
|
||||
};
|
||||
|
||||
return std::make_unique<Impl>();
|
||||
}
|
||||
|
||||
void addToKey(const GrShaderCaps &caps, skgpu::KeyBuilder *builder) const override {}
|
||||
|
||||
private:
|
||||
static constexpr Attribute kPos = {"pos", kFloat2_GrVertexAttribType, SkSLType::kFloat2};
|
||||
};
|
||||
|
||||
void onPrepareDraws(GrMeshDrawTarget* target) override {
|
||||
fMesh = target->allocMesh();
|
||||
fMesh->set(fBuffer, fVertexCount, fBaseVertex);
|
||||
}
|
||||
|
||||
void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
|
||||
if (!fProgramInfo) {
|
||||
this->createProgramInfo(flushState);
|
||||
}
|
||||
|
||||
flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
|
||||
flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
|
||||
flushState->drawMesh(*fMesh);
|
||||
}
|
||||
|
||||
sk_sp<GrGpuBuffer> fBuffer;
|
||||
|
||||
GP fGP;
|
||||
|
||||
GrProcessorSet fProcessorSet;
|
||||
|
||||
int fBaseVertex;
|
||||
int fVertexCount;
|
||||
|
||||
GrProgramInfo* fProgramInfo = nullptr;
|
||||
GrSimpleMesh* fMesh = nullptr;
|
||||
|
||||
friend class ::GrOp;
|
||||
};
|
||||
|
||||
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrGpuBufferTransferTest, reporter, ctxInfo) {
|
||||
if (!ctxInfo.directContext()->priv().caps()->transferFromBufferToBufferSupport()) {
|
||||
return;
|
||||
}
|
||||
|
||||
GrDirectContext* dc = ctxInfo.directContext();
|
||||
|
||||
GrResourceProvider* rp = ctxInfo.directContext()->priv().resourceProvider();
|
||||
|
||||
GrGpu* gpu = ctxInfo.directContext()->priv().getGpu();
|
||||
|
||||
auto create_cpu_to_gpu_buffer = [&](int baseVertex) {
|
||||
// Ensure any extra vertices are offscreen
|
||||
int totalVertices = baseVertex + 6;
|
||||
auto points = std::make_unique<SkPoint[]>(totalVertices);
|
||||
SkPoint offscreenPt{-10000, -10000};
|
||||
std::fill_n(points.get(), totalVertices, offscreenPt);
|
||||
|
||||
// set the quad at the desired base vertex
|
||||
static constexpr SkPoint kUnitQuad[] {{0, 0}, {0, 1}, {1, 0},
|
||||
{1, 0}, {0, 1}, {1, 1}};
|
||||
std::copy_n(kUnitQuad, 6, points.get() + baseVertex);
|
||||
|
||||
return rp->createBuffer(points.get(),
|
||||
totalVertices*sizeof(SkPoint),
|
||||
GrGpuBufferType::kXferCpuToGpu,
|
||||
kStream_GrAccessPattern);
|
||||
};
|
||||
|
||||
auto create_vertex_buffer = [&](sk_sp<GrGpuBuffer> srcBuffer,
|
||||
int srcBaseVertex,
|
||||
int vbBaseVertex,
|
||||
bool minSizedTransfers) {
|
||||
// make initialization data of offscreen points.
|
||||
int dstVertexCount = vbBaseVertex + 6;
|
||||
auto points = std::make_unique<SkPoint[]>(dstVertexCount);
|
||||
SkPoint offscreenPt{-10000, -10000};
|
||||
std::fill_n(points.get(), dstVertexCount, offscreenPt);
|
||||
|
||||
sk_sp<GrGpuBuffer> vb = rp->createBuffer(points.get(),
|
||||
dstVertexCount*sizeof(SkPoint),
|
||||
GrGpuBufferType::kVertex,
|
||||
kDynamic_GrAccessPattern);
|
||||
|
||||
// copy actual quad data from the source buffer to our new vb.
|
||||
|
||||
static constexpr size_t kTotalSize = 6*sizeof(SkPoint);
|
||||
|
||||
size_t srcOffset = srcBaseVertex*sizeof(SkPoint);
|
||||
size_t vbOffset = vbBaseVertex*sizeof(SkPoint);
|
||||
|
||||
size_t alignment = gpu->caps()->transferFromBufferToBufferAlignment();
|
||||
SkASSERT(kTotalSize % alignment == 0);
|
||||
SkASSERT(sizeof(SkPoint) % alignment == 0);
|
||||
|
||||
if (minSizedTransfers) {
|
||||
for (size_t n = kTotalSize/alignment, i = 0; i < n; ++i) {
|
||||
gpu->transferFromBufferToBuffer(srcBuffer,
|
||||
srcOffset + i*alignment,
|
||||
vb,
|
||||
vbOffset + i*alignment,
|
||||
alignment);
|
||||
}
|
||||
} else {
|
||||
gpu->transferFromBufferToBuffer(srcBuffer, srcOffset, vb, vbOffset, kTotalSize);
|
||||
}
|
||||
return vb;
|
||||
};
|
||||
|
||||
auto sdc = skgpu::v1::SurfaceDrawContext::Make(dc,
|
||||
GrColorType::kRGBA_8888,
|
||||
nullptr,
|
||||
SkBackingFit::kExact,
|
||||
{1, 1},
|
||||
SkSurfaceProps{},
|
||||
std::string_view{});
|
||||
if (!sdc) {
|
||||
ERRORF(reporter, "Could not create draw context");
|
||||
return;
|
||||
}
|
||||
|
||||
auto pm = GrPixmap::Allocate(sdc->imageInfo().makeColorType(GrColorType::kRGBA_F32));
|
||||
|
||||
for (bool byteAtATime : {false, true}) {
|
||||
for (int srcBaseVertex : {0, 5}) {
|
||||
auto src = create_cpu_to_gpu_buffer(srcBaseVertex);
|
||||
if (!src) {
|
||||
ERRORF(reporter, "Could not create src buffer");
|
||||
return;
|
||||
}
|
||||
for (int vbBaseVertex : {0, 2}) {
|
||||
auto vb = create_vertex_buffer(src, srcBaseVertex, vbBaseVertex, byteAtATime);
|
||||
if (!vb) {
|
||||
ERRORF(reporter, "Could not create vertex buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
static constexpr SkColor4f kRed{1, 0, 0, 1};
|
||||
|
||||
static constexpr SkRect kBounds{0, 0, 1, 1};
|
||||
|
||||
sdc->clear(kRed);
|
||||
|
||||
sdc->addDrawOp(nullptr, TestVertexOp::Make(dc,
|
||||
vb,
|
||||
vbBaseVertex,
|
||||
/*vertexCount=*/6,
|
||||
kBounds));
|
||||
|
||||
auto color = static_cast<SkPMColor4f*>(pm.addr());
|
||||
*color = kRed.premul();
|
||||
if (!sdc->readPixels(dc, pm, {0, 0})) {
|
||||
ERRORF(reporter, "Read back failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
static constexpr SkPMColor4f kGreen{0, 1, 0, 1};
|
||||
|
||||
REPORTER_ASSERT(reporter, *color == kGreen, "src base vertex: %d, "
|
||||
"vb base vertex: %d, "
|
||||
"byteAtATime: %d",
|
||||
srcBaseVertex,
|
||||
vbBaseVertex,
|
||||
byteAtATime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -163,7 +163,7 @@ void basic_transfer_to_test(skiatest::Reporter* reporter,
|
||||
return;
|
||||
}
|
||||
size_t srcRowBytes = SkAlignTo(GrColorTypeBytesPerPixel(allowedSrc.fColorType) * srcBufferWidth,
|
||||
caps->transferBufferAlignment());
|
||||
caps->transferBufferRowBytesAlignment());
|
||||
|
||||
std::unique_ptr<char[]> srcData(new char[kTexDims.fHeight * srcRowBytes]);
|
||||
|
||||
@ -355,8 +355,10 @@ void basic_transfer_from_test(skiatest::Reporter* reporter, const sk_gpu_test::C
|
||||
GrImageInfo readInfo(allowedRead.fColorType, kUnpremul_SkAlphaType, nullptr, kTexDims);
|
||||
|
||||
size_t bpp = GrColorTypeBytesPerPixel(allowedRead.fColorType);
|
||||
size_t fullBufferRowBytes = SkAlignTo(kTexDims.fWidth * bpp, caps->transferBufferAlignment());
|
||||
size_t partialBufferRowBytes = SkAlignTo(kPartialWidth * bpp, caps->transferBufferAlignment());
|
||||
size_t fullBufferRowBytes = SkAlignTo(kTexDims.fWidth * bpp,
|
||||
caps->transferBufferRowBytesAlignment());
|
||||
size_t partialBufferRowBytes = SkAlignTo(kPartialWidth * bpp,
|
||||
caps->transferBufferRowBytesAlignment());
|
||||
size_t offsetAlignment = allowedRead.fOffsetAlignmentForTransferBuffer;
|
||||
SkASSERT(offsetAlignment);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user