[graphite] Add SkPaintParamsKey::BlockReader class

This class hides a lot of the finicky details of block handling and provides a structured means of accessing an SkPaintParamsKey's data payload.

In a following CL this will be used to expose the SkPaintParamsKey's data to the snippet glue code.

Bug: skia:12701
Change-Id: I6554940429c7c6e048cfd37533951c61cf255408
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/516337
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
This commit is contained in:
Robert Phillips 2022-03-08 09:31:13 -05:00 committed by SkCQ
parent 0c43db950c
commit 6509844d91
2 changed files with 188 additions and 87 deletions

View File

@ -196,62 +196,13 @@ bool SkPaintParamsKey::operator==(const SkPaintParamsKey& that) const {
!memcmp(fData.data(), that.fData.data(), fData.size());
}
SkPaintParamsKey::BlockReader SkPaintParamsKey::reader(const SkShaderCodeDictionary* dict,
int headerOffset) const {
return BlockReader(dict, fData, headerOffset);
}
#ifdef SK_DEBUG
namespace {
void output_indent(int indent) {
for (int i = 0; i < indent; ++i) {
SkDebugf(" ");
}
}
int dump_block(const SkShaderCodeDictionary* dict,
const SkPaintParamsKey& key,
int headerOffset,
int indent) {
uint8_t id = key.byte(headerOffset);
uint8_t blockSize = key.byte(headerOffset+1);
SkASSERT(blockSize >= 2 && headerOffset+blockSize <= key.sizeInBytes());
auto entry = dict->getEntry(id);
if (!entry) {
output_indent(indent);
SkDebugf("unknown block! (%dB)\n", blockSize);
}
output_indent(indent);
SkDebugf("%s block (%dB)\n", entry->fStaticFunctionName, blockSize);
int curOffset = headerOffset + SkPaintParamsKey::kBlockHeaderSizeInBytes;
for (int i = 0; i < entry->fNumChildren; ++i) {
output_indent(indent);
// TODO: it would be nice if the names of the children were also stored (i.e., "src"/"dst")
SkDebugf("child %d:\n", i);
int childSize = dump_block(dict, key, curOffset, indent+1);
curOffset += childSize;
}
for (auto e : entry->fDataPayloadExpectations) {
output_indent(indent);
SkDebugf("%s[%d]: ", e.fName, e.fCount);
// TODO: add some sort of templatized reader object for this
SkASSERT(e.fType == SkPaintParamsKey::DataPayloadType::kByte);
for (uint32_t i = 0; i < e.fCount; ++i) {
output_indent(indent);
SkDebugf("%d,", key.byte(curOffset));
++curOffset;
}
SkDebugf("\n");
}
return blockSize;
}
} // anonymous namespace
// This just iterates over the top-level blocks calling block-specific dump methods.
void SkPaintParamsKey::dump(const SkShaderCodeDictionary* dict) const {
SkDebugf("--------------------------------------\n");
@ -259,45 +210,39 @@ void SkPaintParamsKey::dump(const SkShaderCodeDictionary* dict) const {
int curHeaderOffset = 0;
while (curHeaderOffset < this->sizeInBytes()) {
int blockSize = dump_block(dict, *this, curHeaderOffset, 0);
curHeaderOffset += blockSize;
BlockReader reader = this->reader(dict, curHeaderOffset);
reader.dump(dict, /* indent */ 0);
curHeaderOffset += reader.blockSize();
}
}
#endif
#endif // SK_DEBUG
int SkPaintParamsKey::AddBlockToShaderInfo(SkShaderCodeDictionary* dict,
const SkPaintParamsKey& key,
int headerOffset,
SkShaderInfo* result) {
auto [codeSnippetID, blockSize] = key.readCodeSnippetID(headerOffset);
void SkPaintParamsKey::AddBlockToShaderInfo(SkShaderCodeDictionary* dict,
const SkPaintParamsKey::BlockReader& reader,
SkShaderInfo* result) {
auto entry = dict->getEntry(codeSnippetID);
result->add(*entry);
result->add(*reader.entry());
// The child blocks appear right after the parent block's header in the key and go
// right after the parent's SnippetEntry in the shader info
int childOffset = headerOffset + kBlockHeaderSizeInBytes;
for (int i = 0; i < entry->fNumChildren; ++i) {
SkASSERT(childOffset < headerOffset + blockSize);
for (int i = 0; i < reader.numChildren(); ++i) {
SkPaintParamsKey::BlockReader childReader = reader.child(dict, i);
int childBlockSize = AddBlockToShaderInfo(dict, key, childOffset, result);
childOffset += childBlockSize;
AddBlockToShaderInfo(dict, childReader, result);
}
if (codeSnippetID != SkBuiltInCodeSnippetID::kDepthStencilOnlyDraw) {
if (reader.codeSnippetId() != SkBuiltInCodeSnippetID::kDepthStencilOnlyDraw) {
result->setWritesColor();
}
return blockSize;
}
void SkPaintParamsKey::toShaderInfo(SkShaderCodeDictionary* dict, SkShaderInfo* result) const {
int curHeaderOffset = 0;
while (curHeaderOffset < this->sizeInBytes()) {
int blockSize = AddBlockToShaderInfo(dict, *this, curHeaderOffset, result);
curHeaderOffset += blockSize;
SkPaintParamsKey::BlockReader reader = this->reader(dict, curHeaderOffset);
AddBlockToShaderInfo(dict, reader, result);
curHeaderOffset += reader.blockSize();
}
}
@ -308,3 +253,125 @@ bool SkPaintParamsKey::isErrorKey() const {
fData[1] == SkPaintParamsKey::kBlockHeaderSizeInBytes;
}
#endif
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace {
#ifdef SK_DEBUG
void output_indent(int indent) {
for (int i = 0; i < indent; ++i) {
SkDebugf(" ");
}
}
#endif
std::pair<SkBuiltInCodeSnippetID, uint8_t> read_header(SkSpan<const uint8_t> parentSpan,
int headerOffset) {
SkASSERT(headerOffset + SkPaintParamsKey::kBlockHeaderSizeInBytes <=
SkTo<int>(parentSpan.size()));
SkBuiltInCodeSnippetID id = static_cast<SkBuiltInCodeSnippetID>(parentSpan[headerOffset]);
uint8_t blockSize = parentSpan[headerOffset+SkPaintParamsKey::kBlockSizeOffsetInBytes];
SkASSERT(blockSize >= SkPaintParamsKey::kBlockHeaderSizeInBytes);
SkASSERT(headerOffset + blockSize <= static_cast<int>(parentSpan.size()));
return { id, blockSize };
}
} // anonymous namespace
SkPaintParamsKey::BlockReader::BlockReader(const SkShaderCodeDictionary* dict,
SkSpan<const uint8_t> parentSpan,
int offsetInParent) {
auto [codeSnippetID, blockSize] = read_header(parentSpan, offsetInParent);
fBlock = parentSpan.subspan(offsetInParent, blockSize);
fEntry = dict->getEntry(codeSnippetID);
SkASSERT(fEntry);
}
int SkPaintParamsKey::BlockReader::numChildren() const { return fEntry->fNumChildren; }
SkPaintParamsKey::BlockReader SkPaintParamsKey::BlockReader::child(
const SkShaderCodeDictionary* dict,
int childIndex) const {
SkASSERT(childIndex < fEntry->fNumChildren);
int childOffset = kBlockHeaderSizeInBytes;
for (int i = 0; i < childIndex; ++i) {
auto [_, childBlockSize] = read_header(fBlock, childOffset);
childOffset += childBlockSize;
}
return BlockReader(dict, fBlock, childOffset);
}
SkSpan<const uint8_t> SkPaintParamsKey::BlockReader::dataPayload() const {
int payloadOffset = kBlockHeaderSizeInBytes;
for (int i = 0; i < fEntry->fNumChildren; ++i) {
auto [_, childBlockSize] = read_header(fBlock, payloadOffset);
payloadOffset += childBlockSize;
}
int payloadSize = this->blockSize() - payloadOffset;
return fBlock.subspan(payloadOffset, payloadSize);
}
SkSpan<const uint8_t> SkPaintParamsKey::BlockReader::bytes(int fieldIndex) const {
SkASSERT(fEntry->fDataPayloadExpectations[fieldIndex].fType == DataPayloadType::kByte);
int byteOffsetInPayload = 0;
for (int i = 0; i < fieldIndex; ++i) {
SkASSERT(fEntry->fDataPayloadExpectations[i].fType == DataPayloadType::kByte);
byteOffsetInPayload += fEntry->fDataPayloadExpectations[i].fCount;
}
SkSpan<const uint8_t> dataPayload = this->dataPayload();
return dataPayload.subspan(byteOffsetInPayload,
fEntry->fDataPayloadExpectations[fieldIndex].fCount);
}
#ifdef SK_DEBUG
int SkPaintParamsKey::BlockReader::numDataPayloadFields() const {
return fEntry->fDataPayloadExpectations.size();
}
void SkPaintParamsKey::BlockReader::dump(const SkShaderCodeDictionary* dict, int indent) const {
uint8_t id = static_cast<uint8_t>(this->codeSnippetId());
uint8_t blockSize = this->blockSize();
auto entry = dict->getEntry(id);
if (!entry) {
output_indent(indent);
SkDebugf("unknown block! (%dB)\n", blockSize);
}
output_indent(indent);
SkDebugf("%s block (%dB)\n", entry->fStaticFunctionName, blockSize);
for (int i = 0; i < this->numChildren(); ++i) {
output_indent(indent);
// TODO: it would be nice if the names of the children were also stored (i.e., "src"/"dst")
SkDebugf("child %d:\n", i);
SkPaintParamsKey::BlockReader childReader = this->child(dict, i);
childReader.dump(dict, indent+1);
}
for (int i = 0; i < (int) fEntry->fDataPayloadExpectations.size(); ++i) {
output_indent(indent);
SkDebugf("%s[%d]: ",
fEntry->fDataPayloadExpectations[i].fName,
fEntry->fDataPayloadExpectations[i].fCount);
SkSpan<const uint8_t> bytes = this->bytes(i);
for (uint8_t b : bytes) {
SkDebugf("%d,", b);
}
SkDebugf("\n");
}
}
#endif // SK_DEBUG

View File

@ -22,10 +22,10 @@ enum class SkBackend : uint8_t {
kGraphite,
kSkVM
};
struct SkDataPayloadInfo;
class SkPaintParamsKeyBuilder;
class SkShaderCodeDictionary;
class SkShaderInfo;
struct SkShaderSnippet;
// This class is a compact representation of the shader needed to implement a given
// PaintParams. Its structure is a series of blocks where each block has a
@ -56,15 +56,50 @@ public:
~SkPaintParamsKey();
std::pair<SkBuiltInCodeSnippetID, uint8_t> readCodeSnippetID(int headerOffset) const {
SkASSERT(headerOffset <= this->sizeInBytes() - kBlockHeaderSizeInBytes);
class BlockReader {
public:
uint8_t blockSize() const {
SkASSERT(fBlock[kBlockSizeOffsetInBytes] == fBlock.size());
return SkTo<uint8_t>(fBlock.size());
}
SkBuiltInCodeSnippetID id = static_cast<SkBuiltInCodeSnippetID>(fData[headerOffset]);
uint8_t blockSize = fData[headerOffset+1];
SkASSERT(headerOffset + blockSize <= this->sizeInBytes());
int numChildren() const;
return { id, blockSize };
}
// Return the childIndex-th child's BlockReader
BlockReader child(const SkShaderCodeDictionary*, int childIndex) const;
// Retrieve the fieldIndex-th field in the data payload as a span of bytes. The type
// being read (bytes in this case) is checked against the data payload's structure.
SkSpan<const uint8_t> bytes(int fieldIndex) const;
// TODO: add more types (as needed) and their corresponding access methods
const SkShaderSnippet* entry() const { return fEntry; }
#ifdef SK_DEBUG
int numDataPayloadFields() const;
void dump(const SkShaderCodeDictionary*, int indent) const;
#endif
private:
friend class SkPaintParamsKey; // for ctor
BlockReader(const SkShaderCodeDictionary*,
SkSpan<const uint8_t> parentSpan,
int offsetInParent);
SkBuiltInCodeSnippetID codeSnippetId() const {
return static_cast<SkBuiltInCodeSnippetID>(fBlock[0]);
}
// The data payload appears after any children and occupies the remainder of the
// block's space.
SkSpan<const uint8_t> dataPayload() const;
SkSpan<const uint8_t> fBlock;
const SkShaderSnippet* fEntry;
};
BlockReader reader(const SkShaderCodeDictionary*, int headerOffset) const;
#ifdef SK_DEBUG
uint8_t byte(int offset) const {
@ -99,10 +134,9 @@ private:
// is in the dictionary). In this case the dictionary will own the memory backing the span.
SkPaintParamsKey(SkSpan<const uint8_t> rawData);
static int AddBlockToShaderInfo(SkShaderCodeDictionary*,
const SkPaintParamsKey&,
int headerOffset,
SkShaderInfo*);
static void AddBlockToShaderInfo(SkShaderCodeDictionary*,
const SkPaintParamsKey::BlockReader&,
SkShaderInfo*);
// The memory referenced in 'fData' is always owned by someone else.
// If 'fOriginatingBuilder' is null, the dictionary's SkArena owns the memory and no explicit