blob: 2bc4210c0ea4af0418c709da3692a7c1f5d9019d [file] [log] [blame]
/*
* Copyright 2016 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "GrVkResourceProvider.h"
#include "GrVkGpu.h"
#include "GrProcessor.h"
#include "GrRenderTargetPriv.h" // TODO: remove once refPipelineState gets passed stencil settings.
#include "GrVkPipelineState.h"
#include "GrVkPipelineStateBuilder.h"
#include "SkOpts.h"
#include "glsl/GrGLSLFragmentProcessor.h"
#include "glsl/GrGLSLProgramDataManager.h"
#ifdef GR_PIPELINE_STATE_CACHE_STATS
// Display pipeline state cache usage
static const bool c_DisplayVkPipelineCache{false};
#endif
struct GrVkResourceProvider::PipelineStateCache::Entry {
Entry() : fPipelineState(nullptr) {}
static const GrVkPipelineState::Desc& GetKey(const Entry* entry) {
return entry->fPipelineState->getDesc();
}
static uint32_t Hash(const GrVkPipelineState::Desc& key) {
return key.getChecksum();
}
sk_sp<GrVkPipelineState> fPipelineState;
private:
SK_DECLARE_INTERNAL_LLIST_INTERFACE(Entry);
};
GrVkResourceProvider::PipelineStateCache::PipelineStateCache(GrVkGpu* gpu)
: fCount(0)
, fGpu(gpu)
#ifdef GR_PIPELINE_STATE_CACHE_STATS
, fTotalRequests(0)
, fCacheMisses(0)
#endif
{}
GrVkResourceProvider::PipelineStateCache::~PipelineStateCache() {
SkASSERT(0 == fCount);
// dump stats
#ifdef GR_PIPELINE_STATE_CACHE_STATS
if (c_DisplayVkPipelineCache) {
SkDebugf("--- Pipeline State Cache ---\n");
SkDebugf("Total requests: %d\n", fTotalRequests);
SkDebugf("Cache misses: %d\n", fCacheMisses);
SkDebugf("Cache miss %%: %f\n", (fTotalRequests > 0) ?
100.f * fCacheMisses / fTotalRequests :
0.f);
SkDebugf("---------------------\n");
}
#endif
}
void GrVkResourceProvider::PipelineStateCache::reset() {
fHashTable.foreach([](Entry** entry) {
delete *entry;
});
fHashTable.reset();
fCount = 0;
}
void GrVkResourceProvider::PipelineStateCache::abandon() {
fHashTable.foreach([](Entry** entry) {
SkASSERT((*entry)->fPipelineState.get());
(*entry)->fPipelineState->abandonGPUResources();
});
this->reset();
}
void GrVkResourceProvider::PipelineStateCache::release() {
fHashTable.foreach([this](Entry** entry) {
SkASSERT((*entry)->fPipelineState.get());
(*entry)->fPipelineState->freeGPUResources(fGpu);
});
this->reset();
}
sk_sp<GrVkPipelineState> GrVkResourceProvider::PipelineStateCache::refPipelineState(
const GrPipeline& pipeline,
const GrPrimitiveProcessor& primProc,
GrPrimitiveType primitiveType,
const GrVkRenderPass& renderPass) {
#ifdef GR_PIPELINE_STATE_CACHE_STATS
++fTotalRequests;
#endif
GrStencilSettings stencil;
if (pipeline.isStencilEnabled()) {
GrRenderTarget* rt = pipeline.getRenderTarget();
// TODO: attach stencil and create settings during render target flush.
SkASSERT(rt->renderTargetPriv().getStencilAttachment());
stencil.reset(*pipeline.getUserStencil(), pipeline.hasStencilClip(),
rt->renderTargetPriv().numStencilBits());
}
// Get GrVkProgramDesc
GrVkPipelineState::Desc desc;
if (!GrVkPipelineState::Desc::Build(&desc, primProc, pipeline, stencil,
primitiveType, *fGpu->vkCaps().glslCaps())) {
GrCapsDebugf(fGpu->caps(), "Failed to build vk program descriptor!\n");
return nullptr;
}
desc.finalize();
Entry* entry = nullptr;
if (Entry** entryptr = fHashTable.find(desc)) {
SkASSERT(*entryptr);
entry = *entryptr;
}
if (!entry) {
#ifdef GR_PIPELINE_STATE_CACHE_STATS
++fCacheMisses;
#endif
sk_sp<GrVkPipelineState> pipelineState(
GrVkPipelineStateBuilder::CreatePipelineState(fGpu,
pipeline,
stencil,
primProc,
primitiveType,
desc,
renderPass));
if (nullptr == pipelineState) {
return nullptr;
}
if (fCount < kMaxEntries) {
entry = new Entry;
fCount++;
} else {
SkASSERT(fCount == kMaxEntries);
entry = fLRUList.head();
fLRUList.remove(entry);
entry->fPipelineState->freeGPUResources(fGpu);
fHashTable.remove(entry->fPipelineState->getDesc());
}
entry->fPipelineState = std::move(pipelineState);
fHashTable.set(entry);
fLRUList.addToTail(entry);
return entry->fPipelineState;
} else {
fLRUList.remove(entry);
fLRUList.addToTail(entry);
}
return entry->fPipelineState;
}