1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2021 Google LLC
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "experimental/graphite/src/DrawPass.h"
9cb93a386Sopenharmony_ci
10cb93a386Sopenharmony_ci#include "experimental/graphite/include/GraphiteTypes.h"
11cb93a386Sopenharmony_ci#include "experimental/graphite/src/Buffer.h"
12cb93a386Sopenharmony_ci#include "experimental/graphite/src/ContextUtils.h"
13cb93a386Sopenharmony_ci#include "experimental/graphite/src/DrawBufferManager.h"
14cb93a386Sopenharmony_ci#include "experimental/graphite/src/DrawContext.h"
15cb93a386Sopenharmony_ci#include "experimental/graphite/src/DrawList.h"
16cb93a386Sopenharmony_ci#include "experimental/graphite/src/ProgramCache.h"
17cb93a386Sopenharmony_ci#include "experimental/graphite/src/Recorder.h"
18cb93a386Sopenharmony_ci#include "experimental/graphite/src/Renderer.h"
19cb93a386Sopenharmony_ci#include "experimental/graphite/src/TextureProxy.h"
20cb93a386Sopenharmony_ci#include "experimental/graphite/src/UniformCache.h"
21cb93a386Sopenharmony_ci#include "experimental/graphite/src/geom/BoundsManager.h"
22cb93a386Sopenharmony_ci
23cb93a386Sopenharmony_ci#include "src/core/SkMathPriv.h"
24cb93a386Sopenharmony_ci#include "src/core/SkUtils.h"
25cb93a386Sopenharmony_ci#include "src/gpu/BufferWriter.h"
26cb93a386Sopenharmony_ci
27cb93a386Sopenharmony_ci#include <algorithm>
28cb93a386Sopenharmony_ci
29cb93a386Sopenharmony_cinamespace {
30cb93a386Sopenharmony_ci
31cb93a386Sopenharmony_ci// Retrieve the program ID and uniformData ID
32cb93a386Sopenharmony_cistd::tuple<uint32_t, uint32_t> get_ids_from_paint(skgpu::Recorder* recorder,
33cb93a386Sopenharmony_ci                                                  skgpu::PaintParams params) {
34cb93a386Sopenharmony_ci    // TODO: add an ExtractCombo that takes PaintParams directly?
35cb93a386Sopenharmony_ci    SkPaint p;
36cb93a386Sopenharmony_ci
37cb93a386Sopenharmony_ci    p.setColor(params.color());
38cb93a386Sopenharmony_ci    p.setBlendMode(params.blendMode());
39cb93a386Sopenharmony_ci    p.setShader(params.refShader());
40cb93a386Sopenharmony_ci
41cb93a386Sopenharmony_ci    // TODO: perhaps just return the ids here rather than the sk_sps?
42cb93a386Sopenharmony_ci    auto [ combo, uniformData] = ExtractCombo(recorder->uniformCache(), p);
43cb93a386Sopenharmony_ci    auto programInfo = recorder->programCache()->findOrCreateProgram(combo);
44cb93a386Sopenharmony_ci
45cb93a386Sopenharmony_ci    return { programInfo->id(), uniformData->id() };
46cb93a386Sopenharmony_ci}
47cb93a386Sopenharmony_ci
48cb93a386Sopenharmony_ci} // anonymous namespace
49cb93a386Sopenharmony_ci
50cb93a386Sopenharmony_cinamespace skgpu {
51cb93a386Sopenharmony_ci
52cb93a386Sopenharmony_ci/**
53cb93a386Sopenharmony_ci * Each Draw in a DrawList might be processed by multiple RenderSteps (determined by the Draw's
54cb93a386Sopenharmony_ci * Renderer), which can be sorted independently. Each (step, draw) pair produces its own SortKey.
55cb93a386Sopenharmony_ci *
56cb93a386Sopenharmony_ci * The goal of sorting draws for the DrawPass is to minimize pipeline transitions and dynamic binds
57cb93a386Sopenharmony_ci * within a pipeline, while still respecting the overall painter's order. This decreases the number
58cb93a386Sopenharmony_ci * of low-level draw commands in a command buffer and increases the size of those, allowing the GPU
59cb93a386Sopenharmony_ci * to operate more efficiently and have fewer bubbles within its own instruction stream.
60cb93a386Sopenharmony_ci *
61cb93a386Sopenharmony_ci * The Draw's CompresssedPaintersOrder and DisjointStencilINdex represent the most significant bits
62cb93a386Sopenharmony_ci * of the key, and are shared by all SortKeys produced by the same draw. Next, the pipeline
63cb93a386Sopenharmony_ci * description is encoded in two steps:
64cb93a386Sopenharmony_ci *  1. The index of the RenderStep packed in the high bits to ensure each step for a draw is
65cb93a386Sopenharmony_ci *     ordered correctly.
66cb93a386Sopenharmony_ci *  2. An index into a cache of pipeline descriptions is used to encode the identity of the
67cb93a386Sopenharmony_ci *     pipeline (SortKeys that differ in the bits from #1 necessarily would have different
68cb93a386Sopenharmony_ci *     descriptions, but then the specific ordering of the RenderSteps isn't enforced).
69cb93a386Sopenharmony_ci * Last, the SortKey encodes an index into the set of uniform bindings accumulated for a DrawPass.
70cb93a386Sopenharmony_ci * This allows the SortKey to cluster draw steps that have both a compatible pipeline and do not
71cb93a386Sopenharmony_ci * require rebinding uniform data or other state (e.g. scissor). Since the uniform data index and
72cb93a386Sopenharmony_ci * the pipeline description index are packed into indices and not actual pointers, a given SortKey
73cb93a386Sopenharmony_ci * is only valid for the a specific DrawList->DrawPass conversion.
74cb93a386Sopenharmony_ci */
75cb93a386Sopenharmony_ciclass DrawPass::SortKey {
76cb93a386Sopenharmony_cipublic:
77cb93a386Sopenharmony_ci    SortKey(const DrawList::Draw* draw,
78cb93a386Sopenharmony_ci            int renderStep,
79cb93a386Sopenharmony_ci            uint32_t pipelineIndex,
80cb93a386Sopenharmony_ci            uint32_t geomUniformIndex,
81cb93a386Sopenharmony_ci            uint32_t shadingUniformIndex)
82cb93a386Sopenharmony_ci        : fPipelineKey{draw->fOrder.paintOrder().bits(),
83cb93a386Sopenharmony_ci                       draw->fOrder.stencilIndex().bits(),
84cb93a386Sopenharmony_ci                       static_cast<uint32_t>(renderStep),
85cb93a386Sopenharmony_ci                       pipelineIndex}
86cb93a386Sopenharmony_ci        , fUniformKey{geomUniformIndex, shadingUniformIndex}
87cb93a386Sopenharmony_ci        , fDraw(draw) {
88cb93a386Sopenharmony_ci    }
89cb93a386Sopenharmony_ci
90cb93a386Sopenharmony_ci    bool operator<(const SortKey& k) const {
91cb93a386Sopenharmony_ci        uint64_t k1 = this->pipelineKey();
92cb93a386Sopenharmony_ci        uint64_t k2 = k.pipelineKey();
93cb93a386Sopenharmony_ci        return k1 < k2 || (k1 == k2 && this->uniformKey() < k.uniformKey());
94cb93a386Sopenharmony_ci    }
95cb93a386Sopenharmony_ci
96cb93a386Sopenharmony_ci    const DrawList::Draw* draw() const { return fDraw; }
97cb93a386Sopenharmony_ci    uint32_t pipeline() const { return fPipelineKey.fPipeline; }
98cb93a386Sopenharmony_ci    int renderStep() const { return static_cast<int>(fPipelineKey.fRenderStep); }
99cb93a386Sopenharmony_ci
100cb93a386Sopenharmony_ci    uint32_t geometryUniforms() const { return fUniformKey.fGeometryIndex; }
101cb93a386Sopenharmony_ci    uint32_t shadingUniforms() const { return fUniformKey.fShadingIndex; }
102cb93a386Sopenharmony_ci
103cb93a386Sopenharmony_ciprivate:
104cb93a386Sopenharmony_ci    // Fields are ordered from most-significant to lowest when sorting by 128-bit value.
105cb93a386Sopenharmony_ci    struct {
106cb93a386Sopenharmony_ci        uint32_t fColorDepthOrder : 16; // sizeof(CompressedPaintersOrder)
107cb93a386Sopenharmony_ci        uint32_t fStencilOrder    : 16; // sizeof(DisjointStencilIndex)
108cb93a386Sopenharmony_ci        uint32_t fRenderStep      : 2;  // bits >= log2(Renderer::kMaxRenderSteps)
109cb93a386Sopenharmony_ci        uint32_t fPipeline        : 30; // bits >= log2(max steps * DrawList::kMaxDraws)
110cb93a386Sopenharmony_ci    } fPipelineKey; // NOTE: named for bit-punning, can't take address of a bit-field
111cb93a386Sopenharmony_ci
112cb93a386Sopenharmony_ci    uint64_t pipelineKey() const { return sk_bit_cast<uint64_t>(fPipelineKey); }
113cb93a386Sopenharmony_ci
114cb93a386Sopenharmony_ci    struct {
115cb93a386Sopenharmony_ci        uint32_t fGeometryIndex; // bits >= log2(max steps * max draw count)
116cb93a386Sopenharmony_ci        uint32_t fShadingIndex;  //  ""
117cb93a386Sopenharmony_ci    } fUniformKey;
118cb93a386Sopenharmony_ci
119cb93a386Sopenharmony_ci    uint64_t uniformKey() const { return sk_bit_cast<uint64_t>(fUniformKey); }
120cb93a386Sopenharmony_ci
121cb93a386Sopenharmony_ci    // Backpointer to the draw that produced the sort key
122cb93a386Sopenharmony_ci    const DrawList::Draw* fDraw;
123cb93a386Sopenharmony_ci
124cb93a386Sopenharmony_ci    static_assert(16 >= sizeof(CompressedPaintersOrder));
125cb93a386Sopenharmony_ci    static_assert(16 >= sizeof(DisjointStencilIndex));
126cb93a386Sopenharmony_ci    static_assert(2  >= SkNextLog2_portable(Renderer::kMaxRenderSteps));
127cb93a386Sopenharmony_ci    static_assert(30 >= SkNextLog2_portable(Renderer::kMaxRenderSteps * DrawList::kMaxDraws));
128cb93a386Sopenharmony_ci};
129cb93a386Sopenharmony_ci
130cb93a386Sopenharmony_ci///////////////////////////////////////////////////////////////////////////////////////////////////
131cb93a386Sopenharmony_ci
132cb93a386Sopenharmony_cinamespace {
133cb93a386Sopenharmony_ci
134cb93a386Sopenharmony_ciskgpu::UniformData* lookup(skgpu::Recorder* recorder, uint32_t uniformID) {
135cb93a386Sopenharmony_ci    // TODO: just return a raw 'UniformData*' here
136cb93a386Sopenharmony_ci    sk_sp<skgpu::UniformData> tmp = recorder->uniformCache()->lookup(uniformID);
137cb93a386Sopenharmony_ci    return tmp.get();
138cb93a386Sopenharmony_ci}
139cb93a386Sopenharmony_ci
140cb93a386Sopenharmony_ci} // anonymous namespace
141cb93a386Sopenharmony_ci
142cb93a386Sopenharmony_ciDrawPass::DrawPass(sk_sp<TextureProxy> target, const SkIRect& bounds,
143cb93a386Sopenharmony_ci                   std::pair<LoadOp, StoreOp> ops, std::array<float, 4> clearColor,
144cb93a386Sopenharmony_ci                   bool requiresStencil, bool requiresMSAA)
145cb93a386Sopenharmony_ci        : fTarget(std::move(target))
146cb93a386Sopenharmony_ci        , fBounds(bounds)
147cb93a386Sopenharmony_ci        , fOps(ops)
148cb93a386Sopenharmony_ci        , fClearColor(clearColor)
149cb93a386Sopenharmony_ci        , fRequiresStencil(requiresStencil)
150cb93a386Sopenharmony_ci        , fRequiresMSAA(requiresMSAA) {}
151cb93a386Sopenharmony_ci
152cb93a386Sopenharmony_ciDrawPass::~DrawPass() = default;
153cb93a386Sopenharmony_ci
154cb93a386Sopenharmony_cistd::unique_ptr<DrawPass> DrawPass::Make(Recorder* recorder,
155cb93a386Sopenharmony_ci                                         std::unique_ptr<DrawList> draws,
156cb93a386Sopenharmony_ci                                         sk_sp<TextureProxy> target,
157cb93a386Sopenharmony_ci                                         std::pair<LoadOp, StoreOp> ops,
158cb93a386Sopenharmony_ci                                         std::array<float, 4> clearColor,
159cb93a386Sopenharmony_ci                                         const BoundsManager* occlusionCuller) {
160cb93a386Sopenharmony_ci    // NOTE: This assert is here to ensure SortKey is as tightly packed as possible. Any change to
161cb93a386Sopenharmony_ci    // its size should be done with care and good reason. The performance of sorting the keys is
162cb93a386Sopenharmony_ci    // heavily tied to the total size.
163cb93a386Sopenharmony_ci    //
164cb93a386Sopenharmony_ci    // At 24 bytes (current), sorting is about 30% slower than if SortKey could be packed into just
165cb93a386Sopenharmony_ci    // 16 bytes. There are several ways this could be done if necessary:
166cb93a386Sopenharmony_ci    //  - Restricting the max draw count to 16k (14-bits) and only using a single index to refer to
167cb93a386Sopenharmony_ci    //    the uniform data => 8 bytes of key, 8 bytes of pointer.
168cb93a386Sopenharmony_ci    //  - Restrict the max draw count to 32k (15-bits), use a single uniform index, and steal the
169cb93a386Sopenharmony_ci    //    4 low bits from the Draw* pointer since it's 16 byte aligned.
170cb93a386Sopenharmony_ci    //  - Compact the Draw* to an index into the original collection, although that has extra
171cb93a386Sopenharmony_ci    //    indirection and does not work as well with SkTBlockList.
172cb93a386Sopenharmony_ci    // In pseudo tests, manipulating the pointer or having to mask out indices was about 15% slower
173cb93a386Sopenharmony_ci    // than an 8 byte key and unmodified pointer.
174cb93a386Sopenharmony_ci    static_assert(sizeof(DrawPass::SortKey) == 16 + sizeof(void*));
175cb93a386Sopenharmony_ci
176cb93a386Sopenharmony_ci    bool requiresStencil = false;
177cb93a386Sopenharmony_ci    bool requiresMSAA = false;
178cb93a386Sopenharmony_ci    Rect passBounds = Rect::InfiniteInverted();
179cb93a386Sopenharmony_ci
180cb93a386Sopenharmony_ci    std::vector<SortKey> keys;
181cb93a386Sopenharmony_ci    keys.reserve(draws->renderStepCount()); // will not exceed but may use less with occluded draws
182cb93a386Sopenharmony_ci
183cb93a386Sopenharmony_ci    for (const DrawList::Draw& draw : draws->fDraws.items()) {
184cb93a386Sopenharmony_ci        if (occlusionCuller && occlusionCuller->isOccluded(draw.fClip.drawBounds(),
185cb93a386Sopenharmony_ci                                                           draw.fOrder.depth())) {
186cb93a386Sopenharmony_ci            continue;
187cb93a386Sopenharmony_ci        }
188cb93a386Sopenharmony_ci
189cb93a386Sopenharmony_ci        // If we have two different descriptors, such that the uniforms from the PaintParams can be
190cb93a386Sopenharmony_ci        // bound independently of those used by the rest of the RenderStep, then we can upload now
191cb93a386Sopenharmony_ci        // and remember the location for re-use on any RenderStep that does shading.
192cb93a386Sopenharmony_ci        uint32_t programID = ProgramCache::kInvalidProgramID;
193cb93a386Sopenharmony_ci        uint32_t shadingUniformID = UniformData::kInvalidUniformID;
194cb93a386Sopenharmony_ci        if (draw.fPaintParams.has_value()) {
195cb93a386Sopenharmony_ci            std::tie(programID, shadingUniformID) = get_ids_from_paint(recorder,
196cb93a386Sopenharmony_ci                                                                       draw.fPaintParams.value());
197cb93a386Sopenharmony_ci        }
198cb93a386Sopenharmony_ci
199cb93a386Sopenharmony_ci        for (int stepIndex = 0; stepIndex < draw.fRenderer.numRenderSteps(); ++stepIndex) {
200cb93a386Sopenharmony_ci            const RenderStep* const step = draw.fRenderer.steps()[stepIndex];
201cb93a386Sopenharmony_ci
202cb93a386Sopenharmony_ci            // TODO ask step to generate a pipeline description based on the above shading code, and
203cb93a386Sopenharmony_ci            // have pipelineIndex point to that description in the accumulated list of descs
204cb93a386Sopenharmony_ci            uint32_t pipelineIndex = 0;
205cb93a386Sopenharmony_ci            // TODO step writes out geometry uniforms and have geomIndex point to that buffer data,
206cb93a386Sopenharmony_ci            // providing shape, transform, scissor, and paint depth to RenderStep
207cb93a386Sopenharmony_ci            uint32_t geometryIndex = 0;
208cb93a386Sopenharmony_ci
209cb93a386Sopenharmony_ci            uint32_t shadingIndex = UniformData::kInvalidUniformID;
210cb93a386Sopenharmony_ci
211cb93a386Sopenharmony_ci            const bool performsShading = draw.fPaintParams.has_value() && step->performsShading();
212cb93a386Sopenharmony_ci            if (performsShading) {
213cb93a386Sopenharmony_ci                // TODO: we need to combine the 'programID' with the RenderPass info and the
214cb93a386Sopenharmony_ci                // geometric rendering method to get the true 'pipelineIndex'
215cb93a386Sopenharmony_ci                pipelineIndex = programID;
216cb93a386Sopenharmony_ci                shadingIndex = shadingUniformID;
217cb93a386Sopenharmony_ci            } else {
218cb93a386Sopenharmony_ci                // TODO: fill in 'pipelineIndex' for Chris' stencil/depth draws
219cb93a386Sopenharmony_ci            }
220cb93a386Sopenharmony_ci
221cb93a386Sopenharmony_ci            keys.push_back({&draw, stepIndex, pipelineIndex, geometryIndex, shadingIndex});
222cb93a386Sopenharmony_ci        }
223cb93a386Sopenharmony_ci
224cb93a386Sopenharmony_ci        passBounds.join(draw.fClip.drawBounds());
225cb93a386Sopenharmony_ci        requiresStencil |= draw.fRenderer.requiresStencil();
226cb93a386Sopenharmony_ci        requiresMSAA |= draw.fRenderer.requiresMSAA();
227cb93a386Sopenharmony_ci    }
228cb93a386Sopenharmony_ci
229cb93a386Sopenharmony_ci    // TODO: Explore sorting algorithms; in all likelihood this will be mostly sorted already, so
230cb93a386Sopenharmony_ci    // algorithms that approach O(n) in that condition may be favorable. Alternatively, could
231cb93a386Sopenharmony_ci    // explore radix sort that is always O(n). Brief testing suggested std::sort was faster than
232cb93a386Sopenharmony_ci    // std::stable_sort and SkTQSort on my [ml]'s Windows desktop. Also worth considering in-place
233cb93a386Sopenharmony_ci    // vs. algorithms that require an extra O(n) storage.
234cb93a386Sopenharmony_ci    // TODO: It's not strictly necessary, but would a stable sort be useful or just end up hiding
235cb93a386Sopenharmony_ci    // bugs in the DrawOrder determination code?
236cb93a386Sopenharmony_ci    std::sort(keys.begin(), keys.end());
237cb93a386Sopenharmony_ci
238cb93a386Sopenharmony_ci    DrawBufferManager* bufferMgr = recorder->drawBufferManager();
239cb93a386Sopenharmony_ci
240cb93a386Sopenharmony_ci    uint32_t lastPipeline = 0;
241cb93a386Sopenharmony_ci    uint32_t lastShadingUniforms = UniformData::kInvalidUniformID;
242cb93a386Sopenharmony_ci    uint32_t lastGeometryUniforms = 0;
243cb93a386Sopenharmony_ci    SkIRect lastScissor = SkIRect::MakeSize(target->dimensions());
244cb93a386Sopenharmony_ci    Buffer* lastBoundVertexBuffer = nullptr;
245cb93a386Sopenharmony_ci    Buffer* lastBoundIndexBuffer = nullptr;
246cb93a386Sopenharmony_ci
247cb93a386Sopenharmony_ci    for (const SortKey& key : keys) {
248cb93a386Sopenharmony_ci        const DrawList::Draw& draw = *key.draw();
249cb93a386Sopenharmony_ci        int renderStep = key.renderStep();
250cb93a386Sopenharmony_ci
251cb93a386Sopenharmony_ci        size_t vertexSize = draw.requiredVertexSpace(renderStep);
252cb93a386Sopenharmony_ci        size_t indexSize = draw.requiredIndexSpace(renderStep);
253cb93a386Sopenharmony_ci        auto [vertexWriter, vertexInfo] = bufferMgr->getVertexWriter(vertexSize);
254cb93a386Sopenharmony_ci        auto [indexWriter, indexInfo] = bufferMgr->getIndexWriter(indexSize);
255cb93a386Sopenharmony_ci        // TODO: handle the case where we fail to get a vertex or index writer besides asserting
256cb93a386Sopenharmony_ci        SkASSERT(!vertexSize || (vertexWriter && vertexInfo.fBuffer));
257cb93a386Sopenharmony_ci        SkASSERT(!indexSize || (indexWriter && indexInfo.fBuffer));
258cb93a386Sopenharmony_ci        draw.writeVertices(std::move(vertexWriter), std::move(indexWriter), renderStep);
259cb93a386Sopenharmony_ci
260cb93a386Sopenharmony_ci        if (vertexSize) {
261cb93a386Sopenharmony_ci            if (lastBoundVertexBuffer != vertexInfo.fBuffer) {
262cb93a386Sopenharmony_ci                // TODO: Record a vertex bind call that stores the vertexInfo.fBuffer.
263cb93a386Sopenharmony_ci            }
264cb93a386Sopenharmony_ci            // TODO: Store the vertexInfo.fOffset so the draw will know its vertex offset when it
265cb93a386Sopenharmony_ci            // executes.
266cb93a386Sopenharmony_ci        }
267cb93a386Sopenharmony_ci        if (indexSize) {
268cb93a386Sopenharmony_ci            if (lastBoundIndexBuffer != indexInfo.fBuffer) {
269cb93a386Sopenharmony_ci                // TODO: Record a vertex bind call that stores the vertexInfo.fBuffer.
270cb93a386Sopenharmony_ci            }
271cb93a386Sopenharmony_ci            // TODO: Store the vertexInfo.fOffset so the draw will know its vertex offset when it
272cb93a386Sopenharmony_ci            // executes.
273cb93a386Sopenharmony_ci        }
274cb93a386Sopenharmony_ci
275cb93a386Sopenharmony_ci        // TODO: Have the render step write out vertices and figure out what draw call function and
276cb93a386Sopenharmony_ci        // primitive type it uses. The vertex buffer binding/offset and draw params will be examined
277cb93a386Sopenharmony_ci        // to determine if the active draw can be updated to include the new vertices, or if it has
278cb93a386Sopenharmony_ci        // to be ended and a new one begun for this step. In addition to checking this state, must
279cb93a386Sopenharmony_ci        // also check if pipeline, uniform, scissor etc. would require the active draw to end.
280cb93a386Sopenharmony_ci        //
281cb93a386Sopenharmony_ci        // const RenderStep* const step = draw.fRenderer.steps()[key.renderStep()];
282cb93a386Sopenharmony_ci
283cb93a386Sopenharmony_ci        if (key.pipeline() != lastPipeline) {
284cb93a386Sopenharmony_ci            // TODO: Look up pipeline description from key's index and record binding it
285cb93a386Sopenharmony_ci            lastPipeline = key.pipeline();
286cb93a386Sopenharmony_ci            lastShadingUniforms = UniformData::kInvalidUniformID;
287cb93a386Sopenharmony_ci            lastGeometryUniforms = 0;
288cb93a386Sopenharmony_ci        }
289cb93a386Sopenharmony_ci        if (key.geometryUniforms() != lastGeometryUniforms) {
290cb93a386Sopenharmony_ci            // TODO: Look up uniform buffer binding info corresponding to key's index and record it
291cb93a386Sopenharmony_ci            lastGeometryUniforms = key.geometryUniforms();
292cb93a386Sopenharmony_ci        }
293cb93a386Sopenharmony_ci        if (key.shadingUniforms() != lastShadingUniforms) {
294cb93a386Sopenharmony_ci            auto ud = lookup(recorder, key.shadingUniforms());
295cb93a386Sopenharmony_ci
296cb93a386Sopenharmony_ci            auto [writer, bufferInfo] = bufferMgr->getUniformWriter(ud->dataSize());
297cb93a386Sopenharmony_ci            writer.write(ud->data(), ud->dataSize());
298cb93a386Sopenharmony_ci            // TODO: recording 'bufferInfo' somewhere to allow a later uniform bind call
299cb93a386Sopenharmony_ci
300cb93a386Sopenharmony_ci            lastShadingUniforms = key.shadingUniforms();
301cb93a386Sopenharmony_ci        }
302cb93a386Sopenharmony_ci
303cb93a386Sopenharmony_ci        if (draw.fClip.scissor() != lastScissor) {
304cb93a386Sopenharmony_ci            // TODO: Record new scissor rectangle
305cb93a386Sopenharmony_ci        }
306cb93a386Sopenharmony_ci
307cb93a386Sopenharmony_ci        // TODO: Write vertex and index data for the draw step
308cb93a386Sopenharmony_ci    }
309cb93a386Sopenharmony_ci
310cb93a386Sopenharmony_ci    // if (currentDraw) {
311cb93a386Sopenharmony_ci        // TODO: End the current draw if it has pending vertices
312cb93a386Sopenharmony_ci    // }
313cb93a386Sopenharmony_ci
314cb93a386Sopenharmony_ci    passBounds.roundOut();
315cb93a386Sopenharmony_ci    SkIRect pxPassBounds = SkIRect::MakeLTRB((int) passBounds.left(), (int) passBounds.top(),
316cb93a386Sopenharmony_ci                                             (int) passBounds.right(), (int) passBounds.bot());
317cb93a386Sopenharmony_ci    return std::unique_ptr<DrawPass>(new DrawPass(std::move(target), pxPassBounds, ops, clearColor,
318cb93a386Sopenharmony_ci                                                  requiresStencil, requiresMSAA));
319cb93a386Sopenharmony_ci}
320cb93a386Sopenharmony_ci
321cb93a386Sopenharmony_civoid DrawPass::addCommands(CommandBuffer* buffer) const {
322cb93a386Sopenharmony_ci    // TODO
323cb93a386Sopenharmony_ci}
324cb93a386Sopenharmony_ci
325cb93a386Sopenharmony_ci} // namespace skgpu
326