Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
Classes | Public Member Functions | Static Public Member Functions | List of all members
skgpu::graphite::DrawPass Class Reference

#include <DrawPass.h>

Classes

class  SortKey
 

Public Member Functions

 ~DrawPass ()
 
const SkIRectbounds () const
 
TextureProxytarget () const
 
std::pair< LoadOp, StoreOpops () const
 
std::array< float, 4 > clearColor () const
 
bool requiresDstTexture () const
 
bool requiresMSAA () const
 
SkEnumBitMask< DepthStencilFlagsdepthStencilFlags () const
 
size_t vertexBufferSize () const
 
size_t uniformBufferSize () const
 
bool prepareResources (ResourceProvider *, const RuntimeEffectDictionary *, const RenderPassDesc &)
 
DrawPassCommands::List::Iter commands () const
 
const GraphicsPipelinegetPipeline (size_t index) const
 
const TexturegetTexture (size_t index) const
 
const SamplergetSampler (size_t index) const
 
skia_private::TArray< sk_sp< TextureProxy > > sampledTextures () const
 
void addResourceRefs (CommandBuffer *) const
 

Static Public Member Functions

static std::unique_ptr< DrawPassMake (Recorder *, std::unique_ptr< DrawList >, sk_sp< TextureProxy > target, const SkImageInfo &targetInfo, std::pair< LoadOp, StoreOp >, std::array< float, 4 > clearColor)
 

Detailed Description

DrawPass is analogous to a subpass, storing the drawing operations in the order they are stored in the eventual command buffer, as well as the surface proxy the operations are intended for. DrawPasses are grouped into a RenderPassTask for execution within a single render pass if the subpasses are compatible with each other.

Unlike DrawList, DrawPasses are immutable and represent as closely as possible what will be stored in the command buffer while being flexible as to how the pass is incorporated. Depending on the backend, it may even be able to write accumulated vertex and uniform data directly to mapped GPU memory, although that is the extent of the CPU->GPU work they perform before they are executed by a RenderPassTask.

Definition at line 53 of file DrawPass.h.

Constructor & Destructor Documentation

◆ ~DrawPass()

skgpu::graphite::DrawPass::~DrawPass ( )
default

Member Function Documentation

◆ addResourceRefs()

void skgpu::graphite::DrawPass::addResourceRefs ( CommandBuffer commandBuffer) const

Definition at line 744 of file DrawPass.cpp.

744 {
745 for (int i = 0; i < fFullPipelines.size(); ++i) {
746 commandBuffer->trackResource(fFullPipelines[i]);
747 }
748 for (int i = 0; i < fSampledTextures.size(); ++i) {
749 commandBuffer->trackCommandBufferResource(fSampledTextures[i]->refTexture());
750 }
751 for (int i = 0; i < fSamplers.size(); ++i) {
752 commandBuffer->trackResource(fSamplers[i]);
753 }
754}

◆ bounds()

const SkIRect & skgpu::graphite::DrawPass::bounds ( ) const
inline

Definition at line 66 of file DrawPass.h.

66{ return fBounds; }

◆ clearColor()

std::array< float, 4 > skgpu::graphite::DrawPass::clearColor ( ) const
inline

Definition at line 69 of file DrawPass.h.

69{ return fClearColor; }

◆ commands()

DrawPassCommands::List::Iter skgpu::graphite::DrawPass::commands ( ) const
inline

Definition at line 86 of file DrawPass.h.

86 {
87 return fCommandList.commands();
88 }

◆ depthStencilFlags()

SkEnumBitMask< DepthStencilFlags > skgpu::graphite::DrawPass::depthStencilFlags ( ) const
inline

Definition at line 74 of file DrawPass.h.

74{ return fDepthStencilFlags; }

◆ getPipeline()

const GraphicsPipeline * skgpu::graphite::DrawPass::getPipeline ( size_t  index) const
inline

Definition at line 90 of file DrawPass.h.

90 {
91 return fFullPipelines[index].get();
92 }

◆ getSampler()

const Sampler * skgpu::graphite::DrawPass::getSampler ( size_t  index) const

Definition at line 762 of file DrawPass.cpp.

762 {
763 SkASSERT(index < SkToSizeT(fSamplers.size()));
764 SkASSERT(fSamplers[index]);
765 return fSamplers[index].get();
766}
#define SkASSERT(cond)
Definition SkAssert.h:116
constexpr size_t SkToSizeT(S x)
Definition SkTo.h:31

◆ getTexture()

const Texture * skgpu::graphite::DrawPass::getTexture ( size_t  index) const

Definition at line 756 of file DrawPass.cpp.

756 {
757 SkASSERT(index < SkToSizeT(fSampledTextures.size()));
758 SkASSERT(fSampledTextures[index]);
759 SkASSERT(fSampledTextures[index]->texture());
760 return fSampledTextures[index]->texture();
761}
FlTexture * texture

◆ Make()

std::unique_ptr< DrawPass > skgpu::graphite::DrawPass::Make ( Recorder recorder,
std::unique_ptr< DrawList draws,
sk_sp< TextureProxy target,
const SkImageInfo targetInfo,
std::pair< LoadOp, StoreOp ops,
std::array< float, 4 >  clearColor 
)
static

Definition at line 452 of file DrawPass.cpp.

457 {
458 // NOTE: This assert is here to ensure SortKey is as tightly packed as possible. Any change to
459 // its size should be done with care and good reason. The performance of sorting the keys is
460 // heavily tied to the total size.
461 //
462 // At 24 bytes (current), sorting is about 30% slower than if SortKey could be packed into just
463 // 16 bytes. There are several ways this could be done if necessary:
464 // - Restricting the max draw count to 16k (14-bits) and only using a single index to refer to
465 // the uniform data => 8 bytes of key, 8 bytes of pointer.
466 // - Restrict the max draw count to 32k (15-bits), use a single uniform index, and steal the
467 // 4 low bits from the Draw* pointer since it's 16 byte aligned.
468 // - Compact the Draw* to an index into the original collection, although that has extra
469 // indirection and does not work as well with SkTBlockList.
470 // In pseudo tests, manipulating the pointer or having to mask out indices was about 15% slower
471 // than an 8 byte key and unmodified pointer.
472 static_assert(sizeof(DrawPass::SortKey) ==
473 SkAlignTo(16 + sizeof(void*), alignof(DrawPass::SortKey)));
474
475 TRACE_EVENT1("skia.gpu", TRACE_FUNC, "draw count", draws->fDraws.count());
476
477 // The DrawList is converted directly into the DrawPass' data structures, but once the DrawPass
478 // is returned from Make(), it is considered immutable.
479 std::unique_ptr<DrawPass> drawPass(new DrawPass(target, ops, clearColor));
480
481 Rect passBounds = Rect::InfiniteInverted();
482
483 // We don't expect the uniforms from the renderSteps to reappear multiple times across a
484 // recorder's lifetime so we only de-dupe them w/in a given DrawPass.
485 UniformDataCache geometryUniformDataCache;
486 TextureDataCache* textureDataCache = recorder->priv().textureDataCache();
487 DrawBufferManager* bufferMgr = recorder->priv().drawBufferManager();
488 if (bufferMgr->hasMappingFailed()) {
489 SKGPU_LOG_W("Buffer mapping has already failed; dropping draw pass!");
490 return nullptr;
491 }
492
493 GraphicsPipelineCache pipelineCache;
494
495 // Geometry uniforms are currently always UBO-backed.
496 const bool useStorageBuffers = recorder->priv().caps()->storageBufferPreferred();
497 const ResourceBindingRequirements& bindingReqs =
498 recorder->priv().caps()->resourceBindingRequirements();
499 Layout uniformLayout =
500 useStorageBuffers ? bindingReqs.fStorageBufferLayout : bindingReqs.fUniformBufferLayout;
501
502 UniformTracker geometryUniformTracker(useStorageBuffers);
503 UniformTracker shadingUniformTracker(useStorageBuffers);
504 TextureBindingTracker textureBindingTracker;
505
506 ShaderCodeDictionary* dict = recorder->priv().shaderCodeDictionary();
507 PaintParamsKeyBuilder builder(dict);
508
509 // The initial layout we pass here is not important as it will be re-assigned when writing
510 // shading and geometry uniforms below.
511 PipelineDataGatherer gatherer(uniformLayout);
512
513 // Copy of destination, if needed.
515 SkIPoint dstOffset;
516 if (!draws->dstCopyBounds().isEmptyNegativeOrNaN()) {
517 TRACE_EVENT_INSTANT0("skia.gpu", "DrawPass requires dst copy", TRACE_EVENT_SCOPE_THREAD);
518
519 SkIRect dstCopyPixelBounds = draws->dstCopyBounds().makeRoundOut().asSkIRect();
520 dstOffset = dstCopyPixelBounds.topLeft();
522 recorder, target, targetInfo.makeDimensions(dstCopyPixelBounds.size()), dstOffset);
523 if (!dst) {
524 SKGPU_LOG_W("Failed to copy destination for reading. Dropping draw pass!");
525 return nullptr;
526 }
527 }
528
529 std::vector<SortKey> keys;
530 keys.reserve(draws->renderStepCount());
531
532 for (const DrawList::Draw& draw : draws->fDraws.items()) {
533 // If we have two different descriptors, such that the uniforms from the PaintParams can be
534 // bound independently of those used by the rest of the RenderStep, then we can upload now
535 // and remember the location for re-use on any RenderStep that does shading.
536 UniquePaintParamsID shaderID;
537 const UniformDataBlock* shadingUniforms = nullptr;
538 const TextureDataBlock* paintTextures = nullptr;
539 if (draw.fPaintParams.has_value()) {
540 sk_sp<TextureProxy> curDst =
541 draw.fPaintParams->dstReadRequirement() == DstReadRequirement::kTextureCopy
542 ? dst
543 : nullptr;
544 std::tie(shaderID, shadingUniforms, paintTextures) =
545 ExtractPaintData(recorder,
546 &gatherer,
547 &builder,
548 uniformLayout,
549 draw.fDrawParams.transform(),
550 draw.fPaintParams.value(),
551 curDst,
552 dstOffset,
553 targetInfo.colorInfo());
554 } // else depth-only
555
556 for (int stepIndex = 0; stepIndex < draw.fRenderer->numRenderSteps(); ++stepIndex) {
557 const RenderStep* const step = draw.fRenderer->steps()[stepIndex];
558 const bool performsShading = draw.fPaintParams.has_value() && step->performsShading();
559
560 GraphicsPipelineCache::Index pipelineIndex = pipelineCache.insert(
561 {step, performsShading ? shaderID : UniquePaintParamsID::InvalidID()});
562 auto [geometryUniforms, stepTextures] = ExtractRenderStepData(&geometryUniformDataCache,
563 textureDataCache,
564 &gatherer,
565 uniformLayout,
566 step,
567 draw.fDrawParams);
568
569 UniformCache::Index geomUniformIndex = geometryUniformTracker.trackUniforms(
570 pipelineIndex, geometryUniforms);
571 UniformCache::Index shadingUniformIndex = shadingUniformTracker.trackUniforms(
572 pipelineIndex, performsShading ? shadingUniforms : nullptr);
573 TextureBindingCache::Index textureIndex = textureBindingTracker.trackTextures(
574 performsShading ? paintTextures : nullptr, stepTextures);
575
576 keys.push_back({&draw, stepIndex, pipelineIndex,
577 geomUniformIndex, shadingUniformIndex, textureIndex});
578 }
579
580 passBounds.join(draw.fDrawParams.clip().drawBounds());
581 drawPass->fDepthStencilFlags |= draw.fRenderer->depthStencilFlags();
582 drawPass->fRequiresMSAA |= draw.fRenderer->requiresMSAA();
583 }
584
585 if (!geometryUniformTracker.writeUniforms(bufferMgr) ||
586 !shadingUniformTracker.writeUniforms(bufferMgr)) {
587 // The necessary uniform data couldn't be written to the GPU, so the DrawPass is invalid.
588 // Early out now since the next Recording snap will fail.
589 return nullptr;
590 }
591
592 // TODO: Explore sorting algorithms; in all likelihood this will be mostly sorted already, so
593 // algorithms that approach O(n) in that condition may be favorable. Alternatively, could
594 // explore radix sort that is always O(n). Brief testing suggested std::sort was faster than
595 // std::stable_sort and SkTQSort on my [ml]'s Windows desktop. Also worth considering in-place
596 // vs. algorithms that require an extra O(n) storage.
597 // TODO: It's not strictly necessary, but would a stable sort be useful or just end up hiding
598 // bugs in the DrawOrder determination code?
599 std::sort(keys.begin(), keys.end());
600
601 // Used to record vertex/instance data, buffer binds, and draw calls
602 DrawWriter drawWriter(&drawPass->fCommandList, bufferMgr);
603 GraphicsPipelineCache::Index lastPipeline = GraphicsPipelineCache::kInvalidIndex;
604 SkIRect lastScissor = SkIRect::MakeSize(targetInfo.dimensions());
605
606 SkASSERT(drawPass->fTarget->isFullyLazy() ||
607 SkIRect::MakeSize(drawPass->fTarget->dimensions()).contains(lastScissor));
608 drawPass->fCommandList.setScissor(lastScissor);
609
610 for (const SortKey& key : keys) {
611 const DrawList::Draw& draw = key.draw();
612 const RenderStep& renderStep = key.renderStep();
613
614 const bool pipelineChange = key.pipelineIndex() != lastPipeline;
615
616 const bool geomBindingChange = geometryUniformTracker.setCurrentUniforms(
617 key.pipelineIndex(), key.geometryUniformIndex());
618 const bool shadingBindingChange = shadingUniformTracker.setCurrentUniforms(
619 key.pipelineIndex(), key.shadingUniformIndex());
620 const bool textureBindingsChange = textureBindingTracker.setCurrentTextureBindings(
621 key.textureBindingIndex());
622 const SkIRect* newScissor = draw.fDrawParams.clip().scissor() != lastScissor ?
623 &draw.fDrawParams.clip().scissor() : nullptr;
624
625 const bool stateChange = geomBindingChange ||
626 shadingBindingChange ||
627 textureBindingsChange ||
628 SkToBool(newScissor);
629
630 // Update DrawWriter *before* we actually change any state so that accumulated draws from
631 // the previous state use the proper state.
632 if (pipelineChange) {
633 drawWriter.newPipelineState(renderStep.primitiveType(),
634 renderStep.vertexStride(),
635 renderStep.instanceStride());
636 } else if (stateChange) {
637 drawWriter.newDynamicState();
638 }
639
640 // Make state changes before accumulating new draw data
641 if (pipelineChange) {
642 drawPass->fCommandList.bindGraphicsPipeline(key.pipelineIndex());
643 lastPipeline = key.pipelineIndex();
644 }
645 if (stateChange) {
646 if (geomBindingChange) {
647 geometryUniformTracker.bindUniforms(UniformSlot::kRenderStep,
648 &drawPass->fCommandList);
649 }
650 if (shadingBindingChange) {
651 shadingUniformTracker.bindUniforms(UniformSlot::kPaint, &drawPass->fCommandList);
652 }
653 if (textureBindingsChange) {
654 textureBindingTracker.bindTextures(&drawPass->fCommandList);
655 }
656 if (newScissor) {
657 drawPass->fCommandList.setScissor(*newScissor);
658 lastScissor = *newScissor;
659 }
660 }
661
662 UniformCache::Index geometrySsboIndex =
663 (key.geometryUniformIndex() == UniformCache::kInvalidIndex)
664 ? 0
665 : key.geometryUniformIndex();
666 UniformCache::Index shadingSsboIndex =
667 (key.shadingUniformIndex() == UniformCache::kInvalidIndex)
668 ? 0
669 : key.shadingUniformIndex();
670 skvx::ushort2 ssboIndices = {SkToU16(geometrySsboIndex), SkToU16(shadingSsboIndex)};
671 renderStep.writeVertices(&drawWriter, draw.fDrawParams, ssboIndices);
672
673 if (bufferMgr->hasMappingFailed()) {
674 SKGPU_LOG_W("Failed to write necessary vertex/instance data for DrawPass, dropping!");
675 return nullptr;
676 }
677 }
678 // Finish recording draw calls for any collected data at the end of the loop
679 drawWriter.flush();
680
681 drawPass->fBounds = passBounds.roundOut().asSkIRect();
682
683 drawPass->fPipelineDescs = pipelineCache.detach();
684 drawPass->fSamplerDescs = textureBindingTracker.detachSamplers();
685 drawPass->fSampledTextures = textureBindingTracker.detachTextures();
686
687 TRACE_COUNTER1("skia.gpu", "# pipelines", drawPass->fPipelineDescs.size());
688 TRACE_COUNTER1("skia.gpu", "# textures", drawPass->fSampledTextures.size());
689 TRACE_COUNTER1("skia.gpu", "# commands", drawPass->fCommandList.count());
690
691 return drawPass;
692}
static int step(int x, SkScalar min, SkScalar max)
Definition BlurTest.cpp:215
#define SKGPU_LOG_W(fmt,...)
Definition Log.h:40
static constexpr size_t SkAlignTo(size_t x, size_t alignment)
Definition SkAlign.h:33
constexpr uint16_t SkToU16(S x)
Definition SkTo.h:24
static constexpr bool SkToBool(const T &x)
Definition SkTo.h:35
#define TRACE_COUNTER1(category_group, name, value)
#define TRACE_EVENT_SCOPE_THREAD
#define TRACE_FUNC
static void draw(SkCanvas *canvas, SkRect &target, int x, int y)
Definition aaclip.cpp:27
const ResourceBindingRequirements & resourceBindingRequirements() const
Definition Caps.h:143
bool storageBufferPreferred() const
Definition Caps.h:232
std::array< float, 4 > clearColor() const
Definition DrawPass.h:69
std::pair< LoadOp, StoreOp > ops() const
Definition DrawPass.h:68
TextureProxy * target() const
Definition DrawPass.h:67
const ShaderCodeDictionary * shaderCodeDictionary() const
TextureDataCache * textureDataCache()
const Caps * caps() const
DrawBufferManager * drawBufferManager()
static AI Rect InfiniteInverted()
Definition Rect.h:64
static UniquePaintParamsID InvalidID()
dst
Definition cp.py:12
TRect< Scalar > Rect
Definition rect.h:746
std::tuple< UniquePaintParamsID, const UniformDataBlock *, const TextureDataBlock * > ExtractPaintData(Recorder *recorder, PipelineDataGatherer *gatherer, PaintParamsKeyBuilder *builder, const Layout layout, const SkM44 &local2Dev, const PaintParams &p, sk_sp< TextureProxy > dstTexture, SkIPoint dstOffset, const SkColorInfo &targetColorInfo)
PipelineDataCache< UniformDataBlock > UniformDataCache
Definition Recorder.h:59
sk_sp< TextureProxy > add_copy_target_task(Recorder *recorder, sk_sp< TextureProxy > target, const SkImageInfo &targetInfo, const SkIPoint &targetOffset)
Definition DrawPass.cpp:415
std::tuple< const UniformDataBlock *, const TextureDataBlock * > ExtractRenderStepData(UniformDataCache *uniformDataCache, TextureDataCache *textureDataCache, PipelineDataGatherer *gatherer, const Layout layout, const RenderStep *step, const DrawParams &params)
PipelineDataCache< TextureDataBlock > TextureDataCache
Definition Recorder.h:60
constexpr SkISize size() const
Definition SkRect.h:172
static constexpr SkIRect MakeSize(const SkISize &size)
Definition SkRect.h:66
constexpr SkIPoint topLeft() const
Definition SkRect.h:151
bool contains(int32_t x, int32_t y) const
Definition SkRect.h:463
const SkColorInfo & colorInfo() const
SkImageInfo makeDimensions(SkISize newSize) const
SkISize dimensions() const
#define TRACE_EVENT_INSTANT0(category_group, name)
#define TRACE_EVENT1(category_group, name, arg1_name, arg1_val)

◆ ops()

std::pair< LoadOp, StoreOp > skgpu::graphite::DrawPass::ops ( ) const
inline

Definition at line 68 of file DrawPass.h.

68{ return fOps; }

◆ prepareResources()

bool skgpu::graphite::DrawPass::prepareResources ( ResourceProvider resourceProvider,
const RuntimeEffectDictionary runtimeDict,
const RenderPassDesc renderPassDesc 
)

Definition at line 694 of file DrawPass.cpp.

696 {
697 TRACE_EVENT0("skia.gpu", TRACE_FUNC);
698
699 fFullPipelines.reserve(fFullPipelines.size() + fPipelineDescs.size());
700 for (const GraphicsPipelineDesc& pipelineDesc : fPipelineDescs) {
701 auto pipeline = resourceProvider->findOrCreateGraphicsPipeline(runtimeDict,
702 pipelineDesc,
703 renderPassDesc);
704 if (!pipeline) {
705 SKGPU_LOG_W("Failed to create GraphicsPipeline for draw in RenderPass. Dropping pass!");
706 return false;
707 }
708 fFullPipelines.push_back(std::move(pipeline));
709 }
710 // The DrawPass may be long lived on a Recording and we no longer need the GraphicPipelineDescs
711 // once we've created pipelines, so we drop the storage for them here.
712 fPipelineDescs.clear();
713
714 for (int i = 0; i < fSampledTextures.size(); ++i) {
715 // TODO: We need to remove this check once we are creating valid SkImages from things like
716 // snapshot, save layers, etc. Right now we only support SkImages directly made for graphite
717 // and all others have a TextureProxy with an invalid TextureInfo.
718 if (!fSampledTextures[i]->textureInfo().isValid()) {
719 SKGPU_LOG_W("Failed to validate sampled texture. Will not create renderpass!");
720 return false;
721 }
722 if (!TextureProxy::InstantiateIfNotLazy(resourceProvider, fSampledTextures[i].get())) {
723 SKGPU_LOG_W("Failed to instantiate sampled texture. Will not create renderpass!");
724 return false;
725 }
726 }
727
728 fSamplers.reserve(fSamplers.size() + fSamplerDescs.size());
729 for (int i = 0; i < fSamplerDescs.size(); ++i) {
730 sk_sp<Sampler> sampler = resourceProvider->findOrCreateCompatibleSampler(fSamplerDescs[i]);
731 if (!sampler) {
732 SKGPU_LOG_W("Failed to create sampler. Will not create renderpass!");
733 return false;
734 }
735 fSamplers.push_back(std::move(sampler));
736 }
737 // The DrawPass may be long lived on a Recording and we no longer need the SamplerDescs
738 // once we've created Samplers, so we drop the storage for them here.
739 fSamplerDescs.clear();
740
741 return true;
742}
static bool InstantiateIfNotLazy(ResourceProvider *, TextureProxy *)
const myers::Point & get(const myers::Segment &)
#define TRACE_EVENT0(category_group, name)

◆ requiresDstTexture()

bool skgpu::graphite::DrawPass::requiresDstTexture ( ) const
inline

Definition at line 71 of file DrawPass.h.

71{ return false; }

◆ requiresMSAA()

bool skgpu::graphite::DrawPass::requiresMSAA ( ) const
inline

Definition at line 72 of file DrawPass.h.

72{ return fRequiresMSAA; }

◆ sampledTextures()

skia_private::TArray< sk_sp< TextureProxy > > skgpu::graphite::DrawPass::sampledTextures ( ) const
inline

Definition at line 96 of file DrawPass.h.

96{ return fSampledTextures; }

◆ target()

TextureProxy * skgpu::graphite::DrawPass::target ( ) const
inline

Definition at line 67 of file DrawPass.h.

67{ return fTarget.get(); }

◆ uniformBufferSize()

size_t skgpu::graphite::DrawPass::uniformBufferSize ( ) const
inline

Definition at line 77 of file DrawPass.h.

77{ return 0; }

◆ vertexBufferSize()

size_t skgpu::graphite::DrawPass::vertexBufferSize ( ) const
inline

Definition at line 76 of file DrawPass.h.

76{ return 0; }

The documentation for this class was generated from the following files: