Add a writer class for indirect draw commands
This enforces write-only access to the mapped buffers, will enable chaining of indirect strokes, and gives us the ability to reorder the fields for Metal. Bug: chromium:1172543 Bug: skia:11291 Bug: skia:10419 Change-Id: I4449ff85dd0019f6d6d6781ede52bcf26dee8b02 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/367416 Reviewed-by: Greg Daniel <egdaniel@google.com> Commit-Queue: Chris Dalton <csmartdalton@google.com>
This commit is contained in:
parent
fc017c7c7e
commit
a6a3d05ffb
@ -92,6 +92,7 @@ skia_gpu_sources = [
|
||||
"$_src/gpu/GrDirectContextPriv.h",
|
||||
"$_src/gpu/GrDistanceFieldGenFromVector.cpp",
|
||||
"$_src/gpu/GrDistanceFieldGenFromVector.h",
|
||||
"$_src/gpu/GrDrawIndirectCommand.h",
|
||||
"$_src/gpu/GrDrawOpAtlas.cpp",
|
||||
"$_src/gpu/GrDrawOpAtlas.h",
|
||||
"$_src/gpu/GrDrawOpTest.cpp",
|
||||
|
@ -66,26 +66,6 @@ enum class GrPrimitiveRestart : bool {
|
||||
kYes = true
|
||||
};
|
||||
|
||||
struct GrDrawIndirectCommand {
|
||||
uint32_t fVertexCount;
|
||||
uint32_t fInstanceCount;
|
||||
uint32_t fBaseVertex;
|
||||
uint32_t fBaseInstance;
|
||||
};
|
||||
|
||||
static_assert(sizeof(GrDrawIndirectCommand) == 16, "GrDrawIndirectCommand must be tightly packed");
|
||||
|
||||
struct GrDrawIndexedIndirectCommand {
|
||||
uint32_t fIndexCount;
|
||||
uint32_t fInstanceCount;
|
||||
uint32_t fBaseIndex;
|
||||
int32_t fBaseVertex;
|
||||
uint32_t fBaseInstance;
|
||||
};
|
||||
|
||||
static_assert(sizeof(GrDrawIndexedIndirectCommand) == 20,
|
||||
"GrDrawIndexedIndirectCommand must be tightly packed");
|
||||
|
||||
/**
|
||||
* Should a created surface be texturable?
|
||||
*/
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "include/private/SkTArray.h"
|
||||
#include "include/private/SkTDArray.h"
|
||||
#include "src/gpu/GrCpuBuffer.h"
|
||||
#include "src/gpu/GrDrawIndirectCommand.h"
|
||||
#include "src/gpu/GrNonAtomicRef.h"
|
||||
|
||||
class GrGpu;
|
||||
@ -319,23 +320,23 @@ public:
|
||||
GrDrawIndirectBufferAllocPool(GrGpu* gpu, sk_sp<CpuBufferCache> cpuBufferCache)
|
||||
: GrBufferAllocPool(gpu, GrGpuBufferType::kDrawIndirect, std::move(cpuBufferCache)) {}
|
||||
|
||||
GrDrawIndirectCommand* makeSpace(int drawCount, sk_sp<const GrBuffer>* buffer, size_t* offset) {
|
||||
return static_cast<GrDrawIndirectCommand*>(this->GrBufferAllocPool::makeSpace(
|
||||
(size_t)drawCount * sizeof(GrDrawIndirectCommand), 4, buffer, offset));
|
||||
GrDrawIndirectWriter makeSpace(int drawCount, sk_sp<const GrBuffer>* buffer, size_t* offset) {
|
||||
return this->GrBufferAllocPool::makeSpace(drawCount * sizeof(GrDrawIndirectCommand), 4,
|
||||
buffer, offset);
|
||||
}
|
||||
|
||||
void putBack(int drawCount) {
|
||||
this->GrBufferAllocPool::putBack((size_t)drawCount * sizeof(GrDrawIndirectCommand));
|
||||
this->GrBufferAllocPool::putBack(drawCount * sizeof(GrDrawIndirectCommand));
|
||||
}
|
||||
|
||||
GrDrawIndexedIndirectCommand* makeIndexedSpace(int drawCount, sk_sp<const GrBuffer>* buffer,
|
||||
GrDrawIndexedIndirectWriter makeIndexedSpace(int drawCount, sk_sp<const GrBuffer>* buffer,
|
||||
size_t* offset) {
|
||||
return static_cast<GrDrawIndexedIndirectCommand*>(this->GrBufferAllocPool::makeSpace(
|
||||
(size_t)drawCount * sizeof(GrDrawIndexedIndirectCommand), 4, buffer, offset));
|
||||
return this->GrBufferAllocPool::makeSpace(
|
||||
drawCount * sizeof(GrDrawIndexedIndirectCommand), 4, buffer, offset);
|
||||
}
|
||||
|
||||
void putBackIndexed(int drawCount) {
|
||||
this->GrBufferAllocPool::putBack((size_t)drawCount * sizeof(GrDrawIndexedIndirectCommand));
|
||||
this->GrBufferAllocPool::putBack(drawCount * sizeof(GrDrawIndexedIndirectCommand));
|
||||
}
|
||||
|
||||
using GrBufferAllocPool::unmap;
|
||||
|
79
src/gpu/GrDrawIndirectCommand.h
Normal file
79
src/gpu/GrDrawIndirectCommand.h
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2021 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef GrDrawIndirectCommand_DEFINED
|
||||
#define GrDrawIndirectCommand_DEFINED
|
||||
|
||||
#include "src/gpu/GrCaps.h"
|
||||
#include <array>
|
||||
|
||||
// Draw commands on the GPU are simple tuples of uint32_t. The ordering is backend-specific.
|
||||
using GrDrawIndirectCommand = std::array<uint32_t, 4>;
|
||||
using GrDrawIndexedIndirectCommand = std::array<uint32_t, 5>;
|
||||
|
||||
// Helper for writing commands to an indirect draw buffer. Usage:
|
||||
//
|
||||
// GrDrawIndirectWriter indirectWriter = target->makeDrawIndirectSpace(...);
|
||||
// indirectWriter.write(...);
|
||||
// indirectWriter.write(...);
|
||||
struct GrDrawIndirectWriter {
|
||||
public:
|
||||
GrDrawIndirectWriter() = default;
|
||||
GrDrawIndirectWriter(void* data) : fData(static_cast<GrDrawIndirectCommand*>(data)) {}
|
||||
GrDrawIndirectWriter(const GrDrawIndirectWriter&) = delete;
|
||||
GrDrawIndirectWriter(GrDrawIndirectWriter&& that) { *this = std::move(that); }
|
||||
|
||||
GrDrawIndirectWriter& operator=(const GrDrawIndirectWriter&) = delete;
|
||||
GrDrawIndirectWriter& operator=(GrDrawIndirectWriter&& that) {
|
||||
fData = that.fData;
|
||||
that.fData = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool isValid() const { return fData != nullptr; }
|
||||
|
||||
inline void write(uint32_t instanceCount, uint32_t baseInstance, uint32_t vertexCount,
|
||||
uint32_t baseVertex, const GrCaps&) {
|
||||
*fData++ = {vertexCount, instanceCount, baseVertex, baseInstance};
|
||||
}
|
||||
|
||||
private:
|
||||
GrDrawIndirectCommand* fData;
|
||||
};
|
||||
|
||||
// Helper for writing commands to an indexed indirect draw buffer. Usage:
|
||||
//
|
||||
// GrDrawIndexedIndirectWriter indirectWriter = target->makeDrawIndexedIndirectSpace(...);
|
||||
// indirectWriter.writeIndexed(...);
|
||||
// indirectWriter.writeIndexed(...);
|
||||
struct GrDrawIndexedIndirectWriter {
|
||||
public:
|
||||
GrDrawIndexedIndirectWriter() = default;
|
||||
GrDrawIndexedIndirectWriter(void* data)
|
||||
: fData(static_cast<GrDrawIndexedIndirectCommand*>(data)) {}
|
||||
GrDrawIndexedIndirectWriter(const GrDrawIndexedIndirectWriter&) = delete;
|
||||
GrDrawIndexedIndirectWriter(GrDrawIndexedIndirectWriter&& that) { *this = std::move(that); }
|
||||
|
||||
GrDrawIndexedIndirectWriter& operator=(const GrDrawIndexedIndirectWriter&) = delete;
|
||||
GrDrawIndexedIndirectWriter& operator=(GrDrawIndexedIndirectWriter&& that) {
|
||||
fData = that.fData;
|
||||
that.fData = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool isValid() const { return fData != nullptr; }
|
||||
|
||||
inline void writeIndexed(uint32_t indexCount, uint32_t baseIndex, uint32_t instanceCount,
|
||||
uint32_t baseInstance, uint32_t baseVertex, const GrCaps&) {
|
||||
*fData++ = {indexCount, instanceCount, baseIndex, baseVertex, baseInstance};
|
||||
}
|
||||
|
||||
private:
|
||||
GrDrawIndexedIndirectCommand* fData;
|
||||
};
|
||||
|
||||
#endif
|
@ -134,12 +134,13 @@ public:
|
||||
uint16_t* makeIndexSpaceAtLeast(int minIndexCount, int fallbackIndexCount,
|
||||
sk_sp<const GrBuffer>*, int* startIndex,
|
||||
int* actualIndexCount) final;
|
||||
GrDrawIndirectCommand* makeDrawIndirectSpace(int drawCount, sk_sp<const GrBuffer>* buffer,
|
||||
GrDrawIndirectWriter makeDrawIndirectSpace(int drawCount, sk_sp<const GrBuffer>* buffer,
|
||||
size_t* offset) override {
|
||||
return fDrawIndirectPool.makeSpace(drawCount, buffer, offset);
|
||||
}
|
||||
GrDrawIndexedIndirectCommand* makeDrawIndexedIndirectSpace(
|
||||
int drawCount, sk_sp<const GrBuffer>* buffer, size_t* offset) override {
|
||||
GrDrawIndexedIndirectWriter makeDrawIndexedIndirectSpace(int drawCount,
|
||||
sk_sp<const GrBuffer>* buffer,
|
||||
size_t* offset) override {
|
||||
return fDrawIndirectPool.makeIndexedSpace(drawCount, buffer, offset);
|
||||
}
|
||||
void putBackIndices(int indexCount) final;
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "include/core/SkRect.h"
|
||||
#include "src/gpu/GrCaps.h"
|
||||
#include "src/gpu/GrCpuBuffer.h"
|
||||
#include "src/gpu/GrDrawIndirectCommand.h"
|
||||
#include "src/gpu/GrGpu.h"
|
||||
#include "src/gpu/GrPrimitiveProcessor.h"
|
||||
#include "src/gpu/GrProgramInfo.h"
|
||||
@ -284,13 +285,13 @@ void GrOpsRenderPass::drawIndirect(const GrBuffer* drawIndirectBuffer, size_t bu
|
||||
if (!this->gpu()->caps()->nativeDrawIndirectSupport()) {
|
||||
// Polyfill indirect draws with looping instanced calls.
|
||||
SkASSERT(drawIndirectBuffer->isCpuBuffer());
|
||||
auto cpuIndirectBuffer = static_cast<const GrCpuBuffer*>(drawIndirectBuffer);
|
||||
auto cmd = reinterpret_cast<const GrDrawIndirectCommand*>(
|
||||
auto* cpuIndirectBuffer = static_cast<const GrCpuBuffer*>(drawIndirectBuffer);
|
||||
auto* cmds = reinterpret_cast<const GrDrawIndirectCommand*>(
|
||||
cpuIndirectBuffer->data() + bufferOffset);
|
||||
auto end = cmd + drawCount;
|
||||
for (; cmd != end; ++cmd) {
|
||||
this->onDrawInstanced(cmd->fInstanceCount, cmd->fBaseInstance, cmd->fVertexCount,
|
||||
cmd->fBaseVertex);
|
||||
for (int i = 0; i < drawCount; ++i) {
|
||||
// TODO: SkASSERT(caps.drawIndirectSignature() == standard);
|
||||
auto [vertexCount, instanceCount, baseVertex, baseInstance] = cmds[i];
|
||||
this->onDrawInstanced(instanceCount, baseInstance, vertexCount, baseVertex);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -312,13 +313,14 @@ void GrOpsRenderPass::drawIndexedIndirect(const GrBuffer* drawIndirectBuffer, si
|
||||
this->gpu()->caps()->nativeDrawIndexedIndirectIsBroken()) {
|
||||
// Polyfill indexedIndirect draws with looping indexedInstanced calls.
|
||||
SkASSERT(drawIndirectBuffer->isCpuBuffer());
|
||||
auto cpuIndirectBuffer = static_cast<const GrCpuBuffer*>(drawIndirectBuffer);
|
||||
auto cmd = reinterpret_cast<const GrDrawIndexedIndirectCommand*>(
|
||||
auto* cpuIndirectBuffer = static_cast<const GrCpuBuffer*>(drawIndirectBuffer);
|
||||
auto* cmds = reinterpret_cast<const GrDrawIndexedIndirectCommand*>(
|
||||
cpuIndirectBuffer->data() + bufferOffset);
|
||||
auto end = cmd + drawCount;
|
||||
for (; cmd != end; ++cmd) {
|
||||
this->onDrawIndexedInstanced(cmd->fIndexCount, cmd->fBaseIndex, cmd->fInstanceCount,
|
||||
cmd->fBaseInstance, cmd->fBaseVertex);
|
||||
for (int i = 0; i < drawCount; ++i) {
|
||||
// TODO: SkASSERT(caps.drawIndirectSignature() == standard);
|
||||
auto [indexCount, instanceCount, baseIndex, baseVertex, baseInstance] = cmds[i];
|
||||
this->onDrawIndexedInstanced(indexCount, baseIndex, instanceCount, baseInstance,
|
||||
baseVertex);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -286,11 +286,12 @@ void GrGLOpsRenderPass::multiDrawArraysANGLEOrWebGL(const GrBuffer* drawIndirect
|
||||
while (drawCount) {
|
||||
int countInBatch = std::min(drawCount, kMaxDrawCountPerBatch);
|
||||
for (int i = 0; i < countInBatch; ++i) {
|
||||
const auto& cmd = cmds[i];
|
||||
fFirsts[i] = cmd.fBaseVertex;
|
||||
fCounts[i] = cmd.fVertexCount;
|
||||
fInstanceCounts[i] = cmd.fInstanceCount;
|
||||
fBaseInstances[i] = cmd.fBaseInstance;
|
||||
// TODO: SkASSERT(caps.drawIndirectSignature() == standard);
|
||||
auto [vertexCount, instanceCount, baseVertex, baseInstance] = cmds[i];
|
||||
fFirsts[i] = baseVertex;
|
||||
fCounts[i] = vertexCount;
|
||||
fInstanceCounts[i] = instanceCount;
|
||||
fBaseInstances[i] = baseInstance;
|
||||
}
|
||||
if (countInBatch == 1) {
|
||||
GL_CALL(DrawArraysInstancedBaseInstance(glPrimType, fFirsts[0], fCounts[0],
|
||||
@ -359,12 +360,13 @@ void GrGLOpsRenderPass::multiDrawElementsANGLEOrWebGL(const GrBuffer* drawIndire
|
||||
while (drawCount) {
|
||||
int countInBatch = std::min(drawCount, kMaxDrawCountPerBatch);
|
||||
for (int i = 0; i < countInBatch; ++i) {
|
||||
const auto& cmd = cmds[i];
|
||||
fCounts[i] = cmd.fIndexCount;
|
||||
fIndices[i] = this->offsetForBaseIndex(cmd.fBaseIndex);
|
||||
fInstanceCounts[i] = cmd.fInstanceCount;
|
||||
fBaseVertices[i] = cmd.fBaseVertex;
|
||||
fBaseInstances[i] = cmd.fBaseInstance;
|
||||
// TODO: SkASSERT(caps.drawIndirectSignature() == standard);
|
||||
auto [indexCount, instanceCount, baseIndex, baseVertex, baseInstance] = cmds[i];
|
||||
fCounts[i] = indexCount;
|
||||
fIndices[i] = this->offsetForBaseIndex(baseIndex);
|
||||
fInstanceCounts[i] = instanceCount;
|
||||
fBaseVertices[i] = baseVertex;
|
||||
fBaseInstances[i] = baseInstance;
|
||||
}
|
||||
if (countInBatch == 1) {
|
||||
GL_CALL(DrawElementsInstancedBaseVertexBaseInstance(glPrimType, fCounts[0],
|
||||
|
@ -56,32 +56,35 @@ public:
|
||||
return fStaticVertexData;
|
||||
}
|
||||
|
||||
GrDrawIndirectCommand* makeDrawIndirectSpace(int drawCount, sk_sp<const GrBuffer>* buffer,
|
||||
GrDrawIndirectWriter makeDrawIndirectSpace(int drawCount, sk_sp<const GrBuffer>* buffer,
|
||||
size_t* offsetInBytes) override {
|
||||
int staticBufferCount = (int)SK_ARRAY_COUNT(fStaticDrawIndirectData);
|
||||
if (drawCount > staticBufferCount) {
|
||||
SK_ABORT("FATAL: wanted %i static drawIndirect elements; only have %i.\n",
|
||||
drawCount, staticBufferCount);
|
||||
if (sizeof(GrDrawIndirectCommand) * drawCount > sizeof(fStaticIndirectData)) {
|
||||
SK_ABORT("FATAL: wanted %zu bytes of static indirect data; only have %zu.\n",
|
||||
sizeof(GrDrawIndirectCommand) * drawCount, sizeof(fStaticIndirectData));
|
||||
}
|
||||
*offsetInBytes = 0;
|
||||
return fStaticDrawIndirectData;
|
||||
return fStaticIndirectData;
|
||||
}
|
||||
|
||||
void putBackIndirectDraws(int count) override { /* no-op */ }
|
||||
|
||||
GrDrawIndexedIndirectCommand* makeDrawIndexedIndirectSpace(
|
||||
int drawCount, sk_sp<const GrBuffer>* buffer, size_t* offsetInBytes) override {
|
||||
int staticBufferCount = (int)SK_ARRAY_COUNT(fStaticDrawIndexedIndirectData);
|
||||
if (drawCount > staticBufferCount) {
|
||||
SK_ABORT("FATAL: wanted %i static drawIndexedIndirect elements; only have %i.\n",
|
||||
drawCount, staticBufferCount);
|
||||
GrDrawIndexedIndirectWriter makeDrawIndexedIndirectSpace(int drawCount,
|
||||
sk_sp<const GrBuffer>* buffer,
|
||||
size_t* offsetInBytes) override {
|
||||
if (sizeof(GrDrawIndexedIndirectCommand) * drawCount > sizeof(fStaticIndirectData)) {
|
||||
SK_ABORT("FATAL: wanted %zu bytes of static indirect data; only have %zu.\n",
|
||||
sizeof(GrDrawIndexedIndirectCommand) * drawCount, sizeof(fStaticIndirectData));
|
||||
}
|
||||
*offsetInBytes = 0;
|
||||
return fStaticDrawIndexedIndirectData;
|
||||
return fStaticIndirectData;
|
||||
}
|
||||
|
||||
void putBackIndexedIndirectDraws(int count) override { /* no-op */ }
|
||||
|
||||
// Call these methods to see what got written after the previous call to make*Space.
|
||||
const void* peekStaticVertexData() const { return fStaticVertexData; }
|
||||
const void* peekStaticIndirectData() const { return fStaticIndirectData; }
|
||||
|
||||
#define UNIMPL(...) __VA_ARGS__ override { SK_ABORT("unimplemented."); }
|
||||
UNIMPL(void recordDraw(const GrGeometryProcessor*, const GrSimpleMesh[], int,
|
||||
const GrSurfaceProxy* const[], GrPrimitiveType))
|
||||
@ -100,8 +103,7 @@ public:
|
||||
private:
|
||||
sk_sp<GrDirectContext> fMockContext;
|
||||
char fStaticVertexData[6 * 1024 * 1024];
|
||||
GrDrawIndirectCommand fStaticDrawIndirectData[32];
|
||||
GrDrawIndexedIndirectCommand fStaticDrawIndexedIndirectData[32];
|
||||
char fStaticIndirectData[sizeof(GrDrawIndexedIndirectCommand) * 32];
|
||||
SkSTArenaAllocWithReset<1024 * 1024> fAllocator;
|
||||
GrXferProcessor::DstProxyView fDstProxyView;
|
||||
};
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
#include "src/core/SkArenaAlloc.h"
|
||||
#include "src/gpu/GrAppliedClip.h"
|
||||
#include "src/gpu/GrDrawIndirectCommand.h"
|
||||
#include "src/gpu/GrGeometryProcessor.h"
|
||||
#include "src/gpu/GrSimpleMesh.h"
|
||||
#include "src/gpu/ops/GrDrawOp.h"
|
||||
@ -199,16 +200,16 @@ public:
|
||||
* Makes space for elements in a draw-indirect buffer. Upon success, the returned pointer is a
|
||||
* CPU mapping where the data should be written.
|
||||
*/
|
||||
virtual GrDrawIndirectCommand* makeDrawIndirectSpace(int drawCount,
|
||||
sk_sp<const GrBuffer>* buffer,
|
||||
virtual GrDrawIndirectWriter makeDrawIndirectSpace(int drawCount, sk_sp<const GrBuffer>* buffer,
|
||||
size_t* offsetInBytes) = 0;
|
||||
|
||||
/**
|
||||
* Makes space for elements in a draw-indexed-indirect buffer. Upon success, the returned
|
||||
* pointer is a CPU mapping where the data should be written.
|
||||
*/
|
||||
virtual GrDrawIndexedIndirectCommand* makeDrawIndexedIndirectSpace(
|
||||
int drawCount, sk_sp<const GrBuffer>* buffer, size_t* offsetInBytes) = 0;
|
||||
virtual GrDrawIndexedIndirectWriter makeDrawIndexedIndirectSpace(int drawCount,
|
||||
sk_sp<const GrBuffer>*,
|
||||
size_t* offsetInBytes) = 0;
|
||||
|
||||
/** Helpers for ops which over-allocate and then return excess data to the pool. */
|
||||
virtual void putBackIndices(int indices) = 0;
|
||||
|
@ -49,9 +49,10 @@ GrPathIndirectTessellator::GrPathIndirectTessellator(const SkMatrix& viewMatrix,
|
||||
void GrPathIndirectTessellator::prepare(GrMeshDrawOp::Target* target, const SkMatrix& viewMatrix,
|
||||
const SkPath& path,
|
||||
const BreadcrumbTriangleList* breadcrumbTriangleList) {
|
||||
const GrCaps& caps = target->caps();
|
||||
SkASSERT(fTotalInstanceCount == 0);
|
||||
SkASSERT(fIndirectDrawCount == 0);
|
||||
SkASSERT(target->caps().drawInstancedSupport());
|
||||
SkASSERT(caps.drawInstancedSupport());
|
||||
|
||||
int instanceLockCount = fOuterCurveInstanceCount;
|
||||
if (fDrawInnerFan) {
|
||||
@ -110,9 +111,9 @@ void GrPathIndirectTessellator::prepare(GrMeshDrawOp::Target* target, const SkMa
|
||||
// possible resolve level (kMaxResolveLevel; resolveLevel=0 never has any instances), plus one
|
||||
// more for the optional inner fan triangles.
|
||||
int indirectLockCnt = kMaxResolveLevel + 1;
|
||||
GrDrawIndexedIndirectCommand* indirectData = target->makeDrawIndexedIndirectSpace(
|
||||
GrDrawIndexedIndirectWriter indirectWriter = target->makeDrawIndexedIndirectSpace(
|
||||
indirectLockCnt, &fIndirectDrawBuffer, &fIndirectDrawOffset);
|
||||
if (!indirectData) {
|
||||
if (!indirectWriter.isValid()) {
|
||||
SkASSERT(!fIndirectDrawBuffer);
|
||||
return;
|
||||
}
|
||||
@ -126,8 +127,10 @@ void GrPathIndirectTessellator::prepare(GrMeshDrawOp::Target* target, const SkMa
|
||||
// at the beginning of the instance buffer. Add a special-case indirect draw here that will
|
||||
// emit the triangles [P0, P1, P2] from these 4-point instances.
|
||||
SkASSERT(fIndirectDrawCount < indirectLockCnt);
|
||||
indirectData[fIndirectDrawCount++] = GrMiddleOutCubicShader::MakeDrawTrianglesIndirectCmd(
|
||||
numTrianglesAtBeginningOfData, fBaseInstance);
|
||||
GrMiddleOutCubicShader::WriteDrawTrianglesIndirectCmd(&indirectWriter,
|
||||
numTrianglesAtBeginningOfData,
|
||||
fBaseInstance, caps);
|
||||
++fIndirectDrawCount;
|
||||
runningInstanceCount = numTrianglesAtBeginningOfData;
|
||||
}
|
||||
SkASSERT(fResolveLevelCounts[0] == 0);
|
||||
@ -139,8 +142,11 @@ void GrPathIndirectTessellator::prepare(GrMeshDrawOp::Target* target, const SkMa
|
||||
}
|
||||
instanceLocations[resolveLevel] = instanceData + runningInstanceCount * 4;
|
||||
SkASSERT(fIndirectDrawCount < indirectLockCnt);
|
||||
indirectData[fIndirectDrawCount++] = GrMiddleOutCubicShader::MakeDrawCubicsIndirectCmd(
|
||||
resolveLevel, instanceCountAtCurrLevel, fBaseInstance + runningInstanceCount);
|
||||
GrMiddleOutCubicShader::WriteDrawCubicsIndirectCmd(&indirectWriter, resolveLevel,
|
||||
instanceCountAtCurrLevel,
|
||||
fBaseInstance + runningInstanceCount,
|
||||
caps);
|
||||
++fIndirectDrawCount;
|
||||
runningInstanceCount += instanceCountAtCurrLevel;
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
#ifndef GrStencilPathShader_DEFINED
|
||||
#define GrStencilPathShader_DEFINED
|
||||
|
||||
#include "src/gpu/GrDrawIndirectCommand.h"
|
||||
#include "src/gpu/tessellate/GrPathShader.h"
|
||||
#include "src/gpu/tessellate/GrTessellationPathRenderer.h"
|
||||
|
||||
@ -165,25 +166,26 @@ public:
|
||||
|
||||
// Configures an indirect draw to render cubic instances with 2^resolveLevel evenly-spaced (in
|
||||
// the parametric sense) line segments.
|
||||
static GrDrawIndexedIndirectCommand MakeDrawCubicsIndirectCmd(int resolveLevel,
|
||||
uint32_t instanceCount,
|
||||
uint32_t baseInstance) {
|
||||
static void WriteDrawCubicsIndirectCmd(GrDrawIndexedIndirectWriter* indirectWriter,
|
||||
int resolveLevel, uint32_t instanceCount,
|
||||
uint32_t baseInstance, const GrCaps& caps) {
|
||||
SkASSERT(resolveLevel > 0 && resolveLevel <= GrTessellationPathRenderer::kMaxResolveLevel);
|
||||
// Starting at baseIndex=3, the index buffer triangulates a cubic with 2^kMaxResolveLevel
|
||||
// line segments. Each index value corresponds to a parametric T value on the curve. Since
|
||||
// the triangles are arranged in "middle-out" order, we can conveniently control the
|
||||
// resolveLevel by changing only the indexCount.
|
||||
uint32_t indexCount = NumVerticesAtResolveLevel(resolveLevel);
|
||||
return {indexCount, instanceCount, 3, 0, baseInstance};
|
||||
indirectWriter->writeIndexed(indexCount, 3, instanceCount, baseInstance, 0, caps);
|
||||
}
|
||||
|
||||
// For performance reasons we can often express triangles as an indirect cubic draw and sneak
|
||||
// them in alongside the other indirect draws. This method configures an indirect draw to emit
|
||||
// the triangle [P0, P1, P2] from a 4-point instance.
|
||||
static GrDrawIndexedIndirectCommand MakeDrawTrianglesIndirectCmd(uint32_t instanceCount,
|
||||
uint32_t baseInstance) {
|
||||
static void WriteDrawTrianglesIndirectCmd(GrDrawIndexedIndirectWriter* indirectWriter,
|
||||
uint32_t instanceCount, uint32_t baseInstance,
|
||||
const GrCaps& caps) {
|
||||
// Indices 0,1,2 have special index values that emit points P0, P1, and P2 respectively.
|
||||
return {3, instanceCount, 0, 0, baseInstance};
|
||||
indirectWriter->writeIndexed(3, 0, instanceCount, baseInstance, 0, caps);
|
||||
}
|
||||
|
||||
// Returns the index buffer that should be bound when drawing with this shader.
|
||||
|
@ -599,11 +599,14 @@ void GrStrokeIndirectTessellator::prepare(GrMeshDrawOp::Target* target, const Sk
|
||||
return;
|
||||
}
|
||||
|
||||
const GrCaps& caps = target->caps();
|
||||
|
||||
// Allocate enough indirect commands for every resolve level. We will putBack the unused ones
|
||||
// at the end.
|
||||
GrDrawIndirectCommand* drawIndirectData = target->makeDrawIndirectSpace(
|
||||
kMaxResolveLevel + 1, &fDrawIndirectBuffer, &fDrawIndirectOffset);
|
||||
if (!drawIndirectData) {
|
||||
GrDrawIndirectWriter indirectWriter = target->makeDrawIndirectSpace(kMaxResolveLevel + 1,
|
||||
&fDrawIndirectBuffer,
|
||||
&fDrawIndirectOffset);
|
||||
if (!indirectWriter.isValid()) {
|
||||
SkASSERT(!fDrawIndirectBuffer);
|
||||
return;
|
||||
}
|
||||
@ -627,11 +630,9 @@ void GrStrokeIndirectTessellator::prepare(GrMeshDrawOp::Target* target, const Sk
|
||||
for (int i = 0; i <= kMaxResolveLevel; ++i) {
|
||||
if (fResolveLevelCounts[i]) {
|
||||
int numEdges = numExtraEdgesInJoin + num_edges_in_resolve_level(i);
|
||||
auto& cmd = drawIndirectData[fDrawIndirectCount++];
|
||||
cmd.fVertexCount = numEdges * 2;
|
||||
cmd.fInstanceCount = fResolveLevelCounts[i];
|
||||
cmd.fBaseVertex = 0;
|
||||
cmd.fBaseInstance = baseInstance + currentInstanceIdx;
|
||||
indirectWriter.write(fResolveLevelCounts[i], baseInstance + currentInstanceIdx,
|
||||
numEdges * 2, 0, caps);
|
||||
++fDrawIndirectCount;
|
||||
numEdgesPerResolveLevel[i] = numEdges;
|
||||
nextInstanceLocations[i] = instanceData + currentInstanceIdx;
|
||||
#ifdef SK_DEBUG
|
||||
|
@ -61,9 +61,9 @@ private:
|
||||
|
||||
#if GR_TEST_UTILS
|
||||
public:
|
||||
void verifyResolveLevels(skiatest::Reporter*, GrMeshDrawOp::Target*, const SkMatrix&,
|
||||
void verifyResolveLevels(skiatest::Reporter*, class GrMockOpTarget*, const SkMatrix&,
|
||||
const SkPath&, const SkStrokeRec&);
|
||||
void verifyBuffers(skiatest::Reporter*, GrMeshDrawOp::Target*, const SkMatrix&,
|
||||
void verifyBuffers(skiatest::Reporter*, class GrMockOpTarget*, const SkMatrix&,
|
||||
const SkStrokeRec&);
|
||||
class Benchmark;
|
||||
#endif
|
||||
|
@ -324,8 +324,8 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrMeshTest, reporter, ctxInfo) {
|
||||
-1,-1, 0,0, 0,1, 1,0, 1,1, -1,-1, 0,0, 1,0, 0,1, 1,1});
|
||||
VALIDATE(helper->fVertBuffer);
|
||||
|
||||
GrDrawIndirectCommand* drawIndirect = nullptr;
|
||||
GrDrawIndexedIndirectCommand* drawIndexedIndirect = nullptr;
|
||||
GrDrawIndirectWriter indirectWriter;
|
||||
GrDrawIndexedIndirectWriter indexedIndirectWriter;
|
||||
if (indexed) {
|
||||
// Make helper->fDrawIndirectBufferOffset nonzero.
|
||||
sk_sp<const GrBuffer> dummyBuff;
|
||||
@ -333,7 +333,7 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrMeshTest, reporter, ctxInfo) {
|
||||
// Make a superfluous call to makeDrawIndirectSpace in order to test
|
||||
// "offsetInBytes!=0" for the actual call to makeDrawIndexedIndirectSpace.
|
||||
helper->target()->makeDrawIndirectSpace(29, &dummyBuff, &dummyOffset);
|
||||
drawIndexedIndirect = helper->target()->makeDrawIndexedIndirectSpace(
|
||||
indexedIndirectWriter = helper->target()->makeDrawIndexedIndirectSpace(
|
||||
kBoxCountY, &helper->fDrawIndirectBuffer,
|
||||
&helper->fDrawIndirectBufferOffset);
|
||||
} else {
|
||||
@ -342,8 +342,9 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrMeshTest, reporter, ctxInfo) {
|
||||
size_t dummyOffset;
|
||||
// Make a superfluous call to makeDrawIndexedIndirectSpace in order to test
|
||||
// "offsetInBytes!=0" for the actual call to makeDrawIndirectSpace.
|
||||
helper->target()->makeDrawIndexedIndirectSpace(7, &dummyBuff, &dummyOffset);
|
||||
drawIndirect = helper->target()->makeDrawIndirectSpace(
|
||||
helper->target()->makeDrawIndexedIndirectSpace(7, &dummyBuff,
|
||||
&dummyOffset);
|
||||
indirectWriter = helper->target()->makeDrawIndirectSpace(
|
||||
kBoxCountY, &helper->fDrawIndirectBuffer,
|
||||
&helper->fDrawIndirectBufferOffset);
|
||||
}
|
||||
@ -353,18 +354,12 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(GrMeshTest, reporter, ctxInfo) {
|
||||
int baseVertex = (y % 2) ? 1 : 6;
|
||||
if (indexed) {
|
||||
int baseIndex = 1 + y * 6;
|
||||
drawIndexedIndirect->fIndexCount = 6;
|
||||
drawIndexedIndirect->fBaseIndex = baseIndex;
|
||||
drawIndexedIndirect->fInstanceCount = kBoxCountX;
|
||||
drawIndexedIndirect->fBaseInstance = y * kBoxCountX;
|
||||
drawIndexedIndirect->fBaseVertex = baseVertex;
|
||||
++drawIndexedIndirect;
|
||||
indexedIndirectWriter.writeIndexed(6, baseIndex, kBoxCountX,
|
||||
y * kBoxCountX, baseVertex,
|
||||
*dContext->priv().caps());
|
||||
} else {
|
||||
drawIndirect->fInstanceCount = kBoxCountX;
|
||||
drawIndirect->fBaseInstance = y * kBoxCountX;
|
||||
drawIndirect->fVertexCount = 4;
|
||||
drawIndirect->fBaseVertex = baseVertex;
|
||||
++drawIndirect;
|
||||
indirectWriter.write(kBoxCountX, y * kBoxCountX, 4, baseVertex,
|
||||
*dContext->priv().caps());
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -32,7 +32,7 @@ static sk_sp<GrDirectContext> make_mock_context() {
|
||||
return GrDirectContext::MakeMock(&mockOptions, ctxOptions);
|
||||
}
|
||||
|
||||
static void test_stroke(skiatest::Reporter* r, GrDirectContext* ctx, GrMeshDrawOp::Target* target,
|
||||
static void test_stroke(skiatest::Reporter* r, GrDirectContext* ctx, GrMockOpTarget* target,
|
||||
const SkPath& path, SkRandom& rand) {
|
||||
SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
|
||||
stroke.setStrokeStyle(.1f);
|
||||
@ -263,7 +263,7 @@ static float test_tolerance(SkPaint::Join joinType) {
|
||||
}
|
||||
|
||||
void GrStrokeIndirectTessellator::verifyResolveLevels(skiatest::Reporter* r,
|
||||
GrMeshDrawOp::Target* target,
|
||||
GrMockOpTarget* target,
|
||||
const SkMatrix& viewMatrix,
|
||||
const SkPath& path,
|
||||
const SkStrokeRec& stroke) {
|
||||
@ -421,29 +421,27 @@ void GrStrokeIndirectTessellator::verifyResolveLevels(skiatest::Reporter* r,
|
||||
SkASSERT(nextResolveLevel == fResolveLevels + fResolveLevelArrayCount);
|
||||
}
|
||||
|
||||
void GrStrokeIndirectTessellator::verifyBuffers(skiatest::Reporter* r,
|
||||
GrMeshDrawOp::Target* target,
|
||||
void GrStrokeIndirectTessellator::verifyBuffers(skiatest::Reporter* r, GrMockOpTarget* target,
|
||||
const SkMatrix& viewMatrix,
|
||||
const SkStrokeRec& stroke) {
|
||||
// Make sure the resolve level we assigned to each instance agrees with the actual data.
|
||||
using IndirectInstance = GrStrokeTessellateShader::IndirectInstance;
|
||||
auto instance = static_cast<const IndirectInstance*>(target->peekStaticVertexData());
|
||||
auto* indirect = static_cast<const GrDrawIndirectCommand*>(target->peekStaticIndirectData());
|
||||
GrStrokeTessellateShader::Tolerances tolerances(viewMatrix.getMaxScale(), stroke.getWidth());
|
||||
float tolerance = test_tolerance(stroke.getJoin());
|
||||
// Make sure the resolve level we assign to each instance agrees with the actual data.
|
||||
// GrMockOpTarget returns the same pointers every time.
|
||||
int _;
|
||||
auto instance = (const IndirectInstance*)target->makeVertexSpace(0, 0, nullptr, &_);
|
||||
size_t __;
|
||||
auto indirect = target->makeDrawIndirectSpace(0, nullptr, &__);
|
||||
for (int i = 0; i < fDrawIndirectCount; ++i) {
|
||||
// TODO: SkASSERT(caps.drawIndirectCmdSignature() == standard);
|
||||
auto [vertexCount, instanceCount, baseVertex, baseInstance] = *indirect++;
|
||||
int numExtraEdgesInJoin = (stroke.getJoin() == SkPaint::kMiter_Join) ? 4 : 3;
|
||||
int numStrokeEdges = indirect->fVertexCount/2 - numExtraEdgesInJoin;
|
||||
int numStrokeEdges = vertexCount/2 - numExtraEdgesInJoin;
|
||||
int numSegments = numStrokeEdges - 1;
|
||||
bool isPow2 = !(numSegments & (numSegments - 1));
|
||||
REPORTER_ASSERT(r, isPow2);
|
||||
int resolveLevel = sk_float_nextlog2(numSegments);
|
||||
REPORTER_ASSERT(r, 1 << resolveLevel == numSegments);
|
||||
for (unsigned j = 0; j < indirect->fInstanceCount; ++j) {
|
||||
SkASSERT(fabsf(instance->fNumTotalEdges) == indirect->fVertexCount/2);
|
||||
for (unsigned j = 0; j < instanceCount; ++j) {
|
||||
SkASSERT(fabsf(instance->fNumTotalEdges) == vertexCount/2);
|
||||
const SkPoint* p = instance->fPts.data();
|
||||
float numParametricSegments = GrWangsFormula::cubic(
|
||||
tolerances.fParametricIntolerance, p);
|
||||
@ -476,6 +474,5 @@ void GrStrokeIndirectTessellator::verifyBuffers(skiatest::Reporter* r,
|
||||
}
|
||||
++instance;
|
||||
}
|
||||
++indirect;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user