/* * 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/gpu/graphite/PaintParamsKey.h" #include "src/base/SkArenaAlloc.h" #include "src/base/SkStringView.h" #include "src/gpu/graphite/KeyHelpers.h" #include "src/gpu/graphite/Log.h" #include "src/gpu/graphite/ShaderCodeDictionary.h" using namespace skia_private; namespace skgpu::graphite { //-------------------------------------------------------------------------------------------------- // PaintParamsKeyBuilder #ifdef SK_DEBUG void PaintParamsKeyBuilder::checkReset() { SkASSERT(!fLocked); SkASSERT(fData.empty()); SkASSERT(fStack.empty()); } void PaintParamsKeyBuilder::pushStack(int32_t codeSnippetID) { SkASSERT(fDict->isValidID(codeSnippetID)); if (!fStack.empty()) { fStack.back().fNumActualChildren++; SkASSERT(fStack.back().fNumActualChildren <= fStack.back().fNumExpectedChildren); } const ShaderSnippet* snippet = fDict->getEntry(codeSnippetID); fStack.push_back({codeSnippetID, snippet->fNumChildren}); } void PaintParamsKeyBuilder::popStack() { SkASSERT(!fStack.empty()); SkASSERT(fStack.back().fNumActualChildren == fStack.back().fNumExpectedChildren); fStack.pop_back(); } #endif // SK_DEBUG //-------------------------------------------------------------------------------------------------- // PaintParamsKey PaintParamsKey PaintParamsKey::clone(SkArenaAlloc* arena) const { int32_t* newData = arena->makeArrayDefault(fData.size()); memcpy(newData, fData.data(), fData.size_bytes()); return PaintParamsKey({newData, fData.size()}); } const ShaderNode* PaintParamsKey::createNode(const ShaderCodeDictionary* dict, int* currentIndex, SkArenaAlloc* arena) const { SkASSERT(*currentIndex < SkTo(fData.size())); const int32_t index = (*currentIndex)++; const int32_t id = fData[index]; const ShaderSnippet* entry = dict->getEntry(id); if (!entry) { SKGPU_LOG_E("Unknown snippet ID in key: %d", id); return nullptr; } const ShaderNode** childArray = arena->makeArray(entry->fNumChildren); for (int i = 0; i < entry->fNumChildren; ++i) { const ShaderNode* child = this->createNode(dict, currentIndex, arena); if (!child) { return nullptr; } childArray[i] = child; } return arena->make(entry, SkSpan(childArray, entry->fNumChildren), id, index); } SkSpan PaintParamsKey::getRootNodes(const ShaderCodeDictionary* dict, SkArenaAlloc* arena) const { // TODO: Once the PaintParamsKey creation is organized to represent a single tree starting at // the final blend, there will only be a single root node and this can be simplified. // For now, we don't know how many roots there are, so collect them into a local array before // copying into the arena. const int keySize = SkTo(fData.size()); // Normal PaintParams creation will have up to 7 roots for the different stages. STArray<7, const ShaderNode*> roots; int currentIndex = 0; while (currentIndex < keySize) { const ShaderNode* root = this->createNode(dict, ¤tIndex, arena); if (!root) { return {}; // a bad key } roots.push_back(root); } // Copy the accumulated roots into a span stored in the arena const ShaderNode** rootSpan = arena->makeArray(roots.size()); memcpy(rootSpan, roots.data(), roots.size_bytes()); return SkSpan(rootSpan, roots.size()); } static int key_to_string(SkString* str, const ShaderCodeDictionary* dict, SkSpan keyData, int currentIndex) { SkASSERT(currentIndex < SkTo(keyData.size())); int32_t id = keyData[currentIndex++]; auto entry = dict->getEntry(id); if (!entry) { str->append("UnknownCodeSnippetID:"); str->appendS32(id); str->append(" "); return currentIndex; } std::string_view name = entry->fName; if (skstd::ends_with(name, "Shader")) { name.remove_suffix(6); } str->append(name); if (entry->fNumChildren > 0) { str->append(" [ "); for (int i = 0; i < entry->fNumChildren; ++i) { currentIndex = key_to_string(str, dict, keyData, currentIndex); } str->append("]"); } str->append(" "); return currentIndex; } SkString PaintParamsKey::toString(const ShaderCodeDictionary* dict) const { SkString str; const int keySize = SkTo(fData.size()); for (int currentIndex = 0; currentIndex < keySize; ) { currentIndex = key_to_string(&str, dict, fData, currentIndex); } return str.isEmpty() ? SkString("(empty)") : str; } #ifdef SK_DEBUG static int dump_node(const ShaderCodeDictionary* dict, SkSpan keyData, int currentIndex, int indent) { SkASSERT(currentIndex < SkTo(keyData.size())); SkDebugf("%*c", 2 * indent, ' '); int32_t id = keyData[currentIndex++]; auto entry = dict->getEntry(id); if (!entry) { SkDebugf("[%d] unknown block!\n", id); return currentIndex; } SkDebugf("[%d] %s\n", id, entry->fStaticFunctionName); for (int i = 0; i < entry->fNumChildren; ++i) { currentIndex = dump_node(dict, keyData, currentIndex, indent + 1); } return currentIndex; } void PaintParamsKey::dump(const ShaderCodeDictionary* dict, UniquePaintParamsID id) const { const int keySize = SkTo(fData.size()); SkDebugf("--------------------------------------\n"); SkDebugf("%u PaintParamsKey (keySize: %d):\n", id.asUInt(), keySize); int currentIndex = 0; while (currentIndex < keySize) { currentIndex = dump_node(dict, fData, currentIndex, 1); } } #endif // SK_DEBUG } // namespace skgpu::graphite