Adding attribute types to SkVertices

Adds structure to the per-vertex (now custom) data.
Attributes currently just have a type, but the next
step is to augment that with semantic flags to handle
transformation logic in the vertex shader.

Added unit tests and GMs that exercise attributes of
varying float counts, as well as normalized bytes.

Bug: skia:9984
Change-Id: I02402d40b66a6e15b39f71125004efb98bc06295
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/280338
Reviewed-by: Mike Reed <reed@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
This commit is contained in:
Brian Osman 2020-03-30 09:57:53 -04:00 committed by Skia Commit-Bot
parent a01c6b0b59
commit ffd11f4ab3
12 changed files with 287 additions and 164 deletions

View File

@ -272,40 +272,44 @@ DEF_SIMPLE_GM(vertices_batching, canvas, 100, 500) {
draw_batching(canvas);
}
DEF_SIMPLE_GM(vertices_data, canvas, 500, 500) {
SkRect r = { 10, 10, 480, 480 };
int vcount = 4; // just a quad
int icount = 0;
SkVertices::CustomLayout customLayout { 4 }; // rgba values for now
SkVertices::Builder builder(SkVertices::kTriangleFan_VertexMode, vcount, icount, customLayout);
using AttrType = SkVertices::Attribute::Type;
// build the quad
SkPoint* pos = builder.positions();
pos[0] = {r.fLeft, r.fTop};
pos[1] = {r.fRight, r.fTop};
pos[2] = {r.fRight, r.fBottom};
pos[3] = {r.fLeft, r.fBottom};
DEF_SIMPLE_GM(vertices_data, canvas, 512, 256) {
for (auto attrType : {AttrType::kFloat4, AttrType::kByte4_unorm}) {
SkRect r = SkRect::MakeWH(256, 256);
int vcount = 4; // just a quad
int icount = 0;
SkVertices::Attribute attrs[] = { attrType };
SkVertices::Builder builder(SkVertices::kTriangleFan_VertexMode, vcount, icount, attrs, 1);
SkV4* col = (SkV4*)builder.perVertexData();
// We happen to treat the 4 fields as RGBA, in coordination with a hack in the raster-impl.
// In the future, these fields will just be passed to whatever SkSL we provide in the paint's
// shader.
col[0] = {1, 0, 0, 1}; // red
col[1] = {0, 1, 0, 1}; // green
col[2] = {0, 0, 1, 1}; // blue
col[3] = {0.5, 0.5, 0.5, 1}; // gray
r.toQuad(builder.positions());
auto vert = builder.detach();
SkPaint paint;
const char* gProg = R"(
varying float4 vtx_color;
void main(float2 p, inout half4 color) {
color = half4(vtx_color);
if (attrType == AttrType::kFloat4) {
SkV4* col = (SkV4*)builder.customData();
col[0] = {1, 0, 0, 1}; // red
col[1] = {0, 1, 0, 1}; // green
col[2] = {0, 0, 1, 1}; // blue
col[3] = {0.5, 0.5, 0.5, 1}; // gray
} else {
uint32_t* col = (uint32_t*)builder.customData();
col[0] = 0xFF0000FF;
col[1] = 0xFF00FF00;
col[2] = 0xFFFF0000;
col[3] = 0xFF7F7F7F;
}
)";
auto [effect, errorText] = SkRuntimeEffect::Make(SkString(gProg));
paint.setShader(effect->makeShader(nullptr, nullptr, 0, nullptr, true));
canvas->drawVertices(vert, paint);
SkPaint paint;
const char* gProg = R"(
varying float4 vtx_color;
void main(float2 p, inout half4 color) {
color = half4(vtx_color);
}
)";
auto[effect, errorText] = SkRuntimeEffect::Make(SkString(gProg));
paint.setShader(effect->makeShader(nullptr, nullptr, 0, nullptr, true));
canvas->drawVertices(builder.detach(), paint);
canvas->translate(r.width(), 0);
}
}
// Test case for skbug.com/10069. We need to draw the vertices twice (with different matrices) to
@ -354,15 +358,16 @@ DEF_SIMPLE_GM(vertices_data_lerp, canvas, 256, 256) {
auto patchVerts = SkPatchUtils::MakeVertices(pts, nullptr, nullptr, 12, 12);
SkVerticesPriv pv(patchVerts->priv());
SkVertices::CustomLayout customLayout { 1 };
SkVertices::Builder builder(pv.mode(), pv.vertexCount(), pv.indexCount(), customLayout);
SkVertices::Attribute attrs[1] = { AttrType::kFloat };
SkVertices::Builder builder(pv.mode(), pv.vertexCount(), pv.indexCount(), attrs, 1);
memcpy(builder.positions(), pv.positions(), pv.vertexCount() * sizeof(SkPoint));
memcpy(builder.indices(), pv.indices(), pv.indexCount() * sizeof(uint16_t));
SkRandom rnd;
float* lerpData = (float*)builder.customData();
for (int i = 0; i < pv.vertexCount(); ++i) {
builder.perVertexData()[i] = rnd.nextBool() ? 1.0f : 0.0f;
lerpData[i] = rnd.nextBool() ? 1.0f : 0.0f;
}
auto verts = builder.detach();

View File

@ -58,7 +58,28 @@ public:
nullptr);
}
struct CustomLayout { int fPerVertexDataCount; };
static constexpr int kMaxCustomAttributes = 8;
struct Attribute {
enum class Type : uint8_t {
kFloat,
kFloat2,
kFloat3,
kFloat4,
kByte4_unorm,
};
Attribute() : fType(Type::kFloat) {}
Attribute(Type t) : fType(t) {}
bool operator==(const Attribute& that) const { return fType == that.fType; }
bool operator!=(const Attribute& that) const { return !(*this == that); }
int channelCount() const;
size_t bytesPerVertex() const;
Type fType;
};
enum BuilderFlags {
kHasTexCoords_BuilderFlag = 1 << 0,
@ -69,21 +90,24 @@ public:
Builder(VertexMode mode, int vertexCount, int indexCount, uint32_t flags);
// EXPERIMENTAL -- do not call if you care what happens
Builder(VertexMode mode, int vertexCount, int indexCount, CustomLayout customLayout);
Builder(VertexMode mode,
int vertexCount,
int indexCount,
const Attribute* attrs,
int attrCount);
bool isValid() const { return fVertices != nullptr; }
// if the builder is invalid, these will return 0
int vertexCount() const;
int indexCount() const;
int perVertexDataCount() const;
SkPoint* positions();
uint16_t* indices(); // returns null if there are no indices
// if we have texCoords or colors, this will always be null
float* perVertexData(); // return null if there is no perVertexData
void* customData(); // returns null if there are no custom attributes
// If we have per-vertex-data, these will always be null
// If we have custom attributes, these will always be null
SkPoint* texCoords(); // returns null if there are no texCoords
SkColor* colors(); // returns null if there are no colors
@ -144,14 +168,16 @@ private:
// these point inside our allocation, so none of these can be "freed"
SkPoint* fPositions; // [vertexCount]
uint16_t* fIndices; // [indexCount] or null
float* fPerVertexData; // [perVertexDataCount * vertexCount] or null
void* fCustomData; // [customDataSize * vertexCount] or null
SkPoint* fTexs; // [vertexCount] or null
SkColor* fColors; // [vertexCount] or null
SkRect fBounds; // computed to be the union of the fPositions[]
int fVertexCount;
int fIndexCount;
int fPerVertexDataCount;
Attribute fAttributes[kMaxCustomAttributes];
int fAttributeCount;
VertexMode fMode;
// below here is where the actual array data is stored.

View File

@ -117,9 +117,6 @@ public:
// Combined size of just the 'uniform' variables.
size_t uniformSize() const { return fUniformSize; }
// Total number of channels across all varyings, combined.
int varyingCount() const;
ConstIterable<Variable> inputs() const { return ConstIterable<Variable>(fInAndUniformVars); }
ConstIterable<SkString> children() const { return ConstIterable<SkString>(fChildren); }
ConstIterable<Varying> varyings() const { return ConstIterable<Varying>(fVaryings); }

View File

@ -1992,9 +1992,17 @@ void SkCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const
// If the vertices contain custom attributes, ensure they line up with the paint's shader
const SkRuntimeEffect* effect =
paint.getShader() ? as_SB(paint.getShader())->asRuntimeEffect() : nullptr;
if (vertices->priv().perVertexDataCount() != (effect ? effect->varyingCount() : 0)) {
if ((size_t)vertices->priv().attributeCount() != (effect ? effect->varyings().count() : 0)) {
return;
}
if (effect) {
int attrIndex = 0;
for (const auto& v : effect->varyings()) {
if (vertices->priv().attributes()[attrIndex++].channelCount() != v.fWidth) {
return;
}
}
}
this->onDrawVerticesObject(vertices, mode, paint);
}

View File

@ -262,7 +262,7 @@ void SkDraw::draw_fixed_vertices(const SkVertices* vertices, SkBlendMode bmode,
const SkPoint dev2[], const SkPoint3 dev3[],
SkArenaAlloc* outerAlloc) const {
SkVerticesPriv info(vertices->priv());
SkASSERT(!info.hasPerVertexData());
SkASSERT(!info.hasCustomData());
const int vertexCount = info.vertexCount();
const int indexCount = info.indexCount();
@ -435,28 +435,7 @@ void SkDraw::draw_vdata_vertices(const SkVertices* vt, const SkPaint& paint,
const SkMatrix& ctmInv,
const SkPoint dev2[], const SkPoint3 dev3[],
SkArenaAlloc* outerAlloc) const {
SkASSERT(!!dev2 != !!dev3);
SkASSERT(!ctmInv.hasPerspective() || dev3 != nullptr);
SkVerticesPriv info(vt->priv());
VertState state(info.vertexCount(), info.indices(), info.indexCount());
VertState::Proc vertProc = state.chooseProc(info.mode());
SkPaint p(paint);
if (info.perVertexDataCount() >= 4) { // hack, treat as colors
auto triShader = outerAlloc->make<SkTriColorShader>(false, dev3 != nullptr);
p.setShader(sk_ref_sp(triShader));
auto blitter = SkCreateRasterPipelineBlitter(fDst, p, *fMatrix, outerAlloc,
this->fRC->clipShader());
auto colors = (const SkPMColor4f*)info.perVertexData();
while (vertProc(&state)) {
if (triShader->update(ctmInv, info.positions(), colors, state.f0, state.f1, state.f2)) {
fill_triangle(state, blitter, *fRC, dev2, dev3);
}
}
}
// TODO: Handle custom attributes
}
void SkDraw::drawVertices(const SkVertices* vertices, SkBlendMode bmode,
@ -464,7 +443,6 @@ void SkDraw::drawVertices(const SkVertices* vertices, SkBlendMode bmode,
SkVerticesPriv info(vertices->priv());
const int vertexCount = info.vertexCount();
const int indexCount = info.indexCount();
const int perVertexDataCount = info.perVertexDataCount();
// abort early if there is nothing to draw
if (vertexCount < 3 || (indexCount > 0 && indexCount < 3) || fRC->isEmpty()) {
@ -503,7 +481,7 @@ void SkDraw::drawVertices(const SkVertices* vertices, SkBlendMode bmode,
}
}
if (perVertexDataCount == 0) {
if (!info.hasCustomData()) {
this->draw_fixed_vertices(vertices, bmode, paint, ctmInv, dev2, dev3, &outerAlloc);
} else {
this->draw_vdata_vertices(vertices, paint, ctmInv, dev2, dev3, &outerAlloc);

View File

@ -269,14 +269,6 @@ size_t SkRuntimeEffect::inputSize() const {
fInAndUniformVars.back().sizeInBytes());
}
int SkRuntimeEffect::varyingCount() const {
int count = 0;
for (const auto& v : fVaryings) {
count += v.fWidth;
}
return count;
}
SkRuntimeEffect::SpecializeResult SkRuntimeEffect::specialize(SkSL::Program& baseProgram,
const void* inputs,
const SkSL::SharedCompiler& compiler) {

View File

@ -27,16 +27,48 @@ static int32_t next_id() {
return id;
}
int SkVertices::Attribute::channelCount() const {
switch (fType) {
case Type::kFloat: return 1;
case Type::kFloat2: return 2;
case Type::kFloat3: return 3;
case Type::kFloat4: return 4;
case Type::kByte4_unorm: return 4;
}
SkUNREACHABLE;
}
size_t SkVertices::Attribute::bytesPerVertex() const {
switch (fType) {
case Type::kFloat: return 1 * sizeof(float);
case Type::kFloat2: return 2 * sizeof(float);
case Type::kFloat3: return 3 * sizeof(float);
case Type::kFloat4: return 4 * sizeof(float);
case Type::kByte4_unorm: return 4 * sizeof(uint8_t);
}
SkUNREACHABLE;
}
static size_t custom_data_size(const SkVertices::Attribute* attrs, int attrCount) {
size_t size = 0;
for (int i = 0; i < attrCount; ++i) {
size += attrs[i].bytesPerVertex();
}
return size;
}
struct SkVertices::Desc {
VertexMode fMode;
int fVertexCount,
fIndexCount,
fPerVertexDataCount;
fIndexCount;
bool fHasTexs,
fHasColors;
const Attribute* fAttributes;
int fAttributeCount;
void validate() const {
SkASSERT(fPerVertexDataCount == 0 || (!fHasTexs && !fHasColors));
SkASSERT(fAttributeCount == 0 || (!fHasTexs && !fHasColors));
}
};
@ -46,9 +78,8 @@ struct SkVertices::Sizes {
SkSafeMath safe;
fVSize = safe.mul(desc.fVertexCount, sizeof(SkPoint));
fDSize = safe.mul(safe.mul(desc.fPerVertexDataCount,
sizeof(float)),
desc.fVertexCount);
fDSize = safe.mul(custom_data_size(desc.fAttributes, desc.fAttributeCount),
desc.fVertexCount);
fTSize = desc.fHasTexs ? safe.mul(desc.fVertexCount, sizeof(SkPoint)) : 0;
fCSize = desc.fHasColors ? safe.mul(desc.fVertexCount, sizeof(SkColor)) : 0;
@ -94,7 +125,7 @@ struct SkVertices::Sizes {
size_t fTotal; // size of entire SkVertices allocation (obj + arrays)
size_t fArrays; // size of all the arrays (V + D + T + C + I)
size_t fVSize;
size_t fDSize; // size of all perVertexData = [fPerVertexDataCount * fVertexCount]
size_t fDSize; // size of all customData = [customDataSize * fVertexCount]
size_t fTSize;
size_t fCSize;
size_t fISize;
@ -108,12 +139,18 @@ SkVertices::Builder::Builder(VertexMode mode, int vertexCount, int indexCount,
uint32_t builderFlags) {
bool hasTexs = SkToBool(builderFlags & SkVertices::kHasTexCoords_BuilderFlag);
bool hasColors = SkToBool(builderFlags & SkVertices::kHasColors_BuilderFlag);
this->init({mode, vertexCount, indexCount, 0, hasTexs, hasColors});
this->init({mode, vertexCount, indexCount, hasTexs, hasColors, nullptr, 0});
}
SkVertices::Builder::Builder(VertexMode mode, int vertexCount, int indexCount,
SkVertices::CustomLayout customLayout) {
this->init({mode, vertexCount, indexCount, customLayout.fPerVertexDataCount, false, false});
SkVertices::Builder::Builder(VertexMode mode,
int vertexCount,
int indexCount,
const SkVertices::Attribute* attrs,
int attrCount) {
if (attrCount <= 0 || attrCount > kMaxCustomAttributes || !attrs) {
return;
}
this->init({mode, vertexCount, indexCount, false, false, attrs, attrCount});
}
SkVertices::Builder::Builder(const Desc& desc) {
@ -145,14 +182,17 @@ void SkVertices::Builder::init(const Desc& desc) {
};
fVertices->fPositions = (SkPoint*) advance(sizes.fVSize);
fVertices->fPerVertexData = (float*) advance(sizes.fDSize);
fVertices->fCustomData = (void*) advance(sizes.fDSize);
fVertices->fTexs = (SkPoint*) advance(sizes.fTSize);
fVertices->fColors = (SkColor*) advance(sizes.fCSize);
fVertices->fIndices = (uint16_t*)advance(sizes.fISize);
fVertices->fVertexCount = desc.fVertexCount;
fVertices->fPerVertexDataCount = desc.fPerVertexDataCount;
fVertices->fIndexCount = desc.fIndexCount;
fVertices->fVertexCount = desc.fVertexCount;
fVertices->fIndexCount = desc.fIndexCount;
sk_careful_memcpy(fVertices->fAttributes, desc.fAttributes,
desc.fAttributeCount * sizeof(Attribute));
fVertices->fAttributeCount = desc.fAttributeCount;
fVertices->fMode = desc.fMode;
@ -197,16 +237,12 @@ int SkVertices::Builder::indexCount() const {
return fVertices ? fVertices->fIndexCount : 0;
}
int SkVertices::Builder::perVertexDataCount() const {
return fVertices ? fVertices->fPerVertexDataCount : 0;
}
SkPoint* SkVertices::Builder::positions() {
return fVertices ? const_cast<SkPoint*>(fVertices->fPositions) : nullptr;
}
float* SkVertices::Builder::perVertexData() {
return fVertices ? const_cast<float*>(fVertices->fPerVertexData) : nullptr;
void* SkVertices::Builder::customData() {
return fVertices ? const_cast<void*>(fVertices->fCustomData) : nullptr;
}
SkPoint* SkVertices::Builder::texCoords() {
@ -233,7 +269,7 @@ sk_sp<SkVertices> SkVertices::MakeCopy(VertexMode mode, int vertexCount,
const SkPoint pos[], const SkPoint texs[],
const SkColor colors[],
int indexCount, const uint16_t indices[]) {
auto desc = Desc{mode, vertexCount, indexCount, 0, !!texs, !!colors};
auto desc = Desc{mode, vertexCount, indexCount, !!texs, !!colors, nullptr, 0};
Builder builder(desc);
if (!builder.isValid()) {
return nullptr;
@ -255,19 +291,25 @@ size_t SkVertices::approximateSize() const {
}
SkVertices::Sizes SkVertices::getSizes() const {
Sizes sizes({fMode, fVertexCount, fIndexCount, fPerVertexDataCount, !!fTexs, !!fColors});
Sizes sizes(
{fMode, fVertexCount, fIndexCount, !!fTexs, !!fColors, fAttributes, fAttributeCount});
SkASSERT(sizes.isValid());
return sizes;
}
size_t SkVerticesPriv::customDataSize() const {
return custom_data_size(fVertices->fAttributes, fVertices->fAttributeCount);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
enum EncodedVerticesVersions {
kOriginal_Version = 0, // before we called out the mask for versions
kNoBones_Version = 1, // we explicitly ignore the bones-flag (0x400)
kPerVertexData_Version = 2, // add new count (and array)
kAttribute_Version = 3, // Adds array of custom attribute types
kCurrent_Version = kPerVertexData_Version
kCurrent_Version = kAttribute_Version
};
struct Header_v1 {
@ -285,7 +327,16 @@ struct Header_v2 {
// [pos] + [vertexData] + [texs] + [colors] + [indices]
};
#define kCurrentHeaderSize sizeof(Header_v2)
struct Header_v3 {
uint32_t fPacked;
int32_t fVertexCount;
int32_t fIndexCount;
int32_t fAttributeCount;
SkVertices::Attribute fAttributes[SkVertices::kMaxCustomAttributes];
// [pos] + [vertexData] + [texs] + [colors] + [indices]
};
#define kCurrentHeaderSize sizeof(Header_v3)
// storage = packed | vertex_count | index_count | pos[] | texs[] | colors[] | boneIndices[] |
// boneWeights[] | indices[]
@ -323,10 +374,13 @@ sk_sp<SkData> SkVertices::encode() const {
writer.write32(packed);
writer.write32(fVertexCount);
writer.write32(fIndexCount);
writer.write32(fPerVertexDataCount);
writer.write32(fAttributeCount);
// Write all custom attribute slots to simplify alignment
writer.write(fAttributes, sizeof(fAttributes));
writer.write(fPositions, sizes.fVSize);
writer.write(fPerVertexData, sizes.fDSize);
writer.write(fCustomData, sizes.fDSize);
writer.write(fTexs, sizes.fTSize);
writer.write(fColors, sizes.fCSize);
// if index-count is odd, we won't be 4-bytes aligned, so we call the pad version
@ -356,7 +410,21 @@ sk_sp<SkVertices> SkVertices::Decode(const void* data, size_t length) {
if (version >= kPerVertexData_Version && length < sizeof(Header_v2)) {
return nullptr;
}
const int perVertexDataCount = (version >= kPerVertexData_Version) ? reader.readS32() : 0;
if (version >= kAttribute_Version && length < sizeof(Header_v3)) {
return nullptr;
}
Attribute attrs[kMaxCustomAttributes];
const int attrCount = (version >= kPerVertexData_Version) ? reader.readS32() : 0;
if (attrCount < 0 || attrCount > kMaxCustomAttributes) {
return nullptr;
}
if (version >= kAttribute_Version) {
reader.read(attrs, sizeof(attrs));
} else if (version == kPerVertexData_Version) {
// Attributes default to kFloat, which works fine to migrate this version of data.
}
// now we finish unpacking the packed field
const bool hasTexs = SkToBool(packed & kHasTexs_Mask);
@ -364,11 +432,11 @@ sk_sp<SkVertices> SkVertices::Decode(const void* data, size_t length) {
const bool hasBones = SkToBool(packed & kHasBones_Mask_V0);
// check if we're overspecified for v2+
if (perVertexDataCount > 0 && (hasTexs || hasColors || hasBones)) {
if (attrCount > 0 && (hasTexs || hasColors || hasBones)) {
return nullptr;
}
const Desc desc{
mode, vertexCount, indexCount, perVertexDataCount, hasTexs, hasColors
mode, vertexCount, indexCount, hasTexs, hasColors, attrCount ? attrs : nullptr, attrCount
};
Sizes sizes(desc);
if (!sizes.isValid()) {
@ -386,7 +454,7 @@ sk_sp<SkVertices> SkVertices::Decode(const void* data, size_t length) {
SkSafeMath safe_math;
reader.read(builder.positions(), sizes.fVSize);
reader.read(builder.perVertexData(), sizes.fDSize);
reader.read(builder.customData(), sizes.fDSize);
reader.read(builder.texCoords(), sizes.fTSize);
reader.read(builder.colors(), sizes.fCSize);
if (hasBones) {

View File

@ -21,20 +21,22 @@ class SkVerticesPriv {
public:
SkVertices::VertexMode mode() const { return fVertices->fMode; }
bool hasPerVertexData() const { return SkToBool(fVertices->fPerVertexData); }
bool hasCustomData() const { return SkToBool(fVertices->fCustomData); }
bool hasColors() const { return SkToBool(fVertices->fColors); }
bool hasTexCoords() const { return SkToBool(fVertices->fTexs); }
bool hasIndices() const { return SkToBool(fVertices->fIndices); }
int vertexCount() const { return fVertices->fVertexCount; }
int indexCount() const { return fVertices->fIndexCount; }
int perVertexDataCount() const { return fVertices->fPerVertexDataCount; }
int attributeCount() const { return fVertices->fAttributeCount; }
size_t customDataSize() const;
const SkPoint* positions() const { return fVertices->fPositions; }
const float* perVertexData() const { return fVertices->fPerVertexData; }
const void* customData() const { return fVertices->fCustomData; }
const SkPoint* texCoords() const { return fVertices->fTexs; }
const SkColor* colors() const { return fVertices->fColors; }
const uint16_t* indices() const { return fVertices->fIndices; }
const SkVertices::Attribute* attributes() const { return fVertices->fAttributes; }
// Never called due to RVO in priv(), but must exist for MSVC 2017.
SkVerticesPriv(const SkVerticesPriv&) = default;

View File

@ -99,6 +99,11 @@ struct GrVertexWriter {
this->write(remainder...);
}
void writeRaw(const void* data, size_t size) {
memcpy(fPtr, data, size);
fPtr = SkTAddOffset<void>(fPtr, size);
}
void write() {}
/**

View File

@ -1038,13 +1038,12 @@ void SkGpuDevice::drawVertices(const SkVertices* vertices, SkBlendMode mode, con
const SkRuntimeEffect* effect =
paint.getShader() ? as_SB(paint.getShader())->asRuntimeEffect() : nullptr;
SkASSERT(info.perVertexDataCount() == (effect ? effect->varyingCount() : 0));
// Pretend that we have tex coords when using custom per-vertex data. The shader is going to
// use those (rather than local coords), but our paint conversion remains the same.
GrPaint grPaint;
bool hasColors = info.hasColors();
bool hasTexs = info.hasTexCoords() || info.hasPerVertexData();
bool hasTexs = info.hasTexCoords() || info.hasCustomData();
if ((!hasTexs || !paint.getShader()) && !hasColors) {
// The dreaded wireframe mode. Fallback to drawVertices and go so slooooooow.
this->wireframeVertices(info.mode(), info.vertexCount(), info.positions(), mode,

View File

@ -34,6 +34,28 @@ enum class LocalCoordsType {
kExplicit,
};
static GrVertexAttribType SkVerticesAttributeToGrVertexAttribType(const SkVertices::Attribute& a) {
switch (a.fType) {
case SkVertices::Attribute::Type::kFloat: return kFloat_GrVertexAttribType;
case SkVertices::Attribute::Type::kFloat2: return kFloat2_GrVertexAttribType;
case SkVertices::Attribute::Type::kFloat3: return kFloat3_GrVertexAttribType;
case SkVertices::Attribute::Type::kFloat4: return kFloat4_GrVertexAttribType;
case SkVertices::Attribute::Type::kByte4_unorm: return kUByte4_norm_GrVertexAttribType;
}
SkUNREACHABLE;
}
static GrSLType SkVerticesAttributeToGrSLType(const SkVertices::Attribute& a) {
switch (a.fType) {
case SkVertices::Attribute::Type::kFloat: return kFloat_GrSLType;
case SkVertices::Attribute::Type::kFloat2: return kFloat2_GrSLType;
case SkVertices::Attribute::Type::kFloat3: return kFloat3_GrSLType;
case SkVertices::Attribute::Type::kFloat4: return kFloat4_GrSLType;
case SkVertices::Attribute::Type::kByte4_unorm: return kHalf4_GrSLType;
}
SkUNREACHABLE;
}
class VerticesGP : public GrGeometryProcessor {
public:
static GrGeometryProcessor* Make(SkArenaAlloc* arena,
@ -42,11 +64,10 @@ public:
const SkPMColor4f& color,
sk_sp<GrColorSpaceXform> colorSpaceXform,
const SkMatrix& viewMatrix,
int attrWidths[],
const SkVertices::Attribute* attrs,
int attrCount) {
return arena->make<VerticesGP>(localCoordsType, colorArrayType, color,
std::move(colorSpaceXform), viewMatrix, attrWidths,
attrCount);
std::move(colorSpaceXform), viewMatrix, attrs, attrCount);
}
const char* name() const override { return "VerticesGP"; }
@ -195,7 +216,7 @@ private:
const SkPMColor4f& color,
sk_sp<GrColorSpaceXform> colorSpaceXform,
const SkMatrix& viewMatrix,
int attrWidths[],
const SkVertices::Attribute* attrs,
int attrCount)
: INHERITED(kVerticesGP_ClassID)
, fColorArrayType(colorArrayType)
@ -212,13 +233,11 @@ private:
: missingAttr);
for (int i = 0; i < attrCount; ++i) {
SkASSERT(attrWidths[i] >= 1 && attrWidths[i] <= 4);
int ofs = attrWidths[i] - 1;
// Attributes store char*, so allocate long-lived storage for the (dynamic) names
fAttrNames.push_back(SkStringPrintf("_vtx_attr%d", i));
fAttributes.push_back({ fAttrNames.back().c_str(),
(GrVertexAttribType)(kFloat_GrVertexAttribType + ofs),
(GrSLType)(kFloat_GrSLType + ofs) });
fAttributes.push_back({fAttrNames.back().c_str(),
SkVerticesAttributeToGrVertexAttribType(attrs[i]),
SkVerticesAttributeToGrSLType(attrs[i])});
}
this->setVertexAttributes(fAttributes.data(), fAttributes.size());
@ -322,7 +341,7 @@ private:
return sizeof(SkPoint) +
(this->requiresPerVertexColors() ? sizeof(uint32_t) : 0) +
(this->requiresPerVertexLocalCoords() ? sizeof(SkPoint) : 0) +
fMeshes[0].fVertices->priv().perVertexDataCount() * sizeof(float);
fMeshes[0].fVertices->priv().customDataSize();
}
Helper fHelper;
@ -336,7 +355,6 @@ private:
LocalCoordsType fLocalCoordsType;
ColorArrayType fColorArrayType;
sk_sp<GrColorSpaceXform> fColorSpaceXform;
std::vector<int> fAttrWidths;
GrSimpleMesh* fMesh = nullptr;
GrProgramInfo* fProgramInfo = nullptr;
@ -364,15 +382,6 @@ DrawVerticesOp::DrawVerticesOp(const Helper::MakeArgs& helperArgs, const SkPMCol
fLocalCoordsType = info.hasTexCoords() ? LocalCoordsType::kExplicit
: LocalCoordsType::kUsePosition;
if (effect) {
SkASSERT(info.perVertexDataCount() == effect->varyingCount());
for (const auto& v : effect->varyings()) {
fAttrWidths.push_back(v.fWidth);
}
} else {
SkASSERT(info.perVertexDataCount() == 0);
}
Mesh& mesh = fMeshes.push_back();
mesh.fColor = color;
mesh.fViewMatrix = viewMatrix;
@ -434,8 +443,10 @@ GrGeometryProcessor* DrawVerticesOp::makeGP(SkArenaAlloc* arena) {
const SkMatrix& vm = fMultipleViewMatrices ? SkMatrix::I() : fMeshes[0].fViewMatrix;
SkVerticesPriv info(fMeshes[0].fVertices->priv());
auto gp = VerticesGP::Make(arena, fLocalCoordsType, fColorArrayType, fMeshes[0].fColor,
std::move(csxform), vm, fAttrWidths.data(), fAttrWidths.size());
std::move(csxform), vm, info.attributes(), info.attributeCount());
SkASSERT(this->vertexStride() == gp->vertexStride());
return gp;
}
@ -494,8 +505,8 @@ void DrawVerticesOp::onPrepareDraws(Target* target) {
const SkPoint* positions = info.positions();
const SkColor* colors = info.colors();
const SkPoint* localCoords = info.texCoords() ? info.texCoords() : positions;
const float* custom = info.perVertexData();
int customCount = info.perVertexDataCount();
const void* custom = info.customData();
size_t customDataSize = info.customDataSize();
// TODO4F: Preserve float colors
GrColor meshColor = mesh.fColor.toBytes_RGBA();
@ -510,8 +521,9 @@ void DrawVerticesOp::onPrepareDraws(Target* target) {
if (hasLocalCoordsAttribute) {
verts.write(localCoords[i]);
}
for (int j = 0; j < customCount; ++j) {
verts.write(*custom++);
if (customDataSize) {
verts.writeRaw(custom, customDataSize);
custom = SkTAddOffset<const void>(custom, customDataSize);
}
}
@ -567,7 +579,11 @@ GrOp::CombineResult DrawVerticesOp::onCombineIfPossible(GrOp* t, GrRecordingCont
return CombineResult::kCannotCombine;
}
if (fAttrWidths != that->fAttrWidths) {
SkVerticesPriv vThis(this->fMeshes[0].fVertices->priv()),
vThat(that->fMeshes[0].fVertices->priv());
if (vThis.attributeCount() != vThat.attributeCount() ||
!std::equal(vThis.attributes(), vThis.attributes() + vThis.attributeCount(),
vThat.attributes())) {
return CombineResult::kCannotCombine;
}

View File

@ -24,11 +24,16 @@ static bool equal(const SkVertices* vert0, const SkVertices* vert1) {
if (v0.indexCount() != v1.indexCount()) {
return false;
}
if (v0.perVertexDataCount() != v1.perVertexDataCount()) {
if (v0.attributeCount() != v1.attributeCount()) {
return false;
}
for (int i = 0; i < v0.attributeCount(); ++i) {
if (v0.attributes()[i] != v1.attributes()[i]) {
return false;
}
}
if (!!v0.perVertexData() != !!v1.perVertexData()) {
if (!!v0.customData() != !!v1.customData()) {
return false;
}
if (!!v0.texCoords() != !!v1.texCoords()) {
@ -53,9 +58,9 @@ static bool equal(const SkVertices* vert0, const SkVertices* vert1) {
}
}
}
int totalVertexDataCount = v0.vertexCount() * v0.perVertexDataCount();
for (int i = 0; i < totalVertexDataCount; ++i) {
if (v0.perVertexData()[i] != v1.perVertexData()[i]) {
size_t totalCustomDataSize = v0.vertexCount() * v0.customDataSize();
if (totalCustomDataSize) {
if (memcmp(v0.customData(), v1.customData(), totalCustomDataSize) != 0) {
return false;
}
}
@ -108,18 +113,36 @@ DEF_TEST(Vertices, reporter) {
self_test(builder.detach(), reporter);
}
}
// per-vertex-data tests
for (int perVertexDataCount : {0, 1, 2, 3, 4, 7, 32}) {
// custom data tests
using AttrType = SkVertices::Attribute::Type;
struct {
int count;
size_t expected_size;
SkVertices::Attribute attrs[4];
} attrTests[] = {
{ 1, 4, { AttrType::kFloat } },
{ 1, 8, { AttrType::kFloat2 } },
{ 1, 12, { AttrType::kFloat3 } },
{ 1, 16, { AttrType::kFloat4 } },
{ 1, 4, { AttrType::kByte4_unorm } },
{ 4, 16, { AttrType::kFloat, AttrType::kFloat, AttrType::kFloat, AttrType::kFloat } },
{ 2, 12, { AttrType::kFloat2, AttrType::kByte4_unorm } },
{ 2, 12, { AttrType::kByte4_unorm, AttrType::kFloat2 } },
};
for (const auto& test : attrTests) {
SkVertices::Builder builder(SkVertices::kTriangles_VertexMode, vCount, iCount,
SkVertices::CustomLayout{perVertexDataCount});
test.attrs, test.count);
REPORTER_ASSERT(reporter, builder.vertexCount() == vCount);
REPORTER_ASSERT(reporter, builder.indexCount() == iCount);
REPORTER_ASSERT(reporter, builder.perVertexDataCount() == perVertexDataCount);
float* customData = (float*)builder.customData();
int customDataCount = test.expected_size / sizeof(float);
for (int i = 0; i < builder.vertexCount(); ++i) {
builder.positions()[i].set((float)i, 1);
for (int j = 0; j < builder.perVertexDataCount(); ++j) {
builder.perVertexData()[i * perVertexDataCount + j] = (float)j;
for (int j = 0; j < customDataCount; ++j) {
customData[i * customDataCount + j] = (float)j;
}
}
for (int i = 0; i < builder.indexCount(); ++i) {
@ -127,6 +150,7 @@ DEF_TEST(Vertices, reporter) {
}
self_test(builder.detach(), reporter);
}
{
// This has the maximum number of vertices to be rewritten as indexed triangles without
// overflowing a 16bit index.
@ -161,24 +185,27 @@ DEF_TEST(Vertices, reporter) {
// validity tests for per-vertex-data
{ // negative count is bad
SkVertices::Builder builder(SkVertices::kTriangleFan_VertexMode, 10, 0,
SkVertices::CustomLayout{-1});
// Check that invalid counts fail to initialize the builder
for (int attrCount : {-1, 0, SkVertices::kMaxCustomAttributes + 1}) {
SkVertices::Attribute attrs[] = { AttrType::kFloat };
SkVertices::Builder builder(SkVertices::kTriangleFan_VertexMode, 10, 0, attrs, attrCount);
REPORTER_ASSERT(reporter, !builder.isValid());
}
{ // zero-per-vertex-data should be ok
SkVertices::Builder builder(SkVertices::kTriangleFan_VertexMode, 10, 0,
SkVertices::CustomLayout{0});
REPORTER_ASSERT(reporter, builder.isValid());
REPORTER_ASSERT(reporter, builder.perVertexDataCount() == 0);
REPORTER_ASSERT(reporter, builder.perVertexData() == nullptr);
{ // nullptr is definitely bad
SkVertices::Builder builder(SkVertices::kTriangleFan_VertexMode, 10, 0, nullptr, 4);
REPORTER_ASSERT(reporter, !builder.isValid());
}
{ // "normal" number of per-vertex-data
SkVertices::Builder builder(SkVertices::kTriangleFan_VertexMode, 10, 0,
SkVertices::CustomLayout{4});
{ // "normal" number of per-vertex-data (all floats)
SkVertices::Attribute attrs[] = {AttrType::kFloat2, AttrType::kFloat2};
SkVertices::Builder builder(SkVertices::kTriangleFan_VertexMode, 10, 0, attrs, 2);
REPORTER_ASSERT(reporter, builder.isValid());
REPORTER_ASSERT(reporter, builder.perVertexDataCount() == 4);
REPORTER_ASSERT(reporter, builder.perVertexData() != nullptr);
REPORTER_ASSERT(reporter, builder.customData() != nullptr);
}
{ // "normal" number of per-vertex-data (with packed bytes)
SkVertices::Attribute attrs[] = {AttrType::kFloat2, AttrType::kByte4_unorm};
SkVertices::Builder builder(SkVertices::kTriangleFan_VertexMode, 10, 0, attrs, 2);
REPORTER_ASSERT(reporter, builder.isValid());
REPORTER_ASSERT(reporter, builder.customData() != nullptr);
}
}