1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2021 Google LLC
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/DrawPass.h"
9*c8dee2aaSAndroid Build Coastguard Worker
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/graphite/GraphiteTypes.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/graphite/Recorder.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkAlign.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkTraceEvent.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/Buffer.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/BufferManager.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/Caps.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/ContextPriv.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/ContextUtils.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/DrawContext.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/DrawList.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/DrawWriter.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/GlobalCache.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/GraphicsPipeline.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/GraphicsPipelineDesc.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/Log.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/PaintParamsKey.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/PipelineData.h"
28*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/RecorderPriv.h"
29*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/Renderer.h"
30*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/ResourceProvider.h"
31*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/Sampler.h"
32*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/Texture.h"
33*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/UniformManager.h"
34*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/geom/BoundsManager.h"
35*c8dee2aaSAndroid Build Coastguard Worker
36*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkMathPriv.h"
37*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkTBlockList.h"
38*c8dee2aaSAndroid Build Coastguard Worker
39*c8dee2aaSAndroid Build Coastguard Worker #include <algorithm>
40*c8dee2aaSAndroid Build Coastguard Worker
41*c8dee2aaSAndroid Build Coastguard Worker using namespace skia_private;
42*c8dee2aaSAndroid Build Coastguard Worker
43*c8dee2aaSAndroid Build Coastguard Worker namespace skgpu::graphite {
44*c8dee2aaSAndroid Build Coastguard Worker
45*c8dee2aaSAndroid Build Coastguard Worker namespace {
46*c8dee2aaSAndroid Build Coastguard Worker
47*c8dee2aaSAndroid Build Coastguard Worker // Helper to manage packed fields within a uint64_t
48*c8dee2aaSAndroid Build Coastguard Worker template <uint64_t Bits, uint64_t Offset>
49*c8dee2aaSAndroid Build Coastguard Worker struct Bitfield {
50*c8dee2aaSAndroid Build Coastguard Worker static constexpr uint64_t kMask = ((uint64_t) 1 << Bits) - 1;
51*c8dee2aaSAndroid Build Coastguard Worker static constexpr uint64_t kOffset = Offset;
52*c8dee2aaSAndroid Build Coastguard Worker static constexpr uint64_t kBits = Bits;
53*c8dee2aaSAndroid Build Coastguard Worker
getskgpu::graphite::__anon6929da090111::Bitfield54*c8dee2aaSAndroid Build Coastguard Worker static uint32_t get(uint64_t v) { return static_cast<uint32_t>((v >> kOffset) & kMask); }
setskgpu::graphite::__anon6929da090111::Bitfield55*c8dee2aaSAndroid Build Coastguard Worker static uint64_t set(uint32_t v) { return (v & kMask) << kOffset; }
56*c8dee2aaSAndroid Build Coastguard Worker };
57*c8dee2aaSAndroid Build Coastguard Worker
58*c8dee2aaSAndroid Build Coastguard Worker // This class maps objects to a dense index which can then be used to look them up later
59*c8dee2aaSAndroid Build Coastguard Worker template <typename T, typename V = T, typename C = V>
60*c8dee2aaSAndroid Build Coastguard Worker class DenseBiMap {
61*c8dee2aaSAndroid Build Coastguard Worker public:
62*c8dee2aaSAndroid Build Coastguard Worker using Index = uint32_t;
63*c8dee2aaSAndroid Build Coastguard Worker
64*c8dee2aaSAndroid Build Coastguard Worker // See note below in GeometryUniformField. This value can be round-tripped within the SortKey
65*c8dee2aaSAndroid Build Coastguard Worker // packing for all fields but will not be produced when recording actual draw data.
66*c8dee2aaSAndroid Build Coastguard Worker static constexpr Index kInvalidIndex{1 << SkNextLog2_portable(DrawList::kMaxRenderSteps)};
67*c8dee2aaSAndroid Build Coastguard Worker
empty() const68*c8dee2aaSAndroid Build Coastguard Worker bool empty() const { return fIndexToData.empty(); }
size() const69*c8dee2aaSAndroid Build Coastguard Worker size_t size() const { return fIndexToData.size(); }
70*c8dee2aaSAndroid Build Coastguard Worker
insert(const T & data)71*c8dee2aaSAndroid Build Coastguard Worker Index insert(const T& data) {
72*c8dee2aaSAndroid Build Coastguard Worker Index* index = fDataToIndex.find(data);
73*c8dee2aaSAndroid Build Coastguard Worker if (!index) {
74*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(SkToU32(fIndexToData.size()) < kInvalidIndex);
75*c8dee2aaSAndroid Build Coastguard Worker index = fDataToIndex.set(data, (Index) fIndexToData.size());
76*c8dee2aaSAndroid Build Coastguard Worker fIndexToData.push_back(C{data});
77*c8dee2aaSAndroid Build Coastguard Worker }
78*c8dee2aaSAndroid Build Coastguard Worker return *index;
79*c8dee2aaSAndroid Build Coastguard Worker }
80*c8dee2aaSAndroid Build Coastguard Worker
lookup(Index index)81*c8dee2aaSAndroid Build Coastguard Worker const V& lookup(Index index) {
82*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(index < kInvalidIndex);
83*c8dee2aaSAndroid Build Coastguard Worker return fIndexToData[index];
84*c8dee2aaSAndroid Build Coastguard Worker }
85*c8dee2aaSAndroid Build Coastguard Worker
data()86*c8dee2aaSAndroid Build Coastguard Worker SkSpan<V> data() { return {fIndexToData.data(), fIndexToData.size()}; }
87*c8dee2aaSAndroid Build Coastguard Worker
detach()88*c8dee2aaSAndroid Build Coastguard Worker TArray<V>&& detach() { return std::move(fIndexToData); }
89*c8dee2aaSAndroid Build Coastguard Worker
90*c8dee2aaSAndroid Build Coastguard Worker private:
91*c8dee2aaSAndroid Build Coastguard Worker THashMap<T, Index> fDataToIndex;
92*c8dee2aaSAndroid Build Coastguard Worker TArray<V> fIndexToData;
93*c8dee2aaSAndroid Build Coastguard Worker };
94*c8dee2aaSAndroid Build Coastguard Worker
95*c8dee2aaSAndroid Build Coastguard Worker // NOTE: TextureBinding's use as a key type in DenseBiMap relies on the fact that the underlying
96*c8dee2aaSAndroid Build Coastguard Worker // data has been de-duplicated by a PipelineDataCache earlier, so that the bit identity of the data
97*c8dee2aaSAndroid Build Coastguard Worker // blocks (e.g. address+size) is equivalent to the content equality of the texture lists.
98*c8dee2aaSAndroid Build Coastguard Worker
99*c8dee2aaSAndroid Build Coastguard Worker // Tracks the combination of textures from the paint and from the RenderStep to describe the full
100*c8dee2aaSAndroid Build Coastguard Worker // binding that needs to be in the command list.
101*c8dee2aaSAndroid Build Coastguard Worker struct TextureBinding {
102*c8dee2aaSAndroid Build Coastguard Worker TextureDataBlock fPaintTextures;
103*c8dee2aaSAndroid Build Coastguard Worker TextureDataBlock fStepTextures;
104*c8dee2aaSAndroid Build Coastguard Worker
operator ==skgpu::graphite::__anon6929da090111::TextureBinding105*c8dee2aaSAndroid Build Coastguard Worker bool operator==(const TextureBinding& other) const {
106*c8dee2aaSAndroid Build Coastguard Worker return fPaintTextures == other.fPaintTextures &&
107*c8dee2aaSAndroid Build Coastguard Worker fStepTextures == other.fStepTextures;
108*c8dee2aaSAndroid Build Coastguard Worker }
operator !=skgpu::graphite::__anon6929da090111::TextureBinding109*c8dee2aaSAndroid Build Coastguard Worker bool operator!=(const TextureBinding& other) const { return !(*this == other); }
110*c8dee2aaSAndroid Build Coastguard Worker
numTexturesskgpu::graphite::__anon6929da090111::TextureBinding111*c8dee2aaSAndroid Build Coastguard Worker int numTextures() const {
112*c8dee2aaSAndroid Build Coastguard Worker return (fPaintTextures ? fPaintTextures.numTextures() : 0) +
113*c8dee2aaSAndroid Build Coastguard Worker (fStepTextures ? fStepTextures.numTextures() : 0);
114*c8dee2aaSAndroid Build Coastguard Worker }
115*c8dee2aaSAndroid Build Coastguard Worker };
116*c8dee2aaSAndroid Build Coastguard Worker
117*c8dee2aaSAndroid Build Coastguard Worker using TextureBindingCache = DenseBiMap<TextureBinding>;
118*c8dee2aaSAndroid Build Coastguard Worker using GraphicsPipelineCache = DenseBiMap<GraphicsPipelineDesc>;
119*c8dee2aaSAndroid Build Coastguard Worker
120*c8dee2aaSAndroid Build Coastguard Worker // Writes uniform data either to uniform buffers or to shared storage buffers, and tracks when
121*c8dee2aaSAndroid Build Coastguard Worker // bindings need to change between draws.
122*c8dee2aaSAndroid Build Coastguard Worker class UniformTracker {
123*c8dee2aaSAndroid Build Coastguard Worker public:
UniformTracker(bool useStorageBuffers)124*c8dee2aaSAndroid Build Coastguard Worker UniformTracker(bool useStorageBuffers) : fUseStorageBuffers(useStorageBuffers) {}
125*c8dee2aaSAndroid Build Coastguard Worker
writeUniforms(UniformDataCache & uniformCache,DrawBufferManager * bufferMgr,UniformDataCache::Index index)126*c8dee2aaSAndroid Build Coastguard Worker bool writeUniforms(UniformDataCache& uniformCache,
127*c8dee2aaSAndroid Build Coastguard Worker DrawBufferManager* bufferMgr,
128*c8dee2aaSAndroid Build Coastguard Worker UniformDataCache::Index index) {
129*c8dee2aaSAndroid Build Coastguard Worker if (index >= UniformDataCache::kInvalidIndex) {
130*c8dee2aaSAndroid Build Coastguard Worker return false;
131*c8dee2aaSAndroid Build Coastguard Worker }
132*c8dee2aaSAndroid Build Coastguard Worker
133*c8dee2aaSAndroid Build Coastguard Worker if (index == fLastIndex) {
134*c8dee2aaSAndroid Build Coastguard Worker return false;
135*c8dee2aaSAndroid Build Coastguard Worker }
136*c8dee2aaSAndroid Build Coastguard Worker fLastIndex = index;
137*c8dee2aaSAndroid Build Coastguard Worker
138*c8dee2aaSAndroid Build Coastguard Worker UniformDataCache::Entry& uniformData = uniformCache.lookup(index);
139*c8dee2aaSAndroid Build Coastguard Worker const size_t uniformDataSize = uniformData.fCpuData.size();
140*c8dee2aaSAndroid Build Coastguard Worker
141*c8dee2aaSAndroid Build Coastguard Worker // Upload the uniform data if we haven't already.
142*c8dee2aaSAndroid Build Coastguard Worker // Alternatively, re-upload the uniform data to avoid a rebind if we're using storage
143*c8dee2aaSAndroid Build Coastguard Worker // buffers. This will result in more data uploaded, but the tradeoff seems worthwhile.
144*c8dee2aaSAndroid Build Coastguard Worker if (!uniformData.fBufferBinding.fBuffer ||
145*c8dee2aaSAndroid Build Coastguard Worker (fUseStorageBuffers && uniformData.fBufferBinding.fBuffer != fLastBinding.fBuffer)) {
146*c8dee2aaSAndroid Build Coastguard Worker UniformWriter writer;
147*c8dee2aaSAndroid Build Coastguard Worker std::tie(writer, uniformData.fBufferBinding) =
148*c8dee2aaSAndroid Build Coastguard Worker fUseStorageBuffers ? bufferMgr->getAlignedSsboWriter(1, uniformDataSize)
149*c8dee2aaSAndroid Build Coastguard Worker : bufferMgr->getUniformWriter(1, uniformDataSize);
150*c8dee2aaSAndroid Build Coastguard Worker
151*c8dee2aaSAndroid Build Coastguard Worker // Early out if buffer mapping failed.
152*c8dee2aaSAndroid Build Coastguard Worker if (!writer) {
153*c8dee2aaSAndroid Build Coastguard Worker return {};
154*c8dee2aaSAndroid Build Coastguard Worker }
155*c8dee2aaSAndroid Build Coastguard Worker
156*c8dee2aaSAndroid Build Coastguard Worker writer.write(uniformData.fCpuData.data(), uniformDataSize);
157*c8dee2aaSAndroid Build Coastguard Worker
158*c8dee2aaSAndroid Build Coastguard Worker if (fUseStorageBuffers) {
159*c8dee2aaSAndroid Build Coastguard Worker // When using storage buffers, store the SSBO index in the binding's offset field
160*c8dee2aaSAndroid Build Coastguard Worker // and always use the entire buffer's size in the size field.
161*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(uniformData.fBufferBinding.fOffset % uniformDataSize == 0);
162*c8dee2aaSAndroid Build Coastguard Worker uniformData.fBufferBinding.fOffset /= uniformDataSize;
163*c8dee2aaSAndroid Build Coastguard Worker uniformData.fBufferBinding.fSize = uniformData.fBufferBinding.fBuffer->size();
164*c8dee2aaSAndroid Build Coastguard Worker }
165*c8dee2aaSAndroid Build Coastguard Worker }
166*c8dee2aaSAndroid Build Coastguard Worker
167*c8dee2aaSAndroid Build Coastguard Worker const bool needsRebind =
168*c8dee2aaSAndroid Build Coastguard Worker uniformData.fBufferBinding.fBuffer != fLastBinding.fBuffer ||
169*c8dee2aaSAndroid Build Coastguard Worker (!fUseStorageBuffers && uniformData.fBufferBinding.fOffset != fLastBinding.fOffset);
170*c8dee2aaSAndroid Build Coastguard Worker
171*c8dee2aaSAndroid Build Coastguard Worker fLastBinding = uniformData.fBufferBinding;
172*c8dee2aaSAndroid Build Coastguard Worker
173*c8dee2aaSAndroid Build Coastguard Worker return needsRebind;
174*c8dee2aaSAndroid Build Coastguard Worker }
175*c8dee2aaSAndroid Build Coastguard Worker
bindUniforms(UniformSlot slot,DrawPassCommands::List * commandList)176*c8dee2aaSAndroid Build Coastguard Worker void bindUniforms(UniformSlot slot, DrawPassCommands::List* commandList) {
177*c8dee2aaSAndroid Build Coastguard Worker BindBufferInfo binding = fLastBinding;
178*c8dee2aaSAndroid Build Coastguard Worker if (fUseStorageBuffers) {
179*c8dee2aaSAndroid Build Coastguard Worker // Track the SSBO index in fLastBinding, but set offset = 0 in the actual used binding.
180*c8dee2aaSAndroid Build Coastguard Worker binding.fOffset = 0;
181*c8dee2aaSAndroid Build Coastguard Worker }
182*c8dee2aaSAndroid Build Coastguard Worker commandList->bindUniformBuffer(binding, slot);
183*c8dee2aaSAndroid Build Coastguard Worker }
184*c8dee2aaSAndroid Build Coastguard Worker
ssboIndex() const185*c8dee2aaSAndroid Build Coastguard Worker uint32_t ssboIndex() const {
186*c8dee2aaSAndroid Build Coastguard Worker // The SSBO index for the last-bound storage buffer is stored in the binding's offset field.
187*c8dee2aaSAndroid Build Coastguard Worker return fLastBinding.fOffset;
188*c8dee2aaSAndroid Build Coastguard Worker }
189*c8dee2aaSAndroid Build Coastguard Worker
190*c8dee2aaSAndroid Build Coastguard Worker private:
191*c8dee2aaSAndroid Build Coastguard Worker // Internally track the last binding returned, so that we know whether new uploads or rebindings
192*c8dee2aaSAndroid Build Coastguard Worker // are necessary. If we're using SSBOs, this is treated specially -- the fOffset field holds the
193*c8dee2aaSAndroid Build Coastguard Worker // index in the storage buffer of the last-written uniforms, and the offsets used for actual
194*c8dee2aaSAndroid Build Coastguard Worker // bindings are always zero.
195*c8dee2aaSAndroid Build Coastguard Worker BindBufferInfo fLastBinding;
196*c8dee2aaSAndroid Build Coastguard Worker
197*c8dee2aaSAndroid Build Coastguard Worker // This keeps track of the last index used for writing uniforms from a provided uniform cache.
198*c8dee2aaSAndroid Build Coastguard Worker // If a provided index matches the last index, the uniforms are assumed to already be written
199*c8dee2aaSAndroid Build Coastguard Worker // and no additional uploading is performed. This assumes a UniformTracker will always be
200*c8dee2aaSAndroid Build Coastguard Worker // provided with the same uniform cache.
201*c8dee2aaSAndroid Build Coastguard Worker UniformDataCache::Index fLastIndex = UniformDataCache::kInvalidIndex;
202*c8dee2aaSAndroid Build Coastguard Worker
203*c8dee2aaSAndroid Build Coastguard Worker const bool fUseStorageBuffers;
204*c8dee2aaSAndroid Build Coastguard Worker };
205*c8dee2aaSAndroid Build Coastguard Worker
206*c8dee2aaSAndroid Build Coastguard Worker // Automatically merges and manages texture bindings and uniform bindings sourced from either the
207*c8dee2aaSAndroid Build Coastguard Worker // paint or the RenderStep. Tracks the bound state based on last-provided unique index to write
208*c8dee2aaSAndroid Build Coastguard Worker // Bind commands to a CommandList when necessary.
209*c8dee2aaSAndroid Build Coastguard Worker class TextureBindingTracker {
210*c8dee2aaSAndroid Build Coastguard Worker public:
trackTextures(TextureDataBlock paintTextures,TextureDataBlock stepTextures)211*c8dee2aaSAndroid Build Coastguard Worker TextureBindingCache::Index trackTextures(TextureDataBlock paintTextures,
212*c8dee2aaSAndroid Build Coastguard Worker TextureDataBlock stepTextures) {
213*c8dee2aaSAndroid Build Coastguard Worker if (!paintTextures && !stepTextures) {
214*c8dee2aaSAndroid Build Coastguard Worker return TextureBindingCache::kInvalidIndex;
215*c8dee2aaSAndroid Build Coastguard Worker }
216*c8dee2aaSAndroid Build Coastguard Worker return fBindingCache.insert({paintTextures, stepTextures});
217*c8dee2aaSAndroid Build Coastguard Worker }
218*c8dee2aaSAndroid Build Coastguard Worker
setCurrentTextureBindings(TextureBindingCache::Index bindingIndex)219*c8dee2aaSAndroid Build Coastguard Worker bool setCurrentTextureBindings(TextureBindingCache::Index bindingIndex) {
220*c8dee2aaSAndroid Build Coastguard Worker if (bindingIndex < TextureBindingCache::kInvalidIndex && fLastIndex != bindingIndex) {
221*c8dee2aaSAndroid Build Coastguard Worker fLastIndex = bindingIndex;
222*c8dee2aaSAndroid Build Coastguard Worker return true;
223*c8dee2aaSAndroid Build Coastguard Worker }
224*c8dee2aaSAndroid Build Coastguard Worker // No binding change
225*c8dee2aaSAndroid Build Coastguard Worker return false;
226*c8dee2aaSAndroid Build Coastguard Worker }
227*c8dee2aaSAndroid Build Coastguard Worker
bindTextures(DrawPassCommands::List * commandList)228*c8dee2aaSAndroid Build Coastguard Worker void bindTextures(DrawPassCommands::List* commandList) {
229*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fLastIndex < TextureBindingCache::kInvalidIndex);
230*c8dee2aaSAndroid Build Coastguard Worker const TextureBinding& binding = fBindingCache.lookup(fLastIndex);
231*c8dee2aaSAndroid Build Coastguard Worker
232*c8dee2aaSAndroid Build Coastguard Worker auto [texIndices, samplerIndices] =
233*c8dee2aaSAndroid Build Coastguard Worker commandList->bindDeferredTexturesAndSamplers(binding.numTextures());
234*c8dee2aaSAndroid Build Coastguard Worker
235*c8dee2aaSAndroid Build Coastguard Worker if (binding.fPaintTextures) {
236*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < binding.fPaintTextures.numTextures(); ++i) {
237*c8dee2aaSAndroid Build Coastguard Worker auto [tex, sampler] = binding.fPaintTextures.texture(i);
238*c8dee2aaSAndroid Build Coastguard Worker *texIndices++ = fProxyCache.insert(tex.get());
239*c8dee2aaSAndroid Build Coastguard Worker *samplerIndices++ = fSamplerCache.insert(sampler);
240*c8dee2aaSAndroid Build Coastguard Worker }
241*c8dee2aaSAndroid Build Coastguard Worker }
242*c8dee2aaSAndroid Build Coastguard Worker if (binding.fStepTextures) {
243*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < binding.fStepTextures.numTextures(); ++i) {
244*c8dee2aaSAndroid Build Coastguard Worker auto [tex, sampler] = binding.fStepTextures.texture(i);
245*c8dee2aaSAndroid Build Coastguard Worker *texIndices++ = fProxyCache.insert(tex.get());
246*c8dee2aaSAndroid Build Coastguard Worker *samplerIndices++ = fSamplerCache.insert(sampler);
247*c8dee2aaSAndroid Build Coastguard Worker }
248*c8dee2aaSAndroid Build Coastguard Worker }
249*c8dee2aaSAndroid Build Coastguard Worker }
250*c8dee2aaSAndroid Build Coastguard Worker
detachTextures()251*c8dee2aaSAndroid Build Coastguard Worker TArray<sk_sp<TextureProxy>>&& detachTextures() { return fProxyCache.detach(); }
detachSamplers()252*c8dee2aaSAndroid Build Coastguard Worker TArray<SamplerDesc>&& detachSamplers() { return fSamplerCache.detach(); }
253*c8dee2aaSAndroid Build Coastguard Worker
254*c8dee2aaSAndroid Build Coastguard Worker private:
255*c8dee2aaSAndroid Build Coastguard Worker struct ProxyRef {
256*c8dee2aaSAndroid Build Coastguard Worker const TextureProxy* fProxy;
operator sk_sp<TextureProxy>skgpu::graphite::__anon6929da090111::TextureBindingTracker::ProxyRef257*c8dee2aaSAndroid Build Coastguard Worker operator sk_sp<TextureProxy>() const { return sk_ref_sp(fProxy); }
258*c8dee2aaSAndroid Build Coastguard Worker };
259*c8dee2aaSAndroid Build Coastguard Worker using TextureProxyCache = DenseBiMap<const TextureProxy*, sk_sp<TextureProxy>, ProxyRef>;
260*c8dee2aaSAndroid Build Coastguard Worker using SamplerDescCache = DenseBiMap<SamplerDesc>;
261*c8dee2aaSAndroid Build Coastguard Worker
262*c8dee2aaSAndroid Build Coastguard Worker TextureBindingCache fBindingCache;
263*c8dee2aaSAndroid Build Coastguard Worker
264*c8dee2aaSAndroid Build Coastguard Worker TextureProxyCache fProxyCache;
265*c8dee2aaSAndroid Build Coastguard Worker SamplerDescCache fSamplerCache;
266*c8dee2aaSAndroid Build Coastguard Worker
267*c8dee2aaSAndroid Build Coastguard Worker TextureBindingCache::Index fLastIndex = TextureBindingCache::kInvalidIndex;
268*c8dee2aaSAndroid Build Coastguard Worker };
269*c8dee2aaSAndroid Build Coastguard Worker
270*c8dee2aaSAndroid Build Coastguard Worker class GradientBufferTracker {
271*c8dee2aaSAndroid Build Coastguard Worker public:
writeData(SkSpan<const float> gradData,DrawBufferManager * bufferMgr)272*c8dee2aaSAndroid Build Coastguard Worker bool writeData(SkSpan<const float> gradData, DrawBufferManager* bufferMgr) {
273*c8dee2aaSAndroid Build Coastguard Worker if (gradData.empty()) {
274*c8dee2aaSAndroid Build Coastguard Worker return true;
275*c8dee2aaSAndroid Build Coastguard Worker }
276*c8dee2aaSAndroid Build Coastguard Worker
277*c8dee2aaSAndroid Build Coastguard Worker auto [writer, bufferInfo] = bufferMgr->getSsboWriter(gradData.size(), sizeof(float));
278*c8dee2aaSAndroid Build Coastguard Worker
279*c8dee2aaSAndroid Build Coastguard Worker if (!writer) {
280*c8dee2aaSAndroid Build Coastguard Worker return false;
281*c8dee2aaSAndroid Build Coastguard Worker }
282*c8dee2aaSAndroid Build Coastguard Worker
283*c8dee2aaSAndroid Build Coastguard Worker writer.write(gradData.data(), gradData.size_bytes());
284*c8dee2aaSAndroid Build Coastguard Worker fBufferInfo = bufferInfo;
285*c8dee2aaSAndroid Build Coastguard Worker fHasData = true;
286*c8dee2aaSAndroid Build Coastguard Worker
287*c8dee2aaSAndroid Build Coastguard Worker return true;
288*c8dee2aaSAndroid Build Coastguard Worker }
289*c8dee2aaSAndroid Build Coastguard Worker
bindIfNeeded(DrawPassCommands::List * commandList) const290*c8dee2aaSAndroid Build Coastguard Worker void bindIfNeeded(DrawPassCommands::List* commandList) const {
291*c8dee2aaSAndroid Build Coastguard Worker if (fHasData) {
292*c8dee2aaSAndroid Build Coastguard Worker commandList->bindUniformBuffer(fBufferInfo, UniformSlot::kGradient);
293*c8dee2aaSAndroid Build Coastguard Worker }
294*c8dee2aaSAndroid Build Coastguard Worker }
295*c8dee2aaSAndroid Build Coastguard Worker
296*c8dee2aaSAndroid Build Coastguard Worker private:
297*c8dee2aaSAndroid Build Coastguard Worker BindBufferInfo fBufferInfo;
298*c8dee2aaSAndroid Build Coastguard Worker bool fHasData = false;
299*c8dee2aaSAndroid Build Coastguard Worker };
300*c8dee2aaSAndroid Build Coastguard Worker
301*c8dee2aaSAndroid Build Coastguard Worker } // namespace
302*c8dee2aaSAndroid Build Coastguard Worker
303*c8dee2aaSAndroid Build Coastguard Worker ///////////////////////////////////////////////////////////////////////////////////////////////////
304*c8dee2aaSAndroid Build Coastguard Worker
305*c8dee2aaSAndroid Build Coastguard Worker /**
306*c8dee2aaSAndroid Build Coastguard Worker * Each Draw in a DrawList might be processed by multiple RenderSteps (determined by the Draw's
307*c8dee2aaSAndroid Build Coastguard Worker * Renderer), which can be sorted independently. Each (step, draw) pair produces its own SortKey.
308*c8dee2aaSAndroid Build Coastguard Worker *
309*c8dee2aaSAndroid Build Coastguard Worker * The goal of sorting draws for the DrawPass is to minimize pipeline transitions and dynamic binds
310*c8dee2aaSAndroid Build Coastguard Worker * within a pipeline, while still respecting the overall painter's order. This decreases the number
311*c8dee2aaSAndroid Build Coastguard Worker * of low-level draw commands in a command buffer and increases the size of those, allowing the GPU
312*c8dee2aaSAndroid Build Coastguard Worker * to operate more efficiently and have fewer bubbles within its own instruction stream.
313*c8dee2aaSAndroid Build Coastguard Worker *
314*c8dee2aaSAndroid Build Coastguard Worker * The Draw's CompresssedPaintersOrder and DisjointStencilINdex represent the most significant bits
315*c8dee2aaSAndroid Build Coastguard Worker * of the key, and are shared by all SortKeys produced by the same draw. Next, the pipeline
316*c8dee2aaSAndroid Build Coastguard Worker * description is encoded in two steps:
317*c8dee2aaSAndroid Build Coastguard Worker * 1. The index of the RenderStep packed in the high bits to ensure each step for a draw is
318*c8dee2aaSAndroid Build Coastguard Worker * ordered correctly.
319*c8dee2aaSAndroid Build Coastguard Worker * 2. An index into a cache of pipeline descriptions is used to encode the identity of the
320*c8dee2aaSAndroid Build Coastguard Worker * pipeline (SortKeys that differ in the bits from #1 necessarily would have different
321*c8dee2aaSAndroid Build Coastguard Worker * descriptions, but then the specific ordering of the RenderSteps isn't enforced).
322*c8dee2aaSAndroid Build Coastguard Worker * Last, the SortKey encodes an index into the set of uniform bindings accumulated for a DrawPass.
323*c8dee2aaSAndroid Build Coastguard Worker * This allows the SortKey to cluster draw steps that have both a compatible pipeline and do not
324*c8dee2aaSAndroid Build Coastguard Worker * require rebinding uniform data or other state (e.g. scissor). Since the uniform data index and
325*c8dee2aaSAndroid Build Coastguard Worker * the pipeline description index are packed into indices and not actual pointers, a given SortKey
326*c8dee2aaSAndroid Build Coastguard Worker * is only valid for the a specific DrawList->DrawPass conversion.
327*c8dee2aaSAndroid Build Coastguard Worker */
328*c8dee2aaSAndroid Build Coastguard Worker class DrawPass::SortKey {
329*c8dee2aaSAndroid Build Coastguard Worker public:
SortKey(const DrawList::Draw * draw,int renderStep,GraphicsPipelineCache::Index pipelineIndex,UniformDataCache::Index geomUniformIndex,UniformDataCache::Index shadingUniformIndex,TextureBindingCache::Index textureBindingIndex)330*c8dee2aaSAndroid Build Coastguard Worker SortKey(const DrawList::Draw* draw,
331*c8dee2aaSAndroid Build Coastguard Worker int renderStep,
332*c8dee2aaSAndroid Build Coastguard Worker GraphicsPipelineCache::Index pipelineIndex,
333*c8dee2aaSAndroid Build Coastguard Worker UniformDataCache::Index geomUniformIndex,
334*c8dee2aaSAndroid Build Coastguard Worker UniformDataCache::Index shadingUniformIndex,
335*c8dee2aaSAndroid Build Coastguard Worker TextureBindingCache::Index textureBindingIndex)
336*c8dee2aaSAndroid Build Coastguard Worker : fPipelineKey(ColorDepthOrderField::set(draw->fDrawParams.order().paintOrder().bits()) |
337*c8dee2aaSAndroid Build Coastguard Worker StencilIndexField::set(draw->fDrawParams.order().stencilIndex().bits()) |
338*c8dee2aaSAndroid Build Coastguard Worker RenderStepField::set(static_cast<uint32_t>(renderStep)) |
339*c8dee2aaSAndroid Build Coastguard Worker PipelineField::set(pipelineIndex))
340*c8dee2aaSAndroid Build Coastguard Worker , fUniformKey(GeometryUniformField::set(geomUniformIndex) |
341*c8dee2aaSAndroid Build Coastguard Worker ShadingUniformField::set(shadingUniformIndex) |
342*c8dee2aaSAndroid Build Coastguard Worker TextureBindingsField::set(textureBindingIndex))
343*c8dee2aaSAndroid Build Coastguard Worker , fDraw(draw) {
344*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(pipelineIndex < GraphicsPipelineCache::kInvalidIndex);
345*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(renderStep <= draw->fRenderer->numRenderSteps());
346*c8dee2aaSAndroid Build Coastguard Worker }
347*c8dee2aaSAndroid Build Coastguard Worker
operator <(const SortKey & k) const348*c8dee2aaSAndroid Build Coastguard Worker bool operator<(const SortKey& k) const {
349*c8dee2aaSAndroid Build Coastguard Worker return fPipelineKey < k.fPipelineKey ||
350*c8dee2aaSAndroid Build Coastguard Worker (fPipelineKey == k.fPipelineKey && fUniformKey < k.fUniformKey);
351*c8dee2aaSAndroid Build Coastguard Worker }
352*c8dee2aaSAndroid Build Coastguard Worker
renderStep() const353*c8dee2aaSAndroid Build Coastguard Worker const RenderStep& renderStep() const {
354*c8dee2aaSAndroid Build Coastguard Worker return fDraw->fRenderer->step(RenderStepField::get(fPipelineKey));
355*c8dee2aaSAndroid Build Coastguard Worker }
356*c8dee2aaSAndroid Build Coastguard Worker
draw() const357*c8dee2aaSAndroid Build Coastguard Worker const DrawList::Draw& draw() const { return *fDraw; }
358*c8dee2aaSAndroid Build Coastguard Worker
pipelineIndex() const359*c8dee2aaSAndroid Build Coastguard Worker GraphicsPipelineCache::Index pipelineIndex() const {
360*c8dee2aaSAndroid Build Coastguard Worker return PipelineField::get(fPipelineKey);
361*c8dee2aaSAndroid Build Coastguard Worker }
geometryUniformIndex() const362*c8dee2aaSAndroid Build Coastguard Worker UniformDataCache::Index geometryUniformIndex() const {
363*c8dee2aaSAndroid Build Coastguard Worker return GeometryUniformField::get(fUniformKey);
364*c8dee2aaSAndroid Build Coastguard Worker }
shadingUniformIndex() const365*c8dee2aaSAndroid Build Coastguard Worker UniformDataCache::Index shadingUniformIndex() const {
366*c8dee2aaSAndroid Build Coastguard Worker return ShadingUniformField::get(fUniformKey);
367*c8dee2aaSAndroid Build Coastguard Worker }
textureBindingIndex() const368*c8dee2aaSAndroid Build Coastguard Worker TextureBindingCache::Index textureBindingIndex() const {
369*c8dee2aaSAndroid Build Coastguard Worker return TextureBindingsField::get(fUniformKey);
370*c8dee2aaSAndroid Build Coastguard Worker }
371*c8dee2aaSAndroid Build Coastguard Worker
372*c8dee2aaSAndroid Build Coastguard Worker private:
373*c8dee2aaSAndroid Build Coastguard Worker // Fields are ordered from most-significant to least when sorting by 128-bit value.
374*c8dee2aaSAndroid Build Coastguard Worker // NOTE: We don't use C++ bit fields because field ordering is implementation defined and we
375*c8dee2aaSAndroid Build Coastguard Worker // need to sort consistently.
376*c8dee2aaSAndroid Build Coastguard Worker using ColorDepthOrderField = Bitfield<16, 48>; // sizeof(CompressedPaintersOrder)
377*c8dee2aaSAndroid Build Coastguard Worker using StencilIndexField = Bitfield<16, 32>; // sizeof(DisjointStencilIndex)
378*c8dee2aaSAndroid Build Coastguard Worker using RenderStepField = Bitfield<2, 30>; // bits >= log2(Renderer::kMaxRenderSteps)
379*c8dee2aaSAndroid Build Coastguard Worker using PipelineField = Bitfield<30, 0>; // bits >= log2(max total steps in draw list)
380*c8dee2aaSAndroid Build Coastguard Worker uint64_t fPipelineKey;
381*c8dee2aaSAndroid Build Coastguard Worker
382*c8dee2aaSAndroid Build Coastguard Worker // The uniform/texture index fields need 1 extra bit to encode "no-data". Values that are
383*c8dee2aaSAndroid Build Coastguard Worker // greater than or equal to 2^(bits-1) represent "no-data", while values between
384*c8dee2aaSAndroid Build Coastguard Worker // [0, 2^(bits-1)-1] can access data arrays without extra logic.
385*c8dee2aaSAndroid Build Coastguard Worker using GeometryUniformField = Bitfield<17, 47>; // bits >= 1+log2(max total steps)
386*c8dee2aaSAndroid Build Coastguard Worker using ShadingUniformField = Bitfield<17, 30>; // bits >= 1+log2(max total steps)
387*c8dee2aaSAndroid Build Coastguard Worker using TextureBindingsField = Bitfield<30, 0>; // bits >= 1+log2(max total steps)
388*c8dee2aaSAndroid Build Coastguard Worker uint64_t fUniformKey;
389*c8dee2aaSAndroid Build Coastguard Worker
390*c8dee2aaSAndroid Build Coastguard Worker // Backpointer to the draw that produced the sort key
391*c8dee2aaSAndroid Build Coastguard Worker const DrawList::Draw* fDraw;
392*c8dee2aaSAndroid Build Coastguard Worker
393*c8dee2aaSAndroid Build Coastguard Worker static_assert(ColorDepthOrderField::kBits >= sizeof(CompressedPaintersOrder));
394*c8dee2aaSAndroid Build Coastguard Worker static_assert(StencilIndexField::kBits >= sizeof(DisjointStencilIndex));
395*c8dee2aaSAndroid Build Coastguard Worker static_assert(RenderStepField::kBits >= SkNextLog2_portable(Renderer::kMaxRenderSteps));
396*c8dee2aaSAndroid Build Coastguard Worker static_assert(PipelineField::kBits >= SkNextLog2_portable(DrawList::kMaxRenderSteps));
397*c8dee2aaSAndroid Build Coastguard Worker static_assert(GeometryUniformField::kBits >= 1+SkNextLog2_portable(DrawList::kMaxRenderSteps));
398*c8dee2aaSAndroid Build Coastguard Worker static_assert(ShadingUniformField::kBits >= 1+SkNextLog2_portable(DrawList::kMaxRenderSteps));
399*c8dee2aaSAndroid Build Coastguard Worker static_assert(TextureBindingsField::kBits >= 1+SkNextLog2_portable(DrawList::kMaxRenderSteps));
400*c8dee2aaSAndroid Build Coastguard Worker };
401*c8dee2aaSAndroid Build Coastguard Worker
402*c8dee2aaSAndroid Build Coastguard Worker ///////////////////////////////////////////////////////////////////////////////////////////////////
403*c8dee2aaSAndroid Build Coastguard Worker
DrawPass(sk_sp<TextureProxy> target,std::pair<LoadOp,StoreOp> ops,std::array<float,4> clearColor)404*c8dee2aaSAndroid Build Coastguard Worker DrawPass::DrawPass(sk_sp<TextureProxy> target,
405*c8dee2aaSAndroid Build Coastguard Worker std::pair<LoadOp, StoreOp> ops,
406*c8dee2aaSAndroid Build Coastguard Worker std::array<float, 4> clearColor)
407*c8dee2aaSAndroid Build Coastguard Worker : fTarget(std::move(target))
408*c8dee2aaSAndroid Build Coastguard Worker , fBounds(SkIRect::MakeEmpty())
409*c8dee2aaSAndroid Build Coastguard Worker , fOps(ops)
410*c8dee2aaSAndroid Build Coastguard Worker , fClearColor(clearColor) {}
411*c8dee2aaSAndroid Build Coastguard Worker
412*c8dee2aaSAndroid Build Coastguard Worker DrawPass::~DrawPass() = default;
413*c8dee2aaSAndroid Build Coastguard Worker
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)414*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<DrawPass> DrawPass::Make(Recorder* recorder,
415*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<DrawList> draws,
416*c8dee2aaSAndroid Build Coastguard Worker sk_sp<TextureProxy> target,
417*c8dee2aaSAndroid Build Coastguard Worker const SkImageInfo& targetInfo,
418*c8dee2aaSAndroid Build Coastguard Worker std::pair<LoadOp, StoreOp> ops,
419*c8dee2aaSAndroid Build Coastguard Worker std::array<float, 4> clearColor) {
420*c8dee2aaSAndroid Build Coastguard Worker // NOTE: This assert is here to ensure SortKey is as tightly packed as possible. Any change to
421*c8dee2aaSAndroid Build Coastguard Worker // its size should be done with care and good reason. The performance of sorting the keys is
422*c8dee2aaSAndroid Build Coastguard Worker // heavily tied to the total size.
423*c8dee2aaSAndroid Build Coastguard Worker //
424*c8dee2aaSAndroid Build Coastguard Worker // At 24 bytes (current), sorting is about 30% slower than if SortKey could be packed into just
425*c8dee2aaSAndroid Build Coastguard Worker // 16 bytes. There are several ways this could be done if necessary:
426*c8dee2aaSAndroid Build Coastguard Worker // - Restricting the max draw count to 16k (14-bits) and only using a single index to refer to
427*c8dee2aaSAndroid Build Coastguard Worker // the uniform data => 8 bytes of key, 8 bytes of pointer.
428*c8dee2aaSAndroid Build Coastguard Worker // - Restrict the max draw count to 32k (15-bits), use a single uniform index, and steal the
429*c8dee2aaSAndroid Build Coastguard Worker // 4 low bits from the Draw* pointer since it's 16 byte aligned.
430*c8dee2aaSAndroid Build Coastguard Worker // - Compact the Draw* to an index into the original collection, although that has extra
431*c8dee2aaSAndroid Build Coastguard Worker // indirection and does not work as well with SkTBlockList.
432*c8dee2aaSAndroid Build Coastguard Worker // In pseudo tests, manipulating the pointer or having to mask out indices was about 15% slower
433*c8dee2aaSAndroid Build Coastguard Worker // than an 8 byte key and unmodified pointer.
434*c8dee2aaSAndroid Build Coastguard Worker static_assert(sizeof(DrawPass::SortKey) ==
435*c8dee2aaSAndroid Build Coastguard Worker SkAlignTo(16 + sizeof(void*), alignof(DrawPass::SortKey)));
436*c8dee2aaSAndroid Build Coastguard Worker
437*c8dee2aaSAndroid Build Coastguard Worker TRACE_EVENT1("skia.gpu", TRACE_FUNC, "draw count", draws->fDraws.count());
438*c8dee2aaSAndroid Build Coastguard Worker
439*c8dee2aaSAndroid Build Coastguard Worker // The DrawList is converted directly into the DrawPass' data structures, but once the DrawPass
440*c8dee2aaSAndroid Build Coastguard Worker // is returned from Make(), it is considered immutable.
441*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<DrawPass> drawPass(new DrawPass(target, ops, clearColor));
442*c8dee2aaSAndroid Build Coastguard Worker
443*c8dee2aaSAndroid Build Coastguard Worker Rect passBounds = Rect::InfiniteInverted();
444*c8dee2aaSAndroid Build Coastguard Worker
445*c8dee2aaSAndroid Build Coastguard Worker UniformDataCache geometryUniformDataCache;
446*c8dee2aaSAndroid Build Coastguard Worker UniformDataCache shadingUniformDataCache;
447*c8dee2aaSAndroid Build Coastguard Worker TextureDataCache* textureDataCache = recorder->priv().textureDataCache();
448*c8dee2aaSAndroid Build Coastguard Worker DrawBufferManager* bufferMgr = recorder->priv().drawBufferManager();
449*c8dee2aaSAndroid Build Coastguard Worker if (bufferMgr->hasMappingFailed()) {
450*c8dee2aaSAndroid Build Coastguard Worker SKGPU_LOG_W("Buffer mapping has already failed; dropping draw pass!");
451*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
452*c8dee2aaSAndroid Build Coastguard Worker }
453*c8dee2aaSAndroid Build Coastguard Worker
454*c8dee2aaSAndroid Build Coastguard Worker GraphicsPipelineCache pipelineCache;
455*c8dee2aaSAndroid Build Coastguard Worker
456*c8dee2aaSAndroid Build Coastguard Worker // Geometry uniforms are currently always UBO-backed.
457*c8dee2aaSAndroid Build Coastguard Worker const bool useStorageBuffers = recorder->priv().caps()->storageBufferSupport();
458*c8dee2aaSAndroid Build Coastguard Worker const ResourceBindingRequirements& bindingReqs =
459*c8dee2aaSAndroid Build Coastguard Worker recorder->priv().caps()->resourceBindingRequirements();
460*c8dee2aaSAndroid Build Coastguard Worker Layout uniformLayout =
461*c8dee2aaSAndroid Build Coastguard Worker useStorageBuffers ? bindingReqs.fStorageBufferLayout : bindingReqs.fUniformBufferLayout;
462*c8dee2aaSAndroid Build Coastguard Worker
463*c8dee2aaSAndroid Build Coastguard Worker TextureBindingTracker textureBindingTracker;
464*c8dee2aaSAndroid Build Coastguard Worker GradientBufferTracker gradientBufferTracker;
465*c8dee2aaSAndroid Build Coastguard Worker
466*c8dee2aaSAndroid Build Coastguard Worker ShaderCodeDictionary* dict = recorder->priv().shaderCodeDictionary();
467*c8dee2aaSAndroid Build Coastguard Worker PaintParamsKeyBuilder builder(dict);
468*c8dee2aaSAndroid Build Coastguard Worker
469*c8dee2aaSAndroid Build Coastguard Worker // The initial layout we pass here is not important as it will be re-assigned when writing
470*c8dee2aaSAndroid Build Coastguard Worker // shading and geometry uniforms below.
471*c8dee2aaSAndroid Build Coastguard Worker PipelineDataGatherer gatherer(uniformLayout);
472*c8dee2aaSAndroid Build Coastguard Worker
473*c8dee2aaSAndroid Build Coastguard Worker std::vector<SortKey> keys;
474*c8dee2aaSAndroid Build Coastguard Worker keys.reserve(draws->renderStepCount());
475*c8dee2aaSAndroid Build Coastguard Worker
476*c8dee2aaSAndroid Build Coastguard Worker for (const DrawList::Draw& draw : draws->fDraws.items()) {
477*c8dee2aaSAndroid Build Coastguard Worker // If we have two different descriptors, such that the uniforms from the PaintParams can be
478*c8dee2aaSAndroid Build Coastguard Worker // bound independently of those used by the rest of the RenderStep, then we can upload now
479*c8dee2aaSAndroid Build Coastguard Worker // and remember the location for re-use on any RenderStep that does shading.
480*c8dee2aaSAndroid Build Coastguard Worker UniquePaintParamsID shaderID;
481*c8dee2aaSAndroid Build Coastguard Worker UniformDataCache::Index shadingUniformIndex = UniformDataCache::kInvalidIndex;
482*c8dee2aaSAndroid Build Coastguard Worker TextureDataBlock paintTextures;
483*c8dee2aaSAndroid Build Coastguard Worker
484*c8dee2aaSAndroid Build Coastguard Worker if (draw.fPaintParams.has_value()) {
485*c8dee2aaSAndroid Build Coastguard Worker shaderID = ExtractPaintData(recorder,
486*c8dee2aaSAndroid Build Coastguard Worker &gatherer,
487*c8dee2aaSAndroid Build Coastguard Worker &builder,
488*c8dee2aaSAndroid Build Coastguard Worker uniformLayout,
489*c8dee2aaSAndroid Build Coastguard Worker draw.fDrawParams.transform(),
490*c8dee2aaSAndroid Build Coastguard Worker draw.fPaintParams.value(),
491*c8dee2aaSAndroid Build Coastguard Worker draw.fDrawParams.geometry(),
492*c8dee2aaSAndroid Build Coastguard Worker targetInfo.colorInfo());
493*c8dee2aaSAndroid Build Coastguard Worker
494*c8dee2aaSAndroid Build Coastguard Worker if (shaderID.isValid()) {
495*c8dee2aaSAndroid Build Coastguard Worker if (gatherer.hasUniforms()) {
496*c8dee2aaSAndroid Build Coastguard Worker shadingUniformIndex =
497*c8dee2aaSAndroid Build Coastguard Worker shadingUniformDataCache.insert(gatherer.finishUniformDataBlock());
498*c8dee2aaSAndroid Build Coastguard Worker }
499*c8dee2aaSAndroid Build Coastguard Worker if (gatherer.hasTextures()) {
500*c8dee2aaSAndroid Build Coastguard Worker paintTextures = textureDataCache->insert(gatherer.textureDataBlock());
501*c8dee2aaSAndroid Build Coastguard Worker }
502*c8dee2aaSAndroid Build Coastguard Worker }
503*c8dee2aaSAndroid Build Coastguard Worker } // else depth-only
504*c8dee2aaSAndroid Build Coastguard Worker
505*c8dee2aaSAndroid Build Coastguard Worker // Create a sort key for every render step in this draw, extracting out any
506*c8dee2aaSAndroid Build Coastguard Worker // RenderStep-specific data.
507*c8dee2aaSAndroid Build Coastguard Worker for (int stepIndex = 0; stepIndex < draw.fRenderer->numRenderSteps(); ++stepIndex) {
508*c8dee2aaSAndroid Build Coastguard Worker const RenderStep* const step = draw.fRenderer->steps()[stepIndex];
509*c8dee2aaSAndroid Build Coastguard Worker const bool performsShading = draw.fPaintParams.has_value() && step->performsShading();
510*c8dee2aaSAndroid Build Coastguard Worker
511*c8dee2aaSAndroid Build Coastguard Worker GraphicsPipelineCache::Index pipelineIndex = pipelineCache.insert(
512*c8dee2aaSAndroid Build Coastguard Worker {step, performsShading ? shaderID : UniquePaintParamsID::InvalidID()});
513*c8dee2aaSAndroid Build Coastguard Worker
514*c8dee2aaSAndroid Build Coastguard Worker gatherer.resetWithNewLayout(uniformLayout);
515*c8dee2aaSAndroid Build Coastguard Worker step->writeUniformsAndTextures(draw.fDrawParams, &gatherer);
516*c8dee2aaSAndroid Build Coastguard Worker
517*c8dee2aaSAndroid Build Coastguard Worker UniformDataCache::Index geomUniformIndex =
518*c8dee2aaSAndroid Build Coastguard Worker gatherer.hasUniforms()
519*c8dee2aaSAndroid Build Coastguard Worker ? geometryUniformDataCache.insert(gatherer.finishUniformDataBlock())
520*c8dee2aaSAndroid Build Coastguard Worker : UniformDataCache::kInvalidIndex;
521*c8dee2aaSAndroid Build Coastguard Worker
522*c8dee2aaSAndroid Build Coastguard Worker TextureDataBlock stepTextures =
523*c8dee2aaSAndroid Build Coastguard Worker gatherer.hasTextures() ? textureDataCache->insert(gatherer.textureDataBlock())
524*c8dee2aaSAndroid Build Coastguard Worker : TextureDataBlock();
525*c8dee2aaSAndroid Build Coastguard Worker TextureBindingCache::Index textureIndex = textureBindingTracker.trackTextures(
526*c8dee2aaSAndroid Build Coastguard Worker performsShading ? paintTextures : TextureDataBlock(), stepTextures);
527*c8dee2aaSAndroid Build Coastguard Worker
528*c8dee2aaSAndroid Build Coastguard Worker keys.push_back({&draw, stepIndex, pipelineIndex,
529*c8dee2aaSAndroid Build Coastguard Worker geomUniformIndex, shadingUniformIndex, textureIndex});
530*c8dee2aaSAndroid Build Coastguard Worker }
531*c8dee2aaSAndroid Build Coastguard Worker
532*c8dee2aaSAndroid Build Coastguard Worker passBounds.join(draw.fDrawParams.clip().drawBounds());
533*c8dee2aaSAndroid Build Coastguard Worker drawPass->fDepthStencilFlags |= draw.fRenderer->depthStencilFlags();
534*c8dee2aaSAndroid Build Coastguard Worker drawPass->fRequiresMSAA |= draw.fRenderer->requiresMSAA();
535*c8dee2aaSAndroid Build Coastguard Worker }
536*c8dee2aaSAndroid Build Coastguard Worker
537*c8dee2aaSAndroid Build Coastguard Worker if (!gradientBufferTracker.writeData(gatherer.gradientBufferData(), bufferMgr)) {
538*c8dee2aaSAndroid Build Coastguard Worker // The necessary uniform data couldn't be written to the GPU, so the DrawPass is invalid.
539*c8dee2aaSAndroid Build Coastguard Worker // Early out now since the next Recording snap will fail.
540*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
541*c8dee2aaSAndroid Build Coastguard Worker }
542*c8dee2aaSAndroid Build Coastguard Worker
543*c8dee2aaSAndroid Build Coastguard Worker // TODO: Explore sorting algorithms; in all likelihood this will be mostly sorted already, so
544*c8dee2aaSAndroid Build Coastguard Worker // algorithms that approach O(n) in that condition may be favorable. Alternatively, could
545*c8dee2aaSAndroid Build Coastguard Worker // explore radix sort that is always O(n). Brief testing suggested std::sort was faster than
546*c8dee2aaSAndroid Build Coastguard Worker // std::stable_sort and SkTQSort on my [ml]'s Windows desktop. Also worth considering in-place
547*c8dee2aaSAndroid Build Coastguard Worker // vs. algorithms that require an extra O(n) storage.
548*c8dee2aaSAndroid Build Coastguard Worker // TODO: It's not strictly necessary, but would a stable sort be useful or just end up hiding
549*c8dee2aaSAndroid Build Coastguard Worker // bugs in the DrawOrder determination code?
550*c8dee2aaSAndroid Build Coastguard Worker std::sort(keys.begin(), keys.end());
551*c8dee2aaSAndroid Build Coastguard Worker
552*c8dee2aaSAndroid Build Coastguard Worker // Used to record vertex/instance data, buffer binds, and draw calls
553*c8dee2aaSAndroid Build Coastguard Worker DrawWriter drawWriter(&drawPass->fCommandList, bufferMgr);
554*c8dee2aaSAndroid Build Coastguard Worker GraphicsPipelineCache::Index lastPipeline = GraphicsPipelineCache::kInvalidIndex;
555*c8dee2aaSAndroid Build Coastguard Worker SkIRect lastScissor = SkIRect::MakeSize(targetInfo.dimensions());
556*c8dee2aaSAndroid Build Coastguard Worker
557*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(drawPass->fTarget->isFullyLazy() ||
558*c8dee2aaSAndroid Build Coastguard Worker SkIRect::MakeSize(drawPass->fTarget->dimensions()).contains(lastScissor));
559*c8dee2aaSAndroid Build Coastguard Worker drawPass->fCommandList.setScissor(lastScissor);
560*c8dee2aaSAndroid Build Coastguard Worker
561*c8dee2aaSAndroid Build Coastguard Worker // All large gradients pack their data into a single buffer throughout the draw pass,
562*c8dee2aaSAndroid Build Coastguard Worker // therefore the gradient buffer only needs to be bound once.
563*c8dee2aaSAndroid Build Coastguard Worker gradientBufferTracker.bindIfNeeded(&drawPass->fCommandList);
564*c8dee2aaSAndroid Build Coastguard Worker
565*c8dee2aaSAndroid Build Coastguard Worker UniformTracker geometryUniformTracker(useStorageBuffers);
566*c8dee2aaSAndroid Build Coastguard Worker UniformTracker shadingUniformTracker(useStorageBuffers);
567*c8dee2aaSAndroid Build Coastguard Worker
568*c8dee2aaSAndroid Build Coastguard Worker // TODO(b/372953722): Remove this forced binding command behavior once dst copies are always
569*c8dee2aaSAndroid Build Coastguard Worker // bound separately from the rest of the textures.
570*c8dee2aaSAndroid Build Coastguard Worker const bool rebindTexturesOnPipelineChange =
571*c8dee2aaSAndroid Build Coastguard Worker recorder->priv().caps()->getDstReadRequirement() == DstReadRequirement::kTextureCopy;
572*c8dee2aaSAndroid Build Coastguard Worker
573*c8dee2aaSAndroid Build Coastguard Worker for (const SortKey& key : keys) {
574*c8dee2aaSAndroid Build Coastguard Worker const DrawList::Draw& draw = key.draw();
575*c8dee2aaSAndroid Build Coastguard Worker const RenderStep& renderStep = key.renderStep();
576*c8dee2aaSAndroid Build Coastguard Worker
577*c8dee2aaSAndroid Build Coastguard Worker const bool pipelineChange = key.pipelineIndex() != lastPipeline;
578*c8dee2aaSAndroid Build Coastguard Worker
579*c8dee2aaSAndroid Build Coastguard Worker const bool geomBindingChange = geometryUniformTracker.writeUniforms(
580*c8dee2aaSAndroid Build Coastguard Worker geometryUniformDataCache, bufferMgr, key.geometryUniformIndex());
581*c8dee2aaSAndroid Build Coastguard Worker const bool shadingBindingChange = shadingUniformTracker.writeUniforms(
582*c8dee2aaSAndroid Build Coastguard Worker shadingUniformDataCache, bufferMgr, key.shadingUniformIndex());
583*c8dee2aaSAndroid Build Coastguard Worker
584*c8dee2aaSAndroid Build Coastguard Worker // TODO(b/372953722): The Dawn and Vulkan CommandBuffer implementations currently append any
585*c8dee2aaSAndroid Build Coastguard Worker // dst copy to the texture bind group/descriptor set automatically when processing a
586*c8dee2aaSAndroid Build Coastguard Worker // BindTexturesAndSamplers call because they use a single group to contain all textures.
587*c8dee2aaSAndroid Build Coastguard Worker // However, from the DrawPass POV, we can run into the scenario where two pipelines have the
588*c8dee2aaSAndroid Build Coastguard Worker // same textures+samplers except one requires a dst-copy and the other does not. In this
589*c8dee2aaSAndroid Build Coastguard Worker // case we wouldn't necessarily insert a new command when the pipeline changed and then
590*c8dee2aaSAndroid Build Coastguard Worker // end up with layout validation errors.
591*c8dee2aaSAndroid Build Coastguard Worker const bool textureBindingsChange = textureBindingTracker.setCurrentTextureBindings(
592*c8dee2aaSAndroid Build Coastguard Worker key.textureBindingIndex()) ||
593*c8dee2aaSAndroid Build Coastguard Worker (rebindTexturesOnPipelineChange && pipelineChange &&
594*c8dee2aaSAndroid Build Coastguard Worker key.textureBindingIndex() != TextureBindingCache::kInvalidIndex);
595*c8dee2aaSAndroid Build Coastguard Worker const SkIRect* newScissor = draw.fDrawParams.clip().scissor() != lastScissor ?
596*c8dee2aaSAndroid Build Coastguard Worker &draw.fDrawParams.clip().scissor() : nullptr;
597*c8dee2aaSAndroid Build Coastguard Worker
598*c8dee2aaSAndroid Build Coastguard Worker const bool stateChange = geomBindingChange ||
599*c8dee2aaSAndroid Build Coastguard Worker shadingBindingChange ||
600*c8dee2aaSAndroid Build Coastguard Worker textureBindingsChange ||
601*c8dee2aaSAndroid Build Coastguard Worker SkToBool(newScissor);
602*c8dee2aaSAndroid Build Coastguard Worker
603*c8dee2aaSAndroid Build Coastguard Worker // Update DrawWriter *before* we actually change any state so that accumulated draws from
604*c8dee2aaSAndroid Build Coastguard Worker // the previous state use the proper state.
605*c8dee2aaSAndroid Build Coastguard Worker if (pipelineChange) {
606*c8dee2aaSAndroid Build Coastguard Worker drawWriter.newPipelineState(renderStep.primitiveType(),
607*c8dee2aaSAndroid Build Coastguard Worker renderStep.vertexStride(),
608*c8dee2aaSAndroid Build Coastguard Worker renderStep.instanceStride());
609*c8dee2aaSAndroid Build Coastguard Worker } else if (stateChange) {
610*c8dee2aaSAndroid Build Coastguard Worker drawWriter.newDynamicState();
611*c8dee2aaSAndroid Build Coastguard Worker }
612*c8dee2aaSAndroid Build Coastguard Worker
613*c8dee2aaSAndroid Build Coastguard Worker // Make state changes before accumulating new draw data
614*c8dee2aaSAndroid Build Coastguard Worker if (pipelineChange) {
615*c8dee2aaSAndroid Build Coastguard Worker drawPass->fCommandList.bindGraphicsPipeline(key.pipelineIndex());
616*c8dee2aaSAndroid Build Coastguard Worker lastPipeline = key.pipelineIndex();
617*c8dee2aaSAndroid Build Coastguard Worker }
618*c8dee2aaSAndroid Build Coastguard Worker if (stateChange) {
619*c8dee2aaSAndroid Build Coastguard Worker if (geomBindingChange) {
620*c8dee2aaSAndroid Build Coastguard Worker geometryUniformTracker.bindUniforms(UniformSlot::kRenderStep,
621*c8dee2aaSAndroid Build Coastguard Worker &drawPass->fCommandList);
622*c8dee2aaSAndroid Build Coastguard Worker }
623*c8dee2aaSAndroid Build Coastguard Worker if (shadingBindingChange) {
624*c8dee2aaSAndroid Build Coastguard Worker shadingUniformTracker.bindUniforms(UniformSlot::kPaint, &drawPass->fCommandList);
625*c8dee2aaSAndroid Build Coastguard Worker }
626*c8dee2aaSAndroid Build Coastguard Worker if (textureBindingsChange) {
627*c8dee2aaSAndroid Build Coastguard Worker textureBindingTracker.bindTextures(&drawPass->fCommandList);
628*c8dee2aaSAndroid Build Coastguard Worker }
629*c8dee2aaSAndroid Build Coastguard Worker if (newScissor) {
630*c8dee2aaSAndroid Build Coastguard Worker drawPass->fCommandList.setScissor(*newScissor);
631*c8dee2aaSAndroid Build Coastguard Worker lastScissor = *newScissor;
632*c8dee2aaSAndroid Build Coastguard Worker }
633*c8dee2aaSAndroid Build Coastguard Worker }
634*c8dee2aaSAndroid Build Coastguard Worker
635*c8dee2aaSAndroid Build Coastguard Worker uint32_t geometrySsboIndex = useStorageBuffers ? geometryUniformTracker.ssboIndex() : 0;
636*c8dee2aaSAndroid Build Coastguard Worker uint32_t shadingSsboIndex = useStorageBuffers ? shadingUniformTracker.ssboIndex() : 0;
637*c8dee2aaSAndroid Build Coastguard Worker skvx::uint2 ssboIndices = {geometrySsboIndex, shadingSsboIndex};
638*c8dee2aaSAndroid Build Coastguard Worker renderStep.writeVertices(&drawWriter, draw.fDrawParams, ssboIndices);
639*c8dee2aaSAndroid Build Coastguard Worker
640*c8dee2aaSAndroid Build Coastguard Worker if (bufferMgr->hasMappingFailed()) {
641*c8dee2aaSAndroid Build Coastguard Worker SKGPU_LOG_W("Failed to write necessary vertex/instance data for DrawPass, dropping!");
642*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
643*c8dee2aaSAndroid Build Coastguard Worker }
644*c8dee2aaSAndroid Build Coastguard Worker }
645*c8dee2aaSAndroid Build Coastguard Worker // Finish recording draw calls for any collected data at the end of the loop
646*c8dee2aaSAndroid Build Coastguard Worker drawWriter.flush();
647*c8dee2aaSAndroid Build Coastguard Worker
648*c8dee2aaSAndroid Build Coastguard Worker drawPass->fBounds = passBounds.roundOut().asSkIRect();
649*c8dee2aaSAndroid Build Coastguard Worker
650*c8dee2aaSAndroid Build Coastguard Worker drawPass->fPipelineDescs = pipelineCache.detach();
651*c8dee2aaSAndroid Build Coastguard Worker drawPass->fSamplerDescs = textureBindingTracker.detachSamplers();
652*c8dee2aaSAndroid Build Coastguard Worker drawPass->fSampledTextures = textureBindingTracker.detachTextures();
653*c8dee2aaSAndroid Build Coastguard Worker
654*c8dee2aaSAndroid Build Coastguard Worker TRACE_COUNTER1("skia.gpu", "# pipelines", drawPass->fPipelineDescs.size());
655*c8dee2aaSAndroid Build Coastguard Worker TRACE_COUNTER1("skia.gpu", "# textures", drawPass->fSampledTextures.size());
656*c8dee2aaSAndroid Build Coastguard Worker TRACE_COUNTER1("skia.gpu", "# commands", drawPass->fCommandList.count());
657*c8dee2aaSAndroid Build Coastguard Worker
658*c8dee2aaSAndroid Build Coastguard Worker return drawPass;
659*c8dee2aaSAndroid Build Coastguard Worker }
660*c8dee2aaSAndroid Build Coastguard Worker
prepareResources(ResourceProvider * resourceProvider,const RuntimeEffectDictionary * runtimeDict,const RenderPassDesc & renderPassDesc)661*c8dee2aaSAndroid Build Coastguard Worker bool DrawPass::prepareResources(ResourceProvider* resourceProvider,
662*c8dee2aaSAndroid Build Coastguard Worker const RuntimeEffectDictionary* runtimeDict,
663*c8dee2aaSAndroid Build Coastguard Worker const RenderPassDesc& renderPassDesc) {
664*c8dee2aaSAndroid Build Coastguard Worker TRACE_EVENT0("skia.gpu", TRACE_FUNC);
665*c8dee2aaSAndroid Build Coastguard Worker
666*c8dee2aaSAndroid Build Coastguard Worker fFullPipelines.reserve(fFullPipelines.size() + fPipelineDescs.size());
667*c8dee2aaSAndroid Build Coastguard Worker for (const GraphicsPipelineDesc& pipelineDesc : fPipelineDescs) {
668*c8dee2aaSAndroid Build Coastguard Worker auto pipeline = resourceProvider->findOrCreateGraphicsPipeline(runtimeDict,
669*c8dee2aaSAndroid Build Coastguard Worker pipelineDesc,
670*c8dee2aaSAndroid Build Coastguard Worker renderPassDesc);
671*c8dee2aaSAndroid Build Coastguard Worker if (!pipeline) {
672*c8dee2aaSAndroid Build Coastguard Worker SKGPU_LOG_W("Failed to create GraphicsPipeline for draw in RenderPass. Dropping pass!");
673*c8dee2aaSAndroid Build Coastguard Worker return false;
674*c8dee2aaSAndroid Build Coastguard Worker }
675*c8dee2aaSAndroid Build Coastguard Worker fFullPipelines.push_back(std::move(pipeline));
676*c8dee2aaSAndroid Build Coastguard Worker }
677*c8dee2aaSAndroid Build Coastguard Worker // The DrawPass may be long lived on a Recording and we no longer need the GraphicPipelineDescs
678*c8dee2aaSAndroid Build Coastguard Worker // once we've created pipelines, so we drop the storage for them here.
679*c8dee2aaSAndroid Build Coastguard Worker fPipelineDescs.clear();
680*c8dee2aaSAndroid Build Coastguard Worker
681*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_DEBUG)
682*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < fSampledTextures.size(); ++i) {
683*c8dee2aaSAndroid Build Coastguard Worker // It should not have been possible to draw an Image that has an invalid texture info
684*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fSampledTextures[i]->textureInfo().isValid());
685*c8dee2aaSAndroid Build Coastguard Worker // Tasks should have been ordered to instantiate any scratch textures already, or any
686*c8dee2aaSAndroid Build Coastguard Worker // client-owned image will have been instantiated at creation.
687*c8dee2aaSAndroid Build Coastguard Worker SkASSERTF(fSampledTextures[i]->isInstantiated() ||
688*c8dee2aaSAndroid Build Coastguard Worker fSampledTextures[i]->isLazy(),
689*c8dee2aaSAndroid Build Coastguard Worker "proxy label = %s", fSampledTextures[i]->label());
690*c8dee2aaSAndroid Build Coastguard Worker }
691*c8dee2aaSAndroid Build Coastguard Worker #endif
692*c8dee2aaSAndroid Build Coastguard Worker
693*c8dee2aaSAndroid Build Coastguard Worker fSamplers.reserve(fSamplers.size() + fSamplerDescs.size());
694*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < fSamplerDescs.size(); ++i) {
695*c8dee2aaSAndroid Build Coastguard Worker sk_sp<Sampler> sampler = resourceProvider->findOrCreateCompatibleSampler(fSamplerDescs[i]);
696*c8dee2aaSAndroid Build Coastguard Worker if (!sampler) {
697*c8dee2aaSAndroid Build Coastguard Worker SKGPU_LOG_W("Failed to create sampler. Will not create renderpass!");
698*c8dee2aaSAndroid Build Coastguard Worker return false;
699*c8dee2aaSAndroid Build Coastguard Worker }
700*c8dee2aaSAndroid Build Coastguard Worker fSamplers.push_back(std::move(sampler));
701*c8dee2aaSAndroid Build Coastguard Worker }
702*c8dee2aaSAndroid Build Coastguard Worker // The DrawPass may be long lived on a Recording and we no longer need the SamplerDescs
703*c8dee2aaSAndroid Build Coastguard Worker // once we've created Samplers, so we drop the storage for them here.
704*c8dee2aaSAndroid Build Coastguard Worker fSamplerDescs.clear();
705*c8dee2aaSAndroid Build Coastguard Worker
706*c8dee2aaSAndroid Build Coastguard Worker return true;
707*c8dee2aaSAndroid Build Coastguard Worker }
708*c8dee2aaSAndroid Build Coastguard Worker
addResourceRefs(CommandBuffer * commandBuffer) const709*c8dee2aaSAndroid Build Coastguard Worker void DrawPass::addResourceRefs(CommandBuffer* commandBuffer) const {
710*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < fFullPipelines.size(); ++i) {
711*c8dee2aaSAndroid Build Coastguard Worker commandBuffer->trackResource(fFullPipelines[i]);
712*c8dee2aaSAndroid Build Coastguard Worker }
713*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < fSampledTextures.size(); ++i) {
714*c8dee2aaSAndroid Build Coastguard Worker commandBuffer->trackCommandBufferResource(fSampledTextures[i]->refTexture());
715*c8dee2aaSAndroid Build Coastguard Worker }
716*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < fSamplers.size(); ++i) {
717*c8dee2aaSAndroid Build Coastguard Worker commandBuffer->trackResource(fSamplers[i]);
718*c8dee2aaSAndroid Build Coastguard Worker }
719*c8dee2aaSAndroid Build Coastguard Worker }
720*c8dee2aaSAndroid Build Coastguard Worker
getTexture(size_t index) const721*c8dee2aaSAndroid Build Coastguard Worker const Texture* DrawPass::getTexture(size_t index) const {
722*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(index < SkToSizeT(fSampledTextures.size()));
723*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fSampledTextures[index]);
724*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fSampledTextures[index]->texture());
725*c8dee2aaSAndroid Build Coastguard Worker return fSampledTextures[index]->texture();
726*c8dee2aaSAndroid Build Coastguard Worker }
getSampler(size_t index) const727*c8dee2aaSAndroid Build Coastguard Worker const Sampler* DrawPass::getSampler(size_t index) const {
728*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(index < SkToSizeT(fSamplers.size()));
729*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fSamplers[index]);
730*c8dee2aaSAndroid Build Coastguard Worker return fSamplers[index].get();
731*c8dee2aaSAndroid Build Coastguard Worker }
732*c8dee2aaSAndroid Build Coastguard Worker
733*c8dee2aaSAndroid Build Coastguard Worker } // namespace skgpu::graphite
734