[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:
parent
07e0a0abd3
commit
f4f8bbb577
@ -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;
|
||||
|
@ -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*);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user