| /* | |
| * Copyright 2019 Google Inc. | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "include/gpu/ganesh/GrRecordingContext.h" | |
| #include "include/core/SkString.h" | |
| #include "include/core/SkTypes.h" | |
| #include "include/gpu/GpuTypes.h" | |
| #include "include/gpu/ganesh/GrBackendSurface.h" | |
| #include "include/gpu/ganesh/GrContextOptions.h" | |
| #include "include/gpu/ganesh/GrContextThreadSafeProxy.h" | |
| #include "include/gpu/ganesh/GrTypes.h" | |
| #include "include/private/base/SkDebug.h" | |
| #include "include/private/base/SkMacros.h" | |
| #include "include/private/gpu/ganesh/GrTypesPriv.h" | |
| #include "src/base/SkArenaAlloc.h" | |
| #include "src/core/SkCPUContextImpl.h" | |
| #include "src/core/SkCPURecorderImpl.h" | |
| #include "src/gpu/ganesh/GrAuditTrail.h" | |
| #include "src/gpu/ganesh/GrCaps.h" | |
| #include "src/gpu/ganesh/GrContextThreadSafeProxyPriv.h" | |
| #include "src/gpu/ganesh/GrDrawingManager.h" | |
| #include "src/gpu/ganesh/GrProgramDesc.h" | |
| #include "src/gpu/ganesh/GrProxyProvider.h" | |
| #include "src/gpu/ganesh/PathRendererChain.h" | |
| #include "src/gpu/ganesh/SkGaneshRecorder.h" | |
| #include "src/gpu/ganesh/ops/AtlasTextOp.h" | |
| #include "src/text/gpu/SubRunAllocator.h" | |
| #include "src/text/gpu/TextBlobRedrawCoordinator.h" | |
| #include <utility> | |
| using namespace skia_private; | |
| using TextBlobRedrawCoordinator = sktext::gpu::TextBlobRedrawCoordinator; | |
| GrRecordingContext::ProgramData::ProgramData(std::unique_ptr<const GrProgramDesc> desc, | |
| const GrProgramInfo* info) | |
| : fDesc(std::move(desc)) | |
| , fInfo(info) { | |
| } | |
| GrRecordingContext::ProgramData::ProgramData(ProgramData&& other) | |
| : fDesc(std::move(other.fDesc)) | |
| , fInfo(other.fInfo) { | |
| } | |
| GrRecordingContext::ProgramData::~ProgramData() = default; | |
| GrRecordingContext::GrRecordingContext(sk_sp<GrContextThreadSafeProxy> proxy, bool ddlRecording) | |
| : GrImageContext(std::move(proxy)) | |
| , fAuditTrail(new GrAuditTrail()) | |
| , fArenas(ddlRecording) { | |
| fProxyProvider = std::make_unique<GrProxyProvider>(this); | |
| fCPUContext = std::make_unique<skcpu::ContextImpl>(); | |
| fRecorder = std::make_unique<SkGaneshRecorder>(this); | |
| } | |
| GrRecordingContext::~GrRecordingContext() { | |
| skgpu::ganesh::AtlasTextOp::ClearCache(); | |
| } | |
| bool GrRecordingContext::init() { | |
| if (!GrImageContext::init()) { | |
| return false; | |
| } | |
| skgpu::ganesh::PathRendererChain::Options prcOptions; | |
| prcOptions.fAllowPathMaskCaching = this->options().fAllowPathMaskCaching; | |
| #if defined(GPU_TEST_UTILS) | |
| prcOptions.fGpuPathRenderers = this->options().fGpuPathRenderers; | |
| #endif | |
| // FIXME: Once this is removed from Chrome and Android, rename to fEnable"". | |
| if (this->options().fDisableDistanceFieldPaths) { | |
| prcOptions.fGpuPathRenderers &= ~GpuPathRenderers::kSmall; | |
| } | |
| bool reduceOpsTaskSplitting = true; | |
| if (this->caps()->avoidReorderingRenderTasks()) { | |
| reduceOpsTaskSplitting = false; | |
| } else if (GrContextOptions::Enable::kYes == this->options().fReduceOpsTaskSplitting) { | |
| reduceOpsTaskSplitting = true; | |
| } else if (GrContextOptions::Enable::kNo == this->options().fReduceOpsTaskSplitting) { | |
| reduceOpsTaskSplitting = false; | |
| } | |
| fDrawingManager.reset(new GrDrawingManager(this, | |
| prcOptions, | |
| reduceOpsTaskSplitting)); | |
| return true; | |
| } | |
| void GrRecordingContext::abandonContext() { | |
| GrImageContext::abandonContext(); | |
| this->destroyDrawingManager(); | |
| } | |
| GrDrawingManager* GrRecordingContext::drawingManager() { | |
| return fDrawingManager.get(); | |
| } | |
| void GrRecordingContext::destroyDrawingManager() { | |
| fDrawingManager.reset(); | |
| } | |
| GrRecordingContext::Arenas::Arenas(SkArenaAlloc* recordTimeAllocator, | |
| sktext::gpu::SubRunAllocator* subRunAllocator) | |
| : fRecordTimeAllocator(recordTimeAllocator) | |
| , fRecordTimeSubRunAllocator(subRunAllocator) { | |
| // OwnedArenas should instantiate these before passing the bare pointer off to this struct. | |
| SkASSERT(subRunAllocator); | |
| } | |
| // Must be defined here so that std::unique_ptr can see the sizes of the various pools, otherwise | |
| // it can't generate a default destructor for them. | |
| GrRecordingContext::OwnedArenas::OwnedArenas(bool ddlRecording) : fDDLRecording(ddlRecording) {} | |
| GrRecordingContext::OwnedArenas::~OwnedArenas() {} | |
| GrRecordingContext::OwnedArenas& GrRecordingContext::OwnedArenas::operator=(OwnedArenas&& a) { | |
| fDDLRecording = a.fDDLRecording; | |
| fRecordTimeAllocator = std::move(a.fRecordTimeAllocator); | |
| fRecordTimeSubRunAllocator = std::move(a.fRecordTimeSubRunAllocator); | |
| return *this; | |
| } | |
| GrRecordingContext::Arenas GrRecordingContext::OwnedArenas::get() { | |
| if (!fRecordTimeAllocator && fDDLRecording) { | |
| // TODO: empirically determine a better number for SkArenaAlloc's firstHeapAllocation param | |
| fRecordTimeAllocator = std::make_unique<SkArenaAlloc>(1024); | |
| } | |
| if (!fRecordTimeSubRunAllocator) { | |
| fRecordTimeSubRunAllocator = std::make_unique<sktext::gpu::SubRunAllocator>(); | |
| } | |
| return {fRecordTimeAllocator.get(), fRecordTimeSubRunAllocator.get()}; | |
| } | |
| GrRecordingContext::OwnedArenas&& GrRecordingContext::detachArenas() { | |
| return std::move(fArenas); | |
| } | |
| TextBlobRedrawCoordinator* GrRecordingContext::getTextBlobRedrawCoordinator() { | |
| return fThreadSafeProxy->priv().getTextBlobRedrawCoordinator(); | |
| } | |
| const TextBlobRedrawCoordinator* GrRecordingContext::getTextBlobRedrawCoordinator() const { | |
| return fThreadSafeProxy->priv().getTextBlobRedrawCoordinator(); | |
| } | |
| GrThreadSafeCache* GrRecordingContext::threadSafeCache() { | |
| return fThreadSafeProxy->priv().threadSafeCache(); | |
| } | |
| const GrThreadSafeCache* GrRecordingContext::threadSafeCache() const { | |
| return fThreadSafeProxy->priv().threadSafeCache(); | |
| } | |
| void GrRecordingContext::addOnFlushCallbackObject(GrOnFlushCallbackObject* onFlushCBObject) { | |
| this->drawingManager()->addOnFlushCallbackObject(onFlushCBObject); | |
| } | |
| //////////////////////////////////////////////////////////////////////////////// | |
| sk_sp<const SkCapabilities> GrRecordingContext::skCapabilities() const { | |
| return this->refCaps(); | |
| } | |
| int GrRecordingContext::maxTextureSize() const { return this->caps()->maxTextureSize(); } | |
| int GrRecordingContext::maxRenderTargetSize() const { return this->caps()->maxRenderTargetSize(); } | |
| bool GrRecordingContext::colorTypeSupportedAsImage(SkColorType colorType) const { | |
| GrBackendFormat format = | |
| this->caps()->getDefaultBackendFormat(SkColorTypeToGrColorType(colorType), | |
| GrRenderable::kNo); | |
| return format.isValid(); | |
| } | |
| bool GrRecordingContext::supportsProtectedContent() const { | |
| return this->caps()->supportsProtectedContent(); | |
| } | |
| std::unique_ptr<skcpu::Recorder> GrRecordingContext::makeCPURecorder() { | |
| return std::make_unique<skcpu::RecorderImpl>(fCPUContext.get()); | |
| } | |
| SkRecorder* GrRecordingContext::asRecorder() { | |
| return fRecorder.get(); | |
| } | |
| /////////////////////////////////////////////////////////////////////////////////////////////////// | |
| #ifdef SK_ENABLE_DUMP_GPU | |
| #include "src/utils/SkJSONWriter.h" | |
| void GrRecordingContext::dumpJSON(SkJSONWriter* writer) const { | |
| writer->beginObject(); | |
| #if GR_GPU_STATS | |
| writer->appendS32("path_masks_generated", this->stats()->numPathMasksGenerated()); | |
| writer->appendS32("path_mask_cache_hits", this->stats()->numPathMaskCacheHits()); | |
| #endif | |
| writer->endObject(); | |
| } | |
| #else | |
| void GrRecordingContext::dumpJSON(SkJSONWriter*) const { } | |
| #endif | |
| #if defined(GPU_TEST_UTILS) | |
| #if GR_GPU_STATS | |
| void GrRecordingContext::Stats::dump(SkString* out) const { | |
| out->appendf("Num Path Masks Generated: %d\n", fNumPathMasksGenerated); | |
| out->appendf("Num Path Mask Cache Hits: %d\n", fNumPathMaskCacheHits); | |
| } | |
| void GrRecordingContext::Stats::dumpKeyValuePairs(TArray<SkString>* keys, | |
| TArray<double>* values) const { | |
| keys->push_back(SkString("path_masks_generated")); | |
| values->push_back(fNumPathMasksGenerated); | |
| keys->push_back(SkString("path_mask_cache_hits")); | |
| values->push_back(fNumPathMaskCacheHits); | |
| } | |
| void GrRecordingContext::DMSAAStats::dumpKeyValuePairs(TArray<SkString>* keys, | |
| TArray<double>* values) const { | |
| keys->push_back(SkString("dmsaa_render_passes")); | |
| values->push_back(fNumRenderPasses); | |
| keys->push_back(SkString("dmsaa_multisample_render_passes")); | |
| values->push_back(fNumMultisampleRenderPasses); | |
| for (const auto& [name, count] : fTriggerCounts) { | |
| keys->push_back(SkStringPrintf("dmsaa_trigger_%s", name.c_str())); | |
| values->push_back(count); | |
| } | |
| } | |
| void GrRecordingContext::DMSAAStats::dump() const { | |
| SkDebugf("DMSAA Render Passes: %d\n", fNumRenderPasses); | |
| SkDebugf("DMSAA Multisample Render Passes: %d\n", fNumMultisampleRenderPasses); | |
| if (!fTriggerCounts.empty()) { | |
| SkDebugf("DMSAA Triggers:\n"); | |
| for (const auto& [name, count] : fTriggerCounts) { | |
| SkDebugf(" %s: %d\n", name.c_str(), count); | |
| } | |
| } | |
| } | |
| void GrRecordingContext::DMSAAStats::merge(const DMSAAStats& stats) { | |
| fNumRenderPasses += stats.fNumRenderPasses; | |
| fNumMultisampleRenderPasses += stats.fNumMultisampleRenderPasses; | |
| for (const auto& [name, count] : stats.fTriggerCounts) { | |
| fTriggerCounts[name] += count; | |
| } | |
| } | |
| #endif // GR_GPU_STATS | |
| #endif // defined(GPU_TEST_UTILS) |