/* * Copyright 2022 Google LLC * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "src/core/SkPaintParamsKey.h" #include #include "src/core/SkKeyHelpers.h" #include "src/core/SkShaderCodeDictionary.h" //-------------------------------------------------------------------------------------------------- SkPaintParamsKeyBuilder::SkPaintParamsKeyBuilder(const SkShaderCodeDictionary* dict) : fDict(dict) { } // Block headers have the following structure: // 1st byte: codeSnippetID // 2nd byte: total blockSize in bytes // This call stores the header's offset in the key on the stack to be used in 'endBlock' void SkPaintParamsKeyBuilder::beginBlock(int codeSnippetID) { if (!this->isValid()) { return; } if (codeSnippetID < 0 || codeSnippetID > fDict->maxCodeSnippetID()) { this->makeInvalid(); return; } fData.reserve_back(SkPaintParamsKey::kBlockHeaderSizeInBytes); fStack.push_back({ codeSnippetID, this->sizeInBytes() }); this->addByte(SkTo(codeSnippetID)); this->addByte(0); // this needs to be patched up with a call to endBlock } // Update the size byte of a block header void SkPaintParamsKeyBuilder::endBlock() { if (!this->isValid()) { return; } if (fStack.empty()) { SkASSERT(0); this->makeInvalid(); return; } int headerOffset = fStack.back().fHeaderOffset; SkASSERT(fData[headerOffset] == fStack.back().fCodeSnippetID); SkASSERT(fData[headerOffset+SkPaintParamsKey::kBlockSizeOffsetInBytes] == 0); int blockSize = this->sizeInBytes() - headerOffset; if (blockSize > SkPaintParamsKey::kMaxBlockSize) { this->makeInvalid(); return; } fData[headerOffset+SkPaintParamsKey::kBlockSizeOffsetInBytes] = blockSize; fStack.pop_back(); } void SkPaintParamsKeyBuilder::addBytes(uint32_t numBytes, const uint8_t* data) { if (!this->isValid()) { return; } fData.push_back_n(numBytes, data); } std::unique_ptr SkPaintParamsKeyBuilder::snap() { if (!fStack.empty()) { this->makeInvalid(); // fall through } auto key = std::unique_ptr(new SkPaintParamsKey(std::move(fData))); // Reset for reuse fIsValid = true; fStack.clear(); return key; } void SkPaintParamsKeyBuilder::makeInvalid() { SkASSERT(fIsValid); fStack.clear(); fData.reset(); this->beginBlock(SkBuiltInCodeSnippetID::kError); this->endBlock(); SkASSERT(fIsValid); fIsValid = false; } //-------------------------------------------------------------------------------------------------- SkPaintParamsKey::SkPaintParamsKey(SkTArray&& data) : fData(std::move(data)) {} bool SkPaintParamsKey::operator==(const SkPaintParamsKey& that) const { return fData == that.fData; } #ifdef SK_DEBUG typedef void (*DumpMethod)(const SkPaintParamsKey&, int headerOffset); namespace { void dump_unknown_block(const SkPaintParamsKey& key, int headerOffset) { uint8_t id = key.byte(headerOffset); uint8_t blockSize = key.byte(headerOffset+1); SkASSERT(blockSize >= 2 && headerOffset+blockSize <= key.sizeInBytes()); SkDebugf("Unknown block - id: %d size: %dB\n", id, blockSize); } DumpMethod get_dump_method(SkBuiltInCodeSnippetID id) { switch (id) { case SkBuiltInCodeSnippetID::kDepthStencilOnlyDraw: return DepthStencilOnlyBlock::Dump; // SkShader code snippets case SkBuiltInCodeSnippetID::kSolidColorShader: return SolidColorShaderBlock::Dump; case SkBuiltInCodeSnippetID::kLinearGradientShader: [[fallthrough]]; case SkBuiltInCodeSnippetID::kRadialGradientShader: [[fallthrough]]; case SkBuiltInCodeSnippetID::kSweepGradientShader: [[fallthrough]]; case SkBuiltInCodeSnippetID::kConicalGradientShader: return GradientShaderBlocks::Dump; case SkBuiltInCodeSnippetID::kImageShader: return ImageShaderBlock::Dump; case SkBuiltInCodeSnippetID::kBlendShader: return BlendShaderBlock::Dump; // BlendMode code snippets case SkBuiltInCodeSnippetID::kSimpleBlendMode: return BlendModeBlock::Dump; default: return dump_unknown_block; } } } // anonymous namespace int SkPaintParamsKey::DumpBlock(const SkPaintParamsKey& key, int headerOffset) { auto [codeSnippetID, blockSize] = key.readCodeSnippetID(headerOffset); get_dump_method(codeSnippetID)(key, headerOffset); return blockSize; } // This just iterates over the top-level blocks calling block-specific dump methods. void SkPaintParamsKey::dump() const { SkDebugf("SkPaintParamsKey %dB:\n", this->sizeInBytes()); int curHeaderOffset = 0; while (curHeaderOffset < this->sizeInBytes()) { int blockSize = DumpBlock(*this, curHeaderOffset); curHeaderOffset += blockSize; } } #endif int SkPaintParamsKey::AddBlockToShaderInfo(SkShaderCodeDictionary* dict, const SkPaintParamsKey& key, int headerOffset, SkShaderInfo* result) { auto [codeSnippetID, blockSize] = key.readCodeSnippetID(headerOffset); if (codeSnippetID != SkBuiltInCodeSnippetID::kSimpleBlendMode) { auto entry = dict->getEntry(codeSnippetID); result->add(*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); int childBlockSize = AddBlockToShaderInfo(dict, key, childOffset, result); childOffset += childBlockSize; } if (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; } }