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:
parent
a01c6b0b59
commit
ffd11f4ab3
@ -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();
|
||||
|
@ -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.
|
||||
|
@ -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); }
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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() {}
|
||||
|
||||
/**
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user