diff --git a/gm/BUILD.bazel b/gm/BUILD.bazel index 46e4629de6..24a0c26bda 100644 --- a/gm/BUILD.bazel +++ b/gm/BUILD.bazel @@ -3238,7 +3238,9 @@ generated_cc_atom( "//include/core:SkData_hdr", "//include/core:SkSurface_hdr", "//include/effects:SkGradientShader_hdr", + "//include/gpu:GrDirectContext_hdr", "//src/core:SkCanvasPriv_hdr", + "//src/core:SkCustomMeshPriv_hdr", ], ) diff --git a/gm/custommesh.cpp b/gm/custommesh.cpp index e58be893ad..c390768a91 100644 --- a/gm/custommesh.cpp +++ b/gm/custommesh.cpp @@ -13,7 +13,9 @@ #include "include/core/SkData.h" #include "include/core/SkSurface.h" #include "include/effects/SkGradientShader.h" +#include "include/gpu/GrDirectContext.h" #include "src/core/SkCanvasPriv.h" +#include "src/core/SkCustomMeshPriv.h" #include @@ -102,31 +104,29 @@ protected: nullptr, 2, SkTileMode::kMirror); - fColorVB = SkCustomMesh::MakeVertexBuffer( - /*GrDirectContext*=*/nullptr, - SkData::MakeWithoutCopy(kColorQuad, sizeof(kColorQuad))); + } - // Make this one such that the data is offset into the buffer. - auto data = SkData::MakeUninitialized(sizeof(kNoColorQuad) + kNoColorOffset); - std::memcpy(SkTAddOffset(data->writable_data(), kNoColorOffset), - kNoColorQuad, - sizeof(kNoColorQuad)); - fNoColorVB = SkCustomMesh::MakeVertexBuffer(/*GrDirectContext*=*/nullptr, std::move(data)); + DrawResult onGpuSetup(GrDirectContext* context, SkString* string) override { + this->ensureBuffers(); + if (!context || context->abandoned()) { + return DrawResult::kOk; + } - fColorIndexedVB = SkCustomMesh::MakeVertexBuffer( - /*GrDirectContext*=*/nullptr, - SkData::MakeWithoutCopy(kColorIndexedQuad, sizeof(kColorIndexedQuad))); + fColorVB = SkCustomMesh::MakeVertexBuffer(context, CpuVBAsData(fColorVB)); + fColorIndexedVB = SkCustomMesh::MakeVertexBuffer(context, CpuVBAsData(fColorIndexedVB)); + fIB[1] = SkCustomMesh::MakeIndexBuffer (context, CpuIBAsData(fIB[0])); + if (!fColorVB || !fColorIndexedVB || !fIB[1]) { + return DrawResult::kFail; + } + return DrawResult::kOk; + } - fNoColorIndexedVB = SkCustomMesh::MakeVertexBuffer( - /*GrDirectContext*=*/nullptr, - SkData::MakeWithoutCopy(kNoColorIndexedQuad, sizeof(kNoColorIndexedQuad))); - - // Also make index buffer with an offset - data = SkData::MakeUninitialized(sizeof(kIndices) + kIndexOffset); - std::memcpy(SkTAddOffset(data->writable_data(), kIndexOffset), - kIndices, - sizeof(kIndices)); - fIB = SkCustomMesh::MakeIndexBuffer(/*GrDirectContext*=*/nullptr, std::move(data)); + void onGpuTeardown() override { + // Destroy the GPU buffers and recreate on CPU + fColorVB = nullptr; + fColorIndexedVB = nullptr; + fIB[1] = nullptr; + this->ensureBuffers(); } SkString onShortName() override { return SkString("custommesh"); } @@ -159,30 +159,30 @@ protected: kRect); } } else { + // Alternate between CPU and GPU-backend index buffers. + auto ib = (i%4 == 0) ? fIB[0] : fIB[1]; if (colors) { cm = SkCustomMesh::MakeIndexed(fSpecWithColor, SkCustomMesh::Mode::kTriangles, fColorIndexedVB, - /*vertexCount=*/ 6, - /*vertexOffset=*/0, - fIB, + /*vertexCount=*/6, + kColorIndexedOffset, + std::move(ib), /*indexCount=*/6, kIndexOffset, kRect); - } else { cm = SkCustomMesh::MakeIndexed(fSpecWithNoColor, SkCustomMesh::Mode::kTriangles, fNoColorIndexedVB, /*vertexCount=*/ 6, /*vertexOffset=*/0, - fIB, + std::move(ib), /*indexCount=*/6, kIndexOffset, kRect); } } - SkPaint paint; paint.setColor(SK_ColorGREEN); paint.setShader(shader ? fShader : nullptr); @@ -200,6 +200,66 @@ protected: } private: + static sk_sp CpuVBAsData(sk_sp buffer) { + auto vb = static_cast(buffer.get()); + SkASSERT(vb->asData()); + return vb->asData(); + } + + static sk_sp CpuIBAsData(sk_sp buffer) { + auto ib = static_cast(buffer.get()); + SkASSERT(ib->asData()); + return ib->asData(); + } + + void ensureBuffers() { + if (!fColorVB) { + fColorVB = SkCustomMesh::MakeVertexBuffer( + /*GrDirectContext*=*/nullptr, + SkData::MakeWithoutCopy(kColorQuad, sizeof(kColorQuad))); + } + + if (!fNoColorVB) { + // Make this one such that the data is offset into the buffer. + auto data = SkData::MakeUninitialized(sizeof(kNoColorQuad) + kNoColorOffset); + std::memcpy(SkTAddOffset(data->writable_data(), kNoColorOffset), + kNoColorQuad, + sizeof(kNoColorQuad)); + fNoColorVB = SkCustomMesh::MakeVertexBuffer(/*GrDirectContext*=*/nullptr, + std::move(data)); + } + + if (!fColorIndexedVB) { + // This buffer also has an offset. + auto data = SkData::MakeUninitialized(sizeof(kColorIndexedQuad) + kColorIndexedOffset); + std::memcpy(SkTAddOffset(data->writable_data(), kColorIndexedOffset), + kColorIndexedQuad, + sizeof(kColorIndexedQuad)); + fColorIndexedVB = SkCustomMesh::MakeVertexBuffer(/*GrDirectContext*=*/nullptr, + std::move(data)); + } + + if (!fNoColorIndexedVB) { + fNoColorIndexedVB = SkCustomMesh::MakeVertexBuffer( + /*GrDirectContext*=*/nullptr, + SkData::MakeWithoutCopy(kNoColorIndexedQuad, sizeof(kNoColorIndexedQuad))); + } + + if (!fIB[0]) { + // The index buffer has an offset. + auto data = SkData::MakeUninitialized(sizeof(kIndices) + kIndexOffset); + std::memcpy(SkTAddOffset(data->writable_data(), kIndexOffset), + kIndices, + sizeof(kIndices)); + fIB[0] = SkCustomMesh::MakeIndexBuffer(/*GrDirectContext*=*/nullptr, std::move(data)); + } + + if (!fIB[1]) { + // On CPU we always use the same CPU-backed index buffer. + fIB[1] = fIB[0]; + } + } + struct ColorVertex { uint32_t pad; uint32_t brag; @@ -249,15 +309,18 @@ private: static constexpr uint16_t kIndices[]{0, 2, 4, 2, 5, 4}; - static constexpr size_t kNoColorOffset = sizeof(NoColorVertex); - static constexpr size_t kIndexOffset = 6; + // For some buffers we add an offset to ensure we're exercising drawing from mid-buffer. + static constexpr size_t kNoColorOffset = sizeof(NoColorVertex); + static constexpr size_t kColorIndexedOffset = 2*sizeof(ColorVertex); + static constexpr size_t kIndexOffset = 6; sk_sp fShader; sk_sp fSpecWithColor; sk_sp fSpecWithNoColor; - sk_sp fIB; + // On GPU the first IB is a CPU buffer and the second is a GPU buffer. + sk_sp fIB[2]; sk_sp fColorVB; sk_sp fNoColorVB; diff --git a/include/core/SkCustomMesh.h b/include/core/SkCustomMesh.h index c2916dcaf8..1ce7cee16b 100644 --- a/include/core/SkCustomMesh.h +++ b/include/core/SkCustomMesh.h @@ -217,18 +217,26 @@ public: /** * Makes an index buffer to be used with SkCustomMeshes. The SkData is used to determine the - * size and contents of the buffer. + * size and contents of the buffer. The buffer may be CPU- or GPU-backed depending on whether + * GrDirectContext* is nullptr. * - * @param GrDirectContext* currently ignored. May be nullptr. + * @param GrDirectContext* If nullptr a CPU-backed object is returned that owns the SkData. + * Otherwise, the data is uploaded to the GPU and a GPU-backed buffer + * is returned. It may only be used to draw into SkSurfaces that + * are backed by the passed GrDirectContext. * @param sk_sp required. The data used to populate the buffer. */ static sk_sp MakeIndexBuffer(GrDirectContext*, sk_sp); /** * Makes a vertex buffer to be used with SkCustomMeshes. The SkData is used to determine the - * size and contents of the buffer. + * size and contents of the buffer.The buffer may be CPU- or GPU-backed depending on whether + * GrDirectContext* is nullptr. * - * @param GrDirectContext* currently ignored. May be nullptr. + * @param GrDirectContext* If nullptr a CPU-backed object is returned that owns the SkData. + * Otherwise, the data is uploaded to the GPU and a GPU-backed buffer + * is returned. It may only be used to draw into SkSurfaces that + * are backed by the passed GrDirectContext. * @param sk_sp required. The data used to populate the buffer. */ static sk_sp MakeVertexBuffer(GrDirectContext*, sk_sp); diff --git a/src/core/BUILD.bazel b/src/core/BUILD.bazel index 3654d0d228..1406a55461 100644 --- a/src/core/BUILD.bazel +++ b/src/core/BUILD.bazel @@ -5566,7 +5566,12 @@ generated_cc_atom( ":SkSLTypeShared_hdr", "//include/core:SkCustomMesh_hdr", "//include/core:SkData_hdr", + "//include/gpu:GrDirectContext_hdr", "//include/private/gpu/ganesh:GrTypesPriv_hdr", + "//src/gpu/ganesh:GrDirectContextPriv_hdr", + "//src/gpu/ganesh:GrGpuBuffer_hdr", + "//src/gpu/ganesh:GrResourceCache_hdr", + "//src/gpu/ganesh:GrResourceProvider_hdr", ], ) diff --git a/src/core/SkCustomMesh.cpp b/src/core/SkCustomMesh.cpp index 6ac7fde341..5360086280 100644 --- a/src/core/SkCustomMesh.cpp +++ b/src/core/SkCustomMesh.cpp @@ -384,12 +384,30 @@ SkCustomMesh::SkCustomMesh(SkCustomMesh&&) = default; SkCustomMesh& SkCustomMesh::operator=(const SkCustomMesh&) = default; SkCustomMesh& SkCustomMesh::operator=(SkCustomMesh&&) = default; -sk_sp SkCustomMesh::MakeIndexBuffer(GrDirectContext*, sk_sp data) { - return SkCustomMeshPriv::CpuIndexBuffer::Make(std::move(data)); +sk_sp SkCustomMesh::MakeIndexBuffer(GrDirectContext* dc, sk_sp data) { + if (!data) { + return nullptr; + } + if (!dc) { + return SkCustomMeshPriv::CpuIndexBuffer::Make(std::move(data)); + } +#if SK_SUPPORT_GPU + return SkCustomMeshPriv::GpuIndexBuffer::Make(dc, std::move(data)); +#endif + return nullptr; } -sk_sp SkCustomMesh::MakeVertexBuffer(GrDirectContext*, sk_sp data) { - return SkCustomMeshPriv::CpuVertexBuffer::Make(std::move(data)); +sk_sp SkCustomMesh::MakeVertexBuffer(GrDirectContext* dc, sk_sp data) { + if (!data) { + return nullptr; + } + if (!dc) { + return SkCustomMeshPriv::CpuVertexBuffer::Make(std::move(data)); + } +#if SK_SUPPORT_GPU + return SkCustomMeshPriv::GpuVertexBuffer::Make(dc, std::move(data)); +#endif + return nullptr; } SkCustomMesh SkCustomMesh::Make(sk_sp spec, diff --git a/src/core/SkCustomMeshPriv.h b/src/core/SkCustomMeshPriv.h index a1e530bb79..39b795a18b 100644 --- a/src/core/SkCustomMeshPriv.h +++ b/src/core/SkCustomMeshPriv.h @@ -15,6 +15,14 @@ #include "include/private/gpu/ganesh/GrTypesPriv.h" #include "src/core/SkSLTypeShared.h" +#if SK_SUPPORT_GPU +#include "include/gpu/GrDirectContext.h" +#include "src/gpu/ganesh/GrDirectContextPriv.h" +#include "src/gpu/ganesh/GrGpuBuffer.h" +#include "src/gpu/ganesh/GrResourceCache.h" +#include "src/gpu/ganesh/GrResourceProvider.h" +#endif + struct SkCustomMeshSpecificationPriv { using Varying = SkCustomMeshSpecification::Varying; using Attribute = SkCustomMeshSpecification::Attribute; @@ -91,7 +99,11 @@ struct SkCustomMeshPriv { Buffer& operator=(const Buffer&) = delete; - virtual sk_sp asData() const = 0; + virtual sk_sp asData() const { return nullptr; } + +#if SK_SUPPORT_GPU + virtual sk_sp asGpuBuffer() const { return nullptr; } +#endif virtual size_t size() const = 0; }; @@ -116,16 +128,69 @@ struct SkCustomMeshPriv { using CpuIndexBuffer = CpuBuffer; using CpuVertexBuffer = CpuBuffer; + +#if SK_SUPPORT_GPU + template class GpuBuffer final : public Base { + public: + GpuBuffer() = default; + + ~GpuBuffer() override; + + static sk_sp Make(GrDirectContext*, sk_sp); + + sk_sp asGpuBuffer() const override { return fBuffer; } + + size_t size() const override { return fBuffer->size(); } + + private: + sk_sp fBuffer; + GrDirectContext::DirectContextID fContextID; + }; + + using GpuIndexBuffer = GpuBuffer; + using GpuVertexBuffer = GpuBuffer; +#endif // SK_SUPPORT_GPU }; inline SkCustomMeshPriv::Buffer::~Buffer() = default; template sk_sp SkCustomMeshPriv::CpuBuffer::Make(sk_sp data) { + SkASSERT(data); + auto result = new CpuBuffer; result->fData = std::move(data); return sk_sp(result); } +#if SK_SUPPORT_GPU + +template +SkCustomMeshPriv::GpuBuffer::~GpuBuffer() { + GrResourceCache::ReturnResourceFromThread(std::move(fBuffer), fContextID); +} + +template +sk_sp SkCustomMeshPriv::GpuBuffer::Make(GrDirectContext* dc, + sk_sp data) { + SkASSERT(dc); + SkASSERT(data); + + sk_sp buffer = dc->priv().resourceProvider()->createBuffer( + data->size(), + Type, + kStatic_GrAccessPattern, + data->data()); + if (!buffer) { + return nullptr; + } + + auto result = new GpuBuffer; + result->fBuffer = std::move(buffer); + result->fContextID = dc->directContextID(); + return sk_sp(result); +} + +#endif // SK_SUPPORT_GPU #endif // SK_ENABLE_SKSL diff --git a/src/gpu/ganesh/ops/DrawCustomMeshOp.cpp b/src/gpu/ganesh/ops/DrawCustomMeshOp.cpp index 0ed3544d0a..cd59109b47 100644 --- a/src/gpu/ganesh/ops/DrawCustomMeshOp.cpp +++ b/src/gpu/ganesh/ops/DrawCustomMeshOp.cpp @@ -396,6 +396,20 @@ private: bool isFromVertices() const { return SkToBool(fVertices); } + std::tuple, size_t> gpuVB() const { + if (this->isFromVertices()) { + return {}; + } + return {fCMData.vb->asGpuBuffer(), fCMData.voffset}; + } + + std::tuple, size_t> gpuIB() const { + if (this->isFromVertices() || !fCMData.ib) { + return {}; + } + return {fCMData.ib->asGpuBuffer(), fCMData.ioffset}; + } + void writeVertices(skgpu::VertexWriter& writer, const SkCustomMeshSpecification& spec, bool transform) const; @@ -405,9 +419,17 @@ private: } const uint16_t* indices() const { - return this->isFromVertices() - ? fVertices->priv().indices() - : SkTAddOffset(fCMData.ib->data(), fCMData.ioffset); + if (this->isFromVertices()) { + return fVertices->priv().indices(); + } + if (!fCMData.ib) { + return nullptr; + } + auto data = fCMData.ib->asData(); + if (!data) { + return nullptr; + } + return SkTAddOffset(data->data(), fCMData.ioffset); } int indexCount() const { @@ -416,8 +438,8 @@ private: private: struct CMData { - sk_sp vb; - sk_sp ib; + sk_sp vb; + sk_sp ib; size_t vcount = 0; size_t icount = 0; @@ -452,9 +474,9 @@ private: CustomMeshOp::Mesh::Mesh(const SkCustomMesh& cm) { new (&fCMData) CMData(); - fCMData.vb = static_cast(cm.vertexBuffer().get())->asData(); + fCMData.vb = sk_ref_sp(static_cast(cm.vertexBuffer().get())); if (cm.indexBuffer()) { - fCMData.ib = static_cast(cm.indexBuffer().get())->asData(); + fCMData.ib = sk_ref_sp(static_cast(cm.indexBuffer().get())); } fCMData.vcount = cm.vertexCount(); fCMData.voffset = cm.vertexOffset(); @@ -507,8 +529,11 @@ void CustomMeshOp::Mesh::writeVertices(skgpu::VertexWriter& writer, } } } else { - auto vb = static_cast(fCMData.vb->data()) + fCMData.voffset; - writer << skgpu::VertexWriter::Array(vb, spec.stride()*fCMData.vcount); + sk_sp data = fCMData.vb->asData(); + if (data) { + auto vdata = static_cast(data->data()) + fCMData.voffset; + writer << skgpu::VertexWriter::Array(vdata, spec.stride() * fCMData.vcount); + } } } @@ -706,25 +731,35 @@ void CustomMeshOp::onCreateProgramInfo(const GrCaps* caps, void CustomMeshOp::onPrepareDraws(GrMeshDrawTarget* target) { size_t vertexStride = fSpecification->stride(); sk_sp vertexBuffer; - int firstVertex = 0; - skgpu::VertexWriter verts = target->makeVertexWriter(vertexStride, - fVertexCount, - &vertexBuffer, - &firstVertex); - if (!verts) { - SkDebugf("Could not allocate vertices.\n"); - return; - } + int firstVertex; + std::tie(vertexBuffer, firstVertex) = fMeshes[0].gpuVB(); - bool transform = fViewMatrix == SkMatrix::InvalidMatrix(); - for (const auto& m : fMeshes) { - m.writeVertices(verts, *fSpecification, transform); + if (!vertexBuffer) { + skgpu::VertexWriter verts = target->makeVertexWriter(vertexStride, + fVertexCount, + &vertexBuffer, + &firstVertex); + if (!verts) { + SkDebugf("Could not allocate vertices.\n"); + return; + } + + bool transform = fViewMatrix == SkMatrix::InvalidMatrix(); + for (const auto& m : fMeshes) { + m.writeVertices(verts, *fSpecification, transform); + } + } else { + SkASSERT(fMeshes.count() == 1); + SkASSERT(firstVertex % fSpecification->stride() == 0); + firstVertex /= fSpecification->stride(); } sk_sp indexBuffer; int firstIndex = 0; - uint16_t* indices = nullptr; - if (fIndexCount) { + + std::tie(indexBuffer, firstIndex) = fMeshes[0].gpuIB(); + if (fIndexCount && !indexBuffer) { + uint16_t* indices = nullptr; indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex); if (!indices) { SkDebugf("Could not allocate indices.\n"); @@ -742,12 +777,16 @@ void CustomMeshOp::onPrepareDraws(GrMeshDrawTarget* target) { } SkASSERT(voffset == fVertexCount); SkASSERT(ioffset == fIndexCount); + } else if (indexBuffer) { + SkASSERT(fMeshes.count() == 1); + SkASSERT(firstIndex % sizeof(uint16_t) == 0); + firstIndex /= sizeof(uint16_t); } SkASSERT(!fMesh); fMesh = target->allocMesh(); - if (indices) { + if (indexBuffer) { fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex,