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