[graphite] Assign indices to pipeline descriptions

Also adds a helper class to upload uniform data the first time it's
seen, and then remember the BufferBindInfo for use after sorting.

Cq-Include-Trybots: luci.skia.skia.primary:Test-Mac11-Clang-MacMini9.1-GPU-AppleM1-arm64-Release-All-Graphite,Test-Mac11-Clang-MacMini9.1-GPU-AppleM1-arm64-Debug-All-ASAN_Graphite,Build-Mac-Clang-arm64-Release-iOS_Graphite,Build-Mac-Clang-arm64-Release-Graphite,Build-Mac-Clang-arm64-Debug-iOS_Graphite,Build-Mac-Clang-arm64-Debug-Graphite_NoGpu,Build-Mac-Clang-arm64-Debug-Graphite,Build-Mac-Clang-arm64-Debug-ASAN_Graphite
Bug: skia:12466
Change-Id: I922166a24c5f8417020c0a3288cddf6043573c79
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/475637
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
Reviewed-by: Jim Van Verth <jvanverth@google.com>
This commit is contained in:
Michael Ludwig 2021-11-29 19:50:39 -05:00 committed by SkCQ
parent 813d84f69a
commit 660cc2a38f
3 changed files with 83 additions and 46 deletions

View File

@ -24,6 +24,7 @@
#include "experimental/graphite/src/geom/BoundsManager.h"
#include "src/core/SkMathPriv.h"
#include "src/core/SkTBlockList.h"
#include "src/core/SkUtils.h"
#include "src/gpu/BufferWriter.h"
@ -151,11 +152,53 @@ public:
namespace {
skgpu::UniformData* lookup(skgpu::Recorder* recorder, uint32_t uniformID) {
// TODO: just return a raw 'UniformData*' here
sk_sp<skgpu::UniformData> tmp = recorder->uniformCache()->lookup(uniformID);
return tmp.get();
}
class UniformBindingCache {
public:
UniformBindingCache(DrawBufferManager* bufferMgr, UniformCache* cache)
: fBufferMgr(bufferMgr), fCache(cache) {}
uint32_t addUniforms(sk_sp<UniformData> data) {
if (!data) {
return UniformCache::kInvalidUniformID;
}
uint32_t index = fCache->insert(data);
if (fBindings.find(index) == fBindings.end()) {
// First time encountering this data, so upload to the GPU
auto [writer, bufferInfo] = fBufferMgr->getUniformWriter(data->dataSize());
writer.write(data->data(), data->dataSize());
fBindings.insert({index, bufferInfo});
}
return index;
}
BindBufferInfo getBinding(uint32_t uniformIndex) {
auto lookup = fBindings.find(uniformIndex);
SkASSERT(lookup != fBindings.end());
return lookup->second;
}
private:
DrawBufferManager* fBufferMgr;
UniformCache* fCache;
std::unordered_map<uint32_t, BindBufferInfo> fBindings;
};
// std::unordered_map implementation for GraphicsPipelineDesc* that de-reference the pointers.
struct Hash {
size_t operator()(const skgpu::GraphicsPipelineDesc* desc) const noexcept {
return skgpu::GraphicsPipelineDesc::Hash()(*desc);
}
};
struct Eq {
bool operator()(const skgpu::GraphicsPipelineDesc* a,
const skgpu::GraphicsPipelineDesc* b) const noexcept {
return *a == *b;
}
};
} // anonymous namespace
@ -199,7 +242,11 @@ std::unique_ptr<DrawPass> DrawPass::Make(Recorder* recorder,
DrawBufferManager* bufferMgr = recorder->drawBufferManager();
UniformCache geometryUniforms;
std::unordered_map<uint32_t, BindBufferInfo> geometryUniformBindings;
UniformBindingCache geometryUniformBindings(bufferMgr, &geometryUniforms);
UniformBindingCache shadingUniformBindings(bufferMgr, recorder->uniformCache());
SkTBlockList<GraphicsPipelineDesc> pipelineDescs; // pointers to items will not move
std::unordered_map<const GraphicsPipelineDesc*, uint32_t, Hash, Eq> pipelineDescToIndex;
std::vector<SortKey> keys;
keys.reserve(draws->renderStepCount()); // will not exceed but may use less with occluded draws
@ -218,7 +265,7 @@ std::unique_ptr<DrawPass> DrawPass::Make(Recorder* recorder,
uint32_t shadingIndex = UniformCache::kInvalidUniformID;
if (draw.fPaintParams.has_value()) {
std::tie(shader, shadingUniforms) = ExtractCombo(draw.fPaintParams.value());
shadingIndex = recorder->uniformCache()->insert(shadingUniforms);
shadingIndex = shadingUniformBindings.addUniforms(shadingUniforms);
} // else depth-only
for (int stepIndex = 0; stepIndex < draw.fRenderer.numRenderSteps(); ++stepIndex) {
@ -235,22 +282,23 @@ std::unique_ptr<DrawPass> DrawPass::Make(Recorder* recorder,
uint32_t geometryIndex = UniformCache::kInvalidUniformID;
if (step->numUniforms() > 0) {
// TODO: Get layout from the GPU
sk_sp<UniformData> uniforms = step->writeUniforms(Layout::kMetal, draw.fShape);
geometryIndex = geometryUniforms.insert(uniforms);
// Upload the data to the GPU if it's the first time encountered
if (geometryUniformBindings.find(geometryIndex) == geometryUniformBindings.end()) {
auto [writer, bufferInfo] = bufferMgr->getUniformWriter(uniforms->dataSize());
writer.write(uniforms->data(), uniforms->dataSize());
geometryUniformBindings.insert({geometryIndex, bufferInfo});
}
geometryIndex = geometryUniformBindings.addUniforms(
step->writeUniforms(Layout::kMetal, draw.fShape));
}
GraphicsPipelineDesc desc;
desc.setProgram(step, stepShader);
uint32_t pipelineIndex = 0;
// TODO: Have a map from descriptions to uint32_ts for the SortKey
auto pipelineLookup = pipelineDescToIndex.find(&desc);
if (pipelineLookup == pipelineDescToIndex.end()) {
// Assign new index to first appearance of this pipeline description
pipelineIndex = SkTo<uint32_t>(pipelineDescs.count());
const GraphicsPipelineDesc& finalDesc = pipelineDescs.push_back(desc);
pipelineDescToIndex.insert({&finalDesc, pipelineIndex});
} else {
// Reuse the existing pipeline description for better batching after sorting
pipelineIndex = pipelineLookup->second;
}
keys.push_back({&draw, stepIndex, pipelineIndex, geometryIndex, stepShadingIndex});
}
@ -301,7 +349,7 @@ std::unique_ptr<DrawPass> DrawPass::Make(Recorder* recorder,
// Make state changes before accumulating new draw data
if (pipelineChange) {
// TODO: Look up pipeline description from key's index and record binding it
// TODO: Record binding of the pipeline index == key.pipeline()
lastPipeline = key.pipeline();
lastShadingUniforms = UniformCache::kInvalidUniformID;
lastGeometryUniforms = UniformCache::kInvalidUniformID;
@ -309,22 +357,16 @@ std::unique_ptr<DrawPass> DrawPass::Make(Recorder* recorder,
if (stateChange) {
if (key.geometryUniforms() != lastGeometryUniforms) {
if (key.geometryUniforms() != UniformCache::kInvalidUniformID) {
auto binding = geometryUniformBindings.find(key.geometryUniforms())->second;
auto binding = geometryUniformBindings.getBinding(key.geometryUniforms());
// TODO: Record bind 'binding' buffer + offset to kRenderStep slot
(void) binding;
}
lastGeometryUniforms = key.geometryUniforms();
}
if (key.shadingUniforms() != lastShadingUniforms) {
// TODO: We should not re-upload the uniforms for every draw that referenced them,
// they should be uploaded first time seen in the earlier loop of DrawPass::Make and
// then this can lookup the cached BindBufferInfo, similar to geometryUniformBinding
auto ud = lookup(recorder, key.shadingUniforms());
auto [writer, bufferInfo] = bufferMgr->getUniformWriter(ud->dataSize());
writer.write(ud->data(), ud->dataSize());
// TODO: recording 'bufferInfo' somewhere to allow a later uniform bind call
auto binding = shadingUniformBindings.getBinding(key.shadingUniforms());
// TODO: Record bind 'binding' buffer + offset to kPaint slot
(void) binding;
lastShadingUniforms = key.shadingUniforms();
}
if (draw.fClip.scissor() != lastScissor) {

View File

@ -13,6 +13,8 @@
#include "experimental/graphite/src/Attribute.h"
#include "experimental/graphite/src/ContextUtils.h"
#include "experimental/graphite/src/DrawTypes.h"
#include "include/core/SkSpan.h"
#include "include/private/SkOpts_spi.h"
#include "include/private/SkTArray.h"
#include <array>
@ -28,16 +30,7 @@ class GraphicsPipelineDesc {
public:
GraphicsPipelineDesc();
// Returns this as a uint32_t array to be used as a key in the pipeline cache.
// TODO: Do we want to do anything here with a tuple or an SkSpan?
const uint32_t* asKey() const {
return fKey.data();
}
// Gets the number of bytes in asKey(). It will be a 4-byte aligned value.
uint32_t keyLength() const {
return fKey.size() * sizeof(uint32_t);
}
SkSpan<const uint32_t> asKey() const { return SkMakeSpan(fKey.data(), fKey.size()); }
bool operator==(const GraphicsPipelineDesc& that) const {
return this->fKey == that.fKey;
@ -63,6 +56,12 @@ public:
fKey[kWords - 1] = shaderCombo.key();
}
struct Hash {
uint32_t operator()(const GraphicsPipelineDesc& desc) const {
return SkOpts::hash_fn(desc.fKey.data(), desc.fKey.size() * sizeof(uint32_t), 0);
}
};
private:
// The key is the RenderStep address and the uint32_t key from Combination
static constexpr int kWords = sizeof(uintptr_t) / sizeof(uint32_t) + 1;

View File

@ -57,13 +57,9 @@ private:
private:
struct Entry;
struct DescHash {
uint32_t operator()(const GraphicsPipelineDesc& desc) const {
return SkOpts::hash_fn(desc.asKey(), desc.keyLength(), 0);
}
};
SkLRUCache<const GraphicsPipelineDesc, std::unique_ptr<Entry>, DescHash> fMap;
SkLRUCache<const GraphicsPipelineDesc,
std::unique_ptr<Entry>,
GraphicsPipelineDesc::Hash> fMap;
ResourceProvider* fResourceProvider;
};