[graphite] Support reserving and returning vertex data in DrawWriter

Cq-Include-Trybots: luci.skia.skia.primary:Test-Mac11-Clang-MacMini9.1-GPU-AppleM1-arm64-Release-All-Graphite,Test-Mac11-Clang-MacMini9.1-GPU-AppleM1-arm64-Debug-All-ASAN_Graphite,Build-Mac-Clang-arm64-Release-Graphite,Build-Mac-Clang-arm64-Release-iOS_Graphite,Build-Mac-Clang-arm64-Debug-iOS_Graphite,Build-Mac-Clang-arm64-Debug-Graphite_NoGpu,Build-Mac-Clang-arm64-Debug-Graphite,Build-Mac-Clang-arm64-Debug-ASAN_Graphite
Bug: skia:12703
Change-Id: I5f1221ff7a51ca8b5935f6f46dd5d5a364cfec45
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/498316
Reviewed-by: Christopher Dalton <csmartdalton@google.com>
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
This commit is contained in:
Michael Ludwig 2022-03-09 20:46:58 -05:00 committed by SkCQ
parent 07e0a0abd3
commit f4f8bbb577
5 changed files with 96 additions and 49 deletions

View File

@ -27,6 +27,18 @@ void* map_offset(BindBufferInfo binding) {
static_cast<ptrdiff_t>(binding.fOffset));
}
template <size_t BufferBlockSize>
size_t sufficient_block_size(size_t requiredBytes) {
// Always request a buffer at least 'requiredBytes', but keep them in multiples of
// 'BufferBlockSize' for improved reuse.
static constexpr size_t kMaxSize = std::numeric_limits<size_t>::max();
static constexpr size_t kMaxBlocks = kMaxSize / BufferBlockSize;
size_t blocks = (requiredBytes / BufferBlockSize) + 1;
size_t bufferSize = blocks > kMaxBlocks ? kMaxSize : (blocks * BufferBlockSize);
SkASSERT(requiredBytes < bufferSize);
return bufferSize;
}
} // anonymous namespace
DrawBufferManager::DrawBufferManager(ResourceProvider* resourceProvider,
@ -54,8 +66,8 @@ std::tuple<VertexWriter, BindBufferInfo> DrawBufferManager::getVertexWriter(size
}
if (!fCurrentVertexBuffer) {
SkASSERT(requiredBytes <= kVertexBufferSize);
fCurrentVertexBuffer = fResourceProvider->findOrCreateBuffer(kVertexBufferSize,
size_t bufferSize = sufficient_block_size<kVertexBufferSize>(requiredBytes);
fCurrentVertexBuffer = fResourceProvider->findOrCreateBuffer(bufferSize,
BufferType::kVertex,
PrioritizeGpuReads::kNo);
fVertexOffset = 0;
@ -70,6 +82,11 @@ std::tuple<VertexWriter, BindBufferInfo> DrawBufferManager::getVertexWriter(size
return {VertexWriter(map_offset(bindInfo), requiredBytes), bindInfo};
}
void DrawBufferManager::returnVertexBytes(size_t unusedBytes) {
SkASSERT(fVertexOffset >= unusedBytes);
fVertexOffset -= unusedBytes;
}
std::tuple<IndexWriter, BindBufferInfo> DrawBufferManager::getIndexWriter(size_t requiredBytes) {
if (!requiredBytes) {
return {IndexWriter(), BindBufferInfo()};
@ -80,8 +97,8 @@ std::tuple<IndexWriter, BindBufferInfo> DrawBufferManager::getIndexWriter(size_t
}
if (!fCurrentIndexBuffer) {
SkASSERT(requiredBytes <= kIndexBufferSize);
fCurrentIndexBuffer = fResourceProvider->findOrCreateBuffer(kIndexBufferSize,
size_t bufferSize = sufficient_block_size<kIndexBufferSize>(requiredBytes);
fCurrentIndexBuffer = fResourceProvider->findOrCreateBuffer(bufferSize,
BufferType::kIndex,
PrioritizeGpuReads::kNo);
fIndexOffset = 0;
@ -110,8 +127,8 @@ std::tuple<UniformWriter, BindBufferInfo> DrawBufferManager::getUniformWriter(
}
if (!fCurrentUniformBuffer) {
SkASSERT(requiredBytes <= kUniformBufferSize);
fCurrentUniformBuffer = fResourceProvider->findOrCreateBuffer(kUniformBufferSize,
size_t bufferSize = sufficient_block_size<kUniformBufferSize>(requiredBytes);
fCurrentUniformBuffer = fResourceProvider->findOrCreateBuffer(bufferSize,
BufferType::kUniform,
PrioritizeGpuReads::kNo);
fUniformOffset = 0;

View File

@ -29,6 +29,10 @@ public:
std::tuple<IndexWriter, BindBufferInfo> getIndexWriter(size_t requiredBytes);
std::tuple<UniformWriter, BindBufferInfo> getUniformWriter(size_t requiredBytes);
// Returns the last 'unusedBytes' from the last call to getVertexWriter(). Assumes that
// 'unusedBytes' is less than the 'requiredBytes' to the original allocation.
void returnVertexBytes(size_t unusedBytes);
// Finalizes all buffers and transfers ownership of them to the CommandBuffer.
void transferToCommandBuffer(CommandBuffer*);

View File

@ -109,28 +109,4 @@ void DrawWriter::flush() {
fPendingCount = 0;
}
VertexWriter DrawWriter::Appender::append(unsigned int count,
size_t stride,
BindBufferInfo& target) {
SkASSERT(&target == &fWriter.fInstances || &target == &fWriter.fVertices);
SkASSERT(this == fWriter.fAppender);
auto [writer, nextChunk] = fWriter.fManager->getVertexWriter(count * stride);
// Check if next chunk's data is contiguous with what's previously been appended
if (nextChunk.fBuffer != target.fBuffer ||
nextChunk.fOffset !=
target.fOffset + (fWriter.fPendingBase + fWriter.fPendingCount) * stride) {
// Alignment mismatch, or the old buffer filled up, so must update the bindings
fWriter.flush();
target = nextChunk;
fWriter.fPendingBase = 0;
fWriter.fPendingBufferBinds = true;
}
fWriter.fPendingCount += count;
return std::move(writer);
}
} // namespace skgpu

View File

@ -8,13 +8,12 @@
#ifndef skgpu_DrawWriter_DEFINED
#define skgpu_DrawWriter_DEFINED
#include "experimental/graphite/src/DrawBufferManager.h"
#include "experimental/graphite/src/DrawTypes.h"
#include "src/gpu/BufferWriter.h"
namespace skgpu {
class DrawBufferManager;
class DrawDispatcher; // Forward declaration, handles virtual dispatch of binds/draws
/**
@ -207,32 +206,80 @@ public:
// template-specific API to accumulate vertex/instance data.
class DrawWriter::Appender {
public:
Appender(DrawWriter& w) : fWriter(w) {
enum class Target { kVertices, kInstances };
Appender(DrawWriter& w, Target target)
: fDrawer(w)
, fTarget(target == Target::kVertices ? w.fVertices : w.fInstances)
, fStride(target == Target::kVertices ? w.fVertexStride : w.fInstanceStride)
, fReservedCount(0)
, fNextWriter() {
SkASSERT(fStride > 0);
SkASSERT(!w.fAppender);
SkDEBUGCODE(w.fAppender = this;)
}
~Appender() {
SkASSERT(fWriter.fAppender == this);
SkDEBUGCODE(fWriter.fAppender = nullptr;)
if (fReservedCount > 0) {
fDrawer.fManager->returnVertexBytes(fReservedCount * fStride);
}
SkASSERT(fDrawer.fAppender == this);
SkDEBUGCODE(fDrawer.fAppender = nullptr;)
}
protected:
DrawWriter& fWriter;
DrawWriter& fDrawer;
BindBufferInfo& fTarget;
size_t fStride;
VertexWriter append(unsigned int count, size_t stride, BindBufferInfo& target);
unsigned int fReservedCount; // in target stride units
VertexWriter fNextWriter; // writing to the target buffer binding
void reserve(unsigned int count) {
if (fReservedCount >= count) {
return;
} else if (fReservedCount > 0) {
// Have contiguous bytes that can't satisfy request, so return them in the event the
// DBM has additional contiguous bytes after the prior reserved range.
fDrawer.fManager->returnVertexBytes(fReservedCount * fStride);
}
fReservedCount = count;
// NOTE: Cannot bind tuple directly to fNextWriter, compilers don't produce the right
// move assignment.
auto [writer, reservedChunk] = fDrawer.fManager->getVertexWriter(count * fStride);
if (reservedChunk.fBuffer != fTarget.fBuffer ||
reservedChunk.fOffset !=
(fTarget.fOffset + (fDrawer.fPendingBase + fDrawer.fPendingCount) * fStride)) {
// Not contiguous, so flush and update binding to 'reservedChunk'
fDrawer.flush();
fTarget = reservedChunk;
fDrawer.fPendingBase = 0;
fDrawer.fPendingBufferBinds = true;
}
fNextWriter = std::move(writer);
}
VertexWriter append(unsigned int count) {
SkASSERT(count > 0);
this->reserve(count);
SkASSERT(fReservedCount >= count);
fReservedCount -= count;
fDrawer.fPendingCount += count;
return std::exchange(fNextWriter, fNextWriter.makeOffset(count * fStride));
}
};
class DrawWriter::Vertices : private DrawWriter::Appender {
public:
Vertices(DrawWriter& w) : Appender(w) {
SkASSERT(w.fVertexStride > 0);
Vertices(DrawWriter& w) : Appender(w, Target::kVertices) {
w.setTemplate(w.fVertices, {}, {}, 0);
}
VertexWriter append(unsigned int count) {
return this->Appender::append(count, fWriter.fVertexStride, fWriter.fVertices);
}
using Appender::reserve;
using Appender::append;
};
class DrawWriter::Instances : private DrawWriter::Appender {
@ -241,14 +288,12 @@ public:
BindBufferInfo vertices,
BindBufferInfo indices,
unsigned int vertexCount)
: Appender(w) {
SkASSERT(w.fInstanceStride > 0);
: Appender(w, Target::kInstances) {
w.setTemplate(vertices, indices, w.fInstances, vertexCount);
}
VertexWriter append(unsigned int count) {
return this->Appender::append(count, fWriter.fInstanceStride, fWriter.fInstances);
}
using Appender::reserve;
using Appender::append;
};
} // namespace skgpu

View File

@ -16,6 +16,7 @@
#include "src/core/SkUniformData.h"
#include "src/gpu/BufferWriter.h"
#include "src/gpu/tessellate/MiddleOutPolygonTriangulator.h"
#include "src/gpu/tessellate/PathTessellator.h"
namespace skgpu {
@ -154,7 +155,13 @@ public:
// TODO: Have Shape provide a path-like iterator so we don't actually have to convert non
// paths to SkPath just to iterate their pts/verbs
SkPath path = shape.asPath();
const int maxCombinedFanEdges =
PathTessellator::MaxCombinedFanEdgesInPathDrawList(path.countVerbs());
const int maxTrianglesInFans = std::max(maxCombinedFanEdges - 2, 0);
DrawWriter::Vertices verts{*writer};
verts.reserve(maxTrianglesInFans * 3);
for (PathMiddleOutFanIter it(path); !it.done();) {
for (auto [p0, p1, p2] : it.nextStack()) {
// TODO: PathMiddleOutFanIter should use SkV2 instead of SkPoint?
@ -162,8 +169,6 @@ public:
SkV4 devPoints[3];
localToDevice.mapPoints(p, devPoints, 3);
// TODO: Support reserving maxTrianglesInFans*3 vertices outside the loop, with
// automatic returns of unused verts.
verts.append(3) << devPoints[0].x << devPoints[0].y << devPoints[0].w // p0
<< devPoints[1].x << devPoints[1].y << devPoints[1].w // p1
<< devPoints[2].x << devPoints[2].y << devPoints[2].w; // p2