1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2022 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/task/UploadTask.h"
9*c8dee2aaSAndroid Build Coastguard Worker
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColorSpace.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/SkAutoPixmapStorage.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkCompressedDataUtils.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkMipmap.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkTraceEvent.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/DataUtils.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/Buffer.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/Caps.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/CommandBuffer.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/Log.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/RecorderPriv.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/ResourceProvider.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/Texture.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/TextureProxy.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/UploadBufferManager.h"
27*c8dee2aaSAndroid Build Coastguard Worker
28*c8dee2aaSAndroid Build Coastguard Worker using namespace skia_private;
29*c8dee2aaSAndroid Build Coastguard Worker
30*c8dee2aaSAndroid Build Coastguard Worker namespace skgpu::graphite {
31*c8dee2aaSAndroid Build Coastguard Worker
32*c8dee2aaSAndroid Build Coastguard Worker UploadInstance::UploadInstance() = default;
33*c8dee2aaSAndroid Build Coastguard Worker UploadInstance::UploadInstance(UploadInstance&&) = default;
34*c8dee2aaSAndroid Build Coastguard Worker UploadInstance& UploadInstance::operator=(UploadInstance&&) = default;
35*c8dee2aaSAndroid Build Coastguard Worker UploadInstance::~UploadInstance() = default;
36*c8dee2aaSAndroid Build Coastguard Worker
UploadInstance(const Buffer * buffer,size_t bytesPerPixel,sk_sp<TextureProxy> textureProxy,std::unique_ptr<ConditionalUploadContext> condContext)37*c8dee2aaSAndroid Build Coastguard Worker UploadInstance::UploadInstance(const Buffer* buffer,
38*c8dee2aaSAndroid Build Coastguard Worker size_t bytesPerPixel,
39*c8dee2aaSAndroid Build Coastguard Worker sk_sp<TextureProxy> textureProxy,
40*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<ConditionalUploadContext> condContext)
41*c8dee2aaSAndroid Build Coastguard Worker : fBuffer(buffer)
42*c8dee2aaSAndroid Build Coastguard Worker , fBytesPerPixel(bytesPerPixel)
43*c8dee2aaSAndroid Build Coastguard Worker , fTextureProxy(textureProxy)
44*c8dee2aaSAndroid Build Coastguard Worker , fConditionalContext(std::move(condContext)) {}
45*c8dee2aaSAndroid Build Coastguard Worker
46*c8dee2aaSAndroid Build Coastguard Worker // Returns total buffer size to allocate, and required offset alignment of that allocation.
47*c8dee2aaSAndroid Build Coastguard Worker // Updates 'levelOffsetsAndRowBytes' with offsets relative to start of the allocation, as well as
48*c8dee2aaSAndroid Build Coastguard Worker // the aligned destination rowBytes for each level.
compute_combined_buffer_size(const Caps * caps,int mipLevelCount,size_t bytesPerBlock,const SkISize & baseDimensions,SkTextureCompressionType compressionType,TArray<std::pair<size_t,size_t>> * levelOffsetsAndRowBytes)49*c8dee2aaSAndroid Build Coastguard Worker std::pair<size_t, size_t> compute_combined_buffer_size(
50*c8dee2aaSAndroid Build Coastguard Worker const Caps* caps,
51*c8dee2aaSAndroid Build Coastguard Worker int mipLevelCount,
52*c8dee2aaSAndroid Build Coastguard Worker size_t bytesPerBlock,
53*c8dee2aaSAndroid Build Coastguard Worker const SkISize& baseDimensions,
54*c8dee2aaSAndroid Build Coastguard Worker SkTextureCompressionType compressionType,
55*c8dee2aaSAndroid Build Coastguard Worker TArray<std::pair<size_t, size_t>>* levelOffsetsAndRowBytes) {
56*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(levelOffsetsAndRowBytes && levelOffsetsAndRowBytes->empty());
57*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(mipLevelCount >= 1);
58*c8dee2aaSAndroid Build Coastguard Worker
59*c8dee2aaSAndroid Build Coastguard Worker SkISize compressedBlockDimensions = CompressedDimensionsInBlocks(compressionType,
60*c8dee2aaSAndroid Build Coastguard Worker baseDimensions);
61*c8dee2aaSAndroid Build Coastguard Worker
62*c8dee2aaSAndroid Build Coastguard Worker size_t minTransferBufferAlignment =
63*c8dee2aaSAndroid Build Coastguard Worker std::max(bytesPerBlock, caps->requiredTransferBufferAlignment());
64*c8dee2aaSAndroid Build Coastguard Worker size_t alignedBytesPerRow =
65*c8dee2aaSAndroid Build Coastguard Worker caps->getAlignedTextureDataRowBytes(compressedBlockDimensions.width() * bytesPerBlock);
66*c8dee2aaSAndroid Build Coastguard Worker
67*c8dee2aaSAndroid Build Coastguard Worker levelOffsetsAndRowBytes->push_back({0, alignedBytesPerRow});
68*c8dee2aaSAndroid Build Coastguard Worker size_t combinedBufferSize = SkAlignTo(alignedBytesPerRow * baseDimensions.height(),
69*c8dee2aaSAndroid Build Coastguard Worker minTransferBufferAlignment);
70*c8dee2aaSAndroid Build Coastguard Worker SkISize levelDimensions = baseDimensions;
71*c8dee2aaSAndroid Build Coastguard Worker
72*c8dee2aaSAndroid Build Coastguard Worker for (int currentMipLevel = 1; currentMipLevel < mipLevelCount; ++currentMipLevel) {
73*c8dee2aaSAndroid Build Coastguard Worker levelDimensions = {std::max(1, levelDimensions.width() / 2),
74*c8dee2aaSAndroid Build Coastguard Worker std::max(1, levelDimensions.height() / 2)};
75*c8dee2aaSAndroid Build Coastguard Worker compressedBlockDimensions = CompressedDimensionsInBlocks(compressionType, levelDimensions);
76*c8dee2aaSAndroid Build Coastguard Worker alignedBytesPerRow = caps->getAlignedTextureDataRowBytes(
77*c8dee2aaSAndroid Build Coastguard Worker compressedBlockDimensions.width() * bytesPerBlock);
78*c8dee2aaSAndroid Build Coastguard Worker size_t alignedSize = SkAlignTo(alignedBytesPerRow * compressedBlockDimensions.height(),
79*c8dee2aaSAndroid Build Coastguard Worker minTransferBufferAlignment);
80*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(combinedBufferSize % minTransferBufferAlignment == 0);
81*c8dee2aaSAndroid Build Coastguard Worker
82*c8dee2aaSAndroid Build Coastguard Worker levelOffsetsAndRowBytes->push_back({combinedBufferSize, alignedBytesPerRow});
83*c8dee2aaSAndroid Build Coastguard Worker combinedBufferSize += alignedSize;
84*c8dee2aaSAndroid Build Coastguard Worker }
85*c8dee2aaSAndroid Build Coastguard Worker
86*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(levelOffsetsAndRowBytes->size() == mipLevelCount);
87*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(combinedBufferSize % minTransferBufferAlignment == 0);
88*c8dee2aaSAndroid Build Coastguard Worker return {combinedBufferSize, minTransferBufferAlignment};
89*c8dee2aaSAndroid Build Coastguard Worker }
90*c8dee2aaSAndroid Build Coastguard Worker
Make(Recorder * recorder,sk_sp<TextureProxy> textureProxy,const SkColorInfo & srcColorInfo,const SkColorInfo & dstColorInfo,SkSpan<const MipLevel> levels,const SkIRect & dstRect,std::unique_ptr<ConditionalUploadContext> condContext)91*c8dee2aaSAndroid Build Coastguard Worker UploadInstance UploadInstance::Make(Recorder* recorder,
92*c8dee2aaSAndroid Build Coastguard Worker sk_sp<TextureProxy> textureProxy,
93*c8dee2aaSAndroid Build Coastguard Worker const SkColorInfo& srcColorInfo,
94*c8dee2aaSAndroid Build Coastguard Worker const SkColorInfo& dstColorInfo,
95*c8dee2aaSAndroid Build Coastguard Worker SkSpan<const MipLevel> levels,
96*c8dee2aaSAndroid Build Coastguard Worker const SkIRect& dstRect,
97*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<ConditionalUploadContext> condContext) {
98*c8dee2aaSAndroid Build Coastguard Worker const Caps* caps = recorder->priv().caps();
99*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(caps->isTexturable(textureProxy->textureInfo()));
100*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(caps->areColorTypeAndTextureInfoCompatible(dstColorInfo.colorType(),
101*c8dee2aaSAndroid Build Coastguard Worker textureProxy->textureInfo()));
102*c8dee2aaSAndroid Build Coastguard Worker
103*c8dee2aaSAndroid Build Coastguard Worker unsigned int mipLevelCount = levels.size();
104*c8dee2aaSAndroid Build Coastguard Worker // The assumption is either that we have no mipmaps, or that our rect is the entire texture
105*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(mipLevelCount == 1 || dstRect == SkIRect::MakeSize(textureProxy->dimensions()));
106*c8dee2aaSAndroid Build Coastguard Worker
107*c8dee2aaSAndroid Build Coastguard Worker // We assume that if the texture has mip levels, we either upload to all the levels or just the
108*c8dee2aaSAndroid Build Coastguard Worker // first.
109*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_DEBUG
110*c8dee2aaSAndroid Build Coastguard Worker unsigned int numExpectedLevels = 1;
111*c8dee2aaSAndroid Build Coastguard Worker if (textureProxy->textureInfo().mipmapped() == Mipmapped::kYes) {
112*c8dee2aaSAndroid Build Coastguard Worker numExpectedLevels = SkMipmap::ComputeLevelCount(textureProxy->dimensions().width(),
113*c8dee2aaSAndroid Build Coastguard Worker textureProxy->dimensions().height()) + 1;
114*c8dee2aaSAndroid Build Coastguard Worker }
115*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(mipLevelCount == 1 || mipLevelCount == numExpectedLevels);
116*c8dee2aaSAndroid Build Coastguard Worker #endif
117*c8dee2aaSAndroid Build Coastguard Worker
118*c8dee2aaSAndroid Build Coastguard Worker if (dstRect.isEmpty()) {
119*c8dee2aaSAndroid Build Coastguard Worker return Invalid();
120*c8dee2aaSAndroid Build Coastguard Worker }
121*c8dee2aaSAndroid Build Coastguard Worker
122*c8dee2aaSAndroid Build Coastguard Worker if (mipLevelCount == 1 && !levels[0].fPixels) {
123*c8dee2aaSAndroid Build Coastguard Worker return Invalid(); // no data to upload
124*c8dee2aaSAndroid Build Coastguard Worker }
125*c8dee2aaSAndroid Build Coastguard Worker
126*c8dee2aaSAndroid Build Coastguard Worker for (unsigned int i = 0; i < mipLevelCount; ++i) {
127*c8dee2aaSAndroid Build Coastguard Worker // We do not allow any gaps in the mip data
128*c8dee2aaSAndroid Build Coastguard Worker if (!levels[i].fPixels) {
129*c8dee2aaSAndroid Build Coastguard Worker return Invalid();
130*c8dee2aaSAndroid Build Coastguard Worker }
131*c8dee2aaSAndroid Build Coastguard Worker }
132*c8dee2aaSAndroid Build Coastguard Worker
133*c8dee2aaSAndroid Build Coastguard Worker SkColorType supportedColorType;
134*c8dee2aaSAndroid Build Coastguard Worker bool isRGB888Format;
135*c8dee2aaSAndroid Build Coastguard Worker std::tie(supportedColorType, isRGB888Format) =
136*c8dee2aaSAndroid Build Coastguard Worker caps->supportedWritePixelsColorType(dstColorInfo.colorType(),
137*c8dee2aaSAndroid Build Coastguard Worker textureProxy->textureInfo(),
138*c8dee2aaSAndroid Build Coastguard Worker srcColorInfo.colorType());
139*c8dee2aaSAndroid Build Coastguard Worker if (supportedColorType == kUnknown_SkColorType) {
140*c8dee2aaSAndroid Build Coastguard Worker return Invalid();
141*c8dee2aaSAndroid Build Coastguard Worker }
142*c8dee2aaSAndroid Build Coastguard Worker
143*c8dee2aaSAndroid Build Coastguard Worker const size_t bpp = isRGB888Format ? 3 : SkColorTypeBytesPerPixel(supportedColorType);
144*c8dee2aaSAndroid Build Coastguard Worker TArray<std::pair<size_t, size_t>> levelOffsetsAndRowBytes(mipLevelCount);
145*c8dee2aaSAndroid Build Coastguard Worker
146*c8dee2aaSAndroid Build Coastguard Worker auto [combinedBufferSize, minAlignment] = compute_combined_buffer_size(
147*c8dee2aaSAndroid Build Coastguard Worker caps,
148*c8dee2aaSAndroid Build Coastguard Worker mipLevelCount,
149*c8dee2aaSAndroid Build Coastguard Worker bpp,
150*c8dee2aaSAndroid Build Coastguard Worker dstRect.size(),
151*c8dee2aaSAndroid Build Coastguard Worker SkTextureCompressionType::kNone,
152*c8dee2aaSAndroid Build Coastguard Worker &levelOffsetsAndRowBytes);
153*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(combinedBufferSize);
154*c8dee2aaSAndroid Build Coastguard Worker
155*c8dee2aaSAndroid Build Coastguard Worker UploadBufferManager* bufferMgr = recorder->priv().uploadBufferManager();
156*c8dee2aaSAndroid Build Coastguard Worker auto [writer, bufferInfo] = bufferMgr->getTextureUploadWriter(combinedBufferSize, minAlignment);
157*c8dee2aaSAndroid Build Coastguard Worker if (!writer) {
158*c8dee2aaSAndroid Build Coastguard Worker SKGPU_LOG_W("Failed to get write-mapped buffer for texture upload of size %zu",
159*c8dee2aaSAndroid Build Coastguard Worker combinedBufferSize);
160*c8dee2aaSAndroid Build Coastguard Worker return Invalid();
161*c8dee2aaSAndroid Build Coastguard Worker }
162*c8dee2aaSAndroid Build Coastguard Worker
163*c8dee2aaSAndroid Build Coastguard Worker UploadInstance upload{bufferInfo.fBuffer, bpp, std::move(textureProxy), std::move(condContext)};
164*c8dee2aaSAndroid Build Coastguard Worker
165*c8dee2aaSAndroid Build Coastguard Worker // Fill in copy data
166*c8dee2aaSAndroid Build Coastguard Worker int32_t currentWidth = dstRect.width();
167*c8dee2aaSAndroid Build Coastguard Worker int32_t currentHeight = dstRect.height();
168*c8dee2aaSAndroid Build Coastguard Worker bool needsConversion = (srcColorInfo != dstColorInfo);
169*c8dee2aaSAndroid Build Coastguard Worker for (unsigned int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) {
170*c8dee2aaSAndroid Build Coastguard Worker const size_t trimRowBytes = currentWidth * bpp;
171*c8dee2aaSAndroid Build Coastguard Worker const size_t srcRowBytes = levels[currentMipLevel].fRowBytes;
172*c8dee2aaSAndroid Build Coastguard Worker const auto [mipOffset, dstRowBytes] = levelOffsetsAndRowBytes[currentMipLevel];
173*c8dee2aaSAndroid Build Coastguard Worker
174*c8dee2aaSAndroid Build Coastguard Worker // copy data into the buffer, skipping any trailing bytes
175*c8dee2aaSAndroid Build Coastguard Worker const char* src = (const char*)levels[currentMipLevel].fPixels;
176*c8dee2aaSAndroid Build Coastguard Worker
177*c8dee2aaSAndroid Build Coastguard Worker if (isRGB888Format) {
178*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(supportedColorType == kRGB_888x_SkColorType &&
179*c8dee2aaSAndroid Build Coastguard Worker dstColorInfo.colorType() == kRGB_888x_SkColorType);
180*c8dee2aaSAndroid Build Coastguard Worker SkISize dims = {currentWidth, currentHeight};
181*c8dee2aaSAndroid Build Coastguard Worker SkImageInfo srcImageInfo = SkImageInfo::Make(dims, srcColorInfo);
182*c8dee2aaSAndroid Build Coastguard Worker SkImageInfo dstImageInfo = SkImageInfo::Make(dims, dstColorInfo);
183*c8dee2aaSAndroid Build Coastguard Worker
184*c8dee2aaSAndroid Build Coastguard Worker const void* rgbConvertSrc = src;
185*c8dee2aaSAndroid Build Coastguard Worker size_t rgbSrcRowBytes = srcRowBytes;
186*c8dee2aaSAndroid Build Coastguard Worker SkAutoPixmapStorage temp;
187*c8dee2aaSAndroid Build Coastguard Worker if (needsConversion) {
188*c8dee2aaSAndroid Build Coastguard Worker temp.alloc(dstImageInfo);
189*c8dee2aaSAndroid Build Coastguard Worker SkAssertResult(SkConvertPixels(dstImageInfo,
190*c8dee2aaSAndroid Build Coastguard Worker temp.writable_addr(),
191*c8dee2aaSAndroid Build Coastguard Worker temp.rowBytes(),
192*c8dee2aaSAndroid Build Coastguard Worker srcImageInfo,
193*c8dee2aaSAndroid Build Coastguard Worker src,
194*c8dee2aaSAndroid Build Coastguard Worker srcRowBytes));
195*c8dee2aaSAndroid Build Coastguard Worker rgbConvertSrc = temp.addr();
196*c8dee2aaSAndroid Build Coastguard Worker rgbSrcRowBytes = temp.rowBytes();
197*c8dee2aaSAndroid Build Coastguard Worker }
198*c8dee2aaSAndroid Build Coastguard Worker writer.writeRGBFromRGBx(mipOffset,
199*c8dee2aaSAndroid Build Coastguard Worker rgbConvertSrc,
200*c8dee2aaSAndroid Build Coastguard Worker rgbSrcRowBytes,
201*c8dee2aaSAndroid Build Coastguard Worker dstRowBytes,
202*c8dee2aaSAndroid Build Coastguard Worker currentWidth,
203*c8dee2aaSAndroid Build Coastguard Worker currentHeight);
204*c8dee2aaSAndroid Build Coastguard Worker } else if (needsConversion) {
205*c8dee2aaSAndroid Build Coastguard Worker SkISize dims = {currentWidth, currentHeight};
206*c8dee2aaSAndroid Build Coastguard Worker SkImageInfo srcImageInfo = SkImageInfo::Make(dims, srcColorInfo);
207*c8dee2aaSAndroid Build Coastguard Worker SkImageInfo dstImageInfo = SkImageInfo::Make(dims, dstColorInfo);
208*c8dee2aaSAndroid Build Coastguard Worker
209*c8dee2aaSAndroid Build Coastguard Worker writer.convertAndWrite(
210*c8dee2aaSAndroid Build Coastguard Worker mipOffset, srcImageInfo, src, srcRowBytes, dstImageInfo, dstRowBytes);
211*c8dee2aaSAndroid Build Coastguard Worker } else {
212*c8dee2aaSAndroid Build Coastguard Worker writer.write(mipOffset, src, srcRowBytes, dstRowBytes, trimRowBytes, currentHeight);
213*c8dee2aaSAndroid Build Coastguard Worker }
214*c8dee2aaSAndroid Build Coastguard Worker
215*c8dee2aaSAndroid Build Coastguard Worker // For mipped data, the dstRect is always the full texture so we don't need to worry about
216*c8dee2aaSAndroid Build Coastguard Worker // modifying the TL coord as it will always be 0,0,for all levels.
217*c8dee2aaSAndroid Build Coastguard Worker upload.fCopyData.push_back({
218*c8dee2aaSAndroid Build Coastguard Worker /*fBufferOffset=*/bufferInfo.fOffset + mipOffset,
219*c8dee2aaSAndroid Build Coastguard Worker /*fBufferRowBytes=*/dstRowBytes,
220*c8dee2aaSAndroid Build Coastguard Worker /*fRect=*/SkIRect::MakeXYWH(dstRect.left(), dstRect.top(), currentWidth, currentHeight),
221*c8dee2aaSAndroid Build Coastguard Worker /*fMipmapLevel=*/currentMipLevel
222*c8dee2aaSAndroid Build Coastguard Worker });
223*c8dee2aaSAndroid Build Coastguard Worker
224*c8dee2aaSAndroid Build Coastguard Worker currentWidth = std::max(1, currentWidth / 2);
225*c8dee2aaSAndroid Build Coastguard Worker currentHeight = std::max(1, currentHeight / 2);
226*c8dee2aaSAndroid Build Coastguard Worker }
227*c8dee2aaSAndroid Build Coastguard Worker
228*c8dee2aaSAndroid Build Coastguard Worker ATRACE_ANDROID_FRAMEWORK("Upload %sTexture [%dx%d]",
229*c8dee2aaSAndroid Build Coastguard Worker mipLevelCount > 1 ? "MipMap " : "",
230*c8dee2aaSAndroid Build Coastguard Worker dstRect.width(), dstRect.height());
231*c8dee2aaSAndroid Build Coastguard Worker
232*c8dee2aaSAndroid Build Coastguard Worker return upload;
233*c8dee2aaSAndroid Build Coastguard Worker }
234*c8dee2aaSAndroid Build Coastguard Worker
MakeCompressed(Recorder * recorder,sk_sp<TextureProxy> textureProxy,const void * data,size_t dataSize)235*c8dee2aaSAndroid Build Coastguard Worker UploadInstance UploadInstance::MakeCompressed(Recorder* recorder,
236*c8dee2aaSAndroid Build Coastguard Worker sk_sp<TextureProxy> textureProxy,
237*c8dee2aaSAndroid Build Coastguard Worker const void* data,
238*c8dee2aaSAndroid Build Coastguard Worker size_t dataSize) {
239*c8dee2aaSAndroid Build Coastguard Worker if (!data) {
240*c8dee2aaSAndroid Build Coastguard Worker return Invalid(); // no data to upload
241*c8dee2aaSAndroid Build Coastguard Worker }
242*c8dee2aaSAndroid Build Coastguard Worker
243*c8dee2aaSAndroid Build Coastguard Worker const TextureInfo& texInfo = textureProxy->textureInfo();
244*c8dee2aaSAndroid Build Coastguard Worker
245*c8dee2aaSAndroid Build Coastguard Worker const Caps* caps = recorder->priv().caps();
246*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(caps->isTexturable(texInfo));
247*c8dee2aaSAndroid Build Coastguard Worker
248*c8dee2aaSAndroid Build Coastguard Worker SkTextureCompressionType compression = texInfo.compressionType();
249*c8dee2aaSAndroid Build Coastguard Worker if (compression == SkTextureCompressionType::kNone) {
250*c8dee2aaSAndroid Build Coastguard Worker return Invalid();
251*c8dee2aaSAndroid Build Coastguard Worker }
252*c8dee2aaSAndroid Build Coastguard Worker
253*c8dee2aaSAndroid Build Coastguard Worker // Create a transfer buffer and fill with data.
254*c8dee2aaSAndroid Build Coastguard Worker const SkISize dimensions = textureProxy->dimensions();
255*c8dee2aaSAndroid Build Coastguard Worker skia_private::STArray<16, size_t> srcMipOffsets;
256*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGCODE(size_t computedSize =) SkCompressedDataSize(compression,
257*c8dee2aaSAndroid Build Coastguard Worker dimensions,
258*c8dee2aaSAndroid Build Coastguard Worker &srcMipOffsets,
259*c8dee2aaSAndroid Build Coastguard Worker texInfo.mipmapped() == Mipmapped::kYes);
260*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(computedSize == dataSize);
261*c8dee2aaSAndroid Build Coastguard Worker
262*c8dee2aaSAndroid Build Coastguard Worker unsigned int mipLevelCount = srcMipOffsets.size();
263*c8dee2aaSAndroid Build Coastguard Worker size_t bytesPerBlock = SkCompressedBlockSize(compression);
264*c8dee2aaSAndroid Build Coastguard Worker TArray<std::pair<size_t, size_t>> levelOffsetsAndRowBytes(mipLevelCount);
265*c8dee2aaSAndroid Build Coastguard Worker auto [combinedBufferSize, minAlignment] = compute_combined_buffer_size(
266*c8dee2aaSAndroid Build Coastguard Worker caps,
267*c8dee2aaSAndroid Build Coastguard Worker mipLevelCount,
268*c8dee2aaSAndroid Build Coastguard Worker bytesPerBlock,
269*c8dee2aaSAndroid Build Coastguard Worker dimensions,
270*c8dee2aaSAndroid Build Coastguard Worker compression,
271*c8dee2aaSAndroid Build Coastguard Worker &levelOffsetsAndRowBytes);
272*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(combinedBufferSize);
273*c8dee2aaSAndroid Build Coastguard Worker
274*c8dee2aaSAndroid Build Coastguard Worker UploadBufferManager* bufferMgr = recorder->priv().uploadBufferManager();
275*c8dee2aaSAndroid Build Coastguard Worker auto [writer, bufferInfo] = bufferMgr->getTextureUploadWriter(combinedBufferSize, minAlignment);
276*c8dee2aaSAndroid Build Coastguard Worker
277*c8dee2aaSAndroid Build Coastguard Worker std::vector<BufferTextureCopyData> copyData(mipLevelCount);
278*c8dee2aaSAndroid Build Coastguard Worker
279*c8dee2aaSAndroid Build Coastguard Worker if (!bufferInfo.fBuffer) {
280*c8dee2aaSAndroid Build Coastguard Worker SKGPU_LOG_W("Failed to get write-mapped buffer for texture upload of size %zu",
281*c8dee2aaSAndroid Build Coastguard Worker combinedBufferSize);
282*c8dee2aaSAndroid Build Coastguard Worker return Invalid();
283*c8dee2aaSAndroid Build Coastguard Worker }
284*c8dee2aaSAndroid Build Coastguard Worker
285*c8dee2aaSAndroid Build Coastguard Worker UploadInstance upload{bufferInfo.fBuffer, bytesPerBlock, std::move(textureProxy)};
286*c8dee2aaSAndroid Build Coastguard Worker
287*c8dee2aaSAndroid Build Coastguard Worker // Fill in copy data
288*c8dee2aaSAndroid Build Coastguard Worker int32_t currentWidth = dimensions.width();
289*c8dee2aaSAndroid Build Coastguard Worker int32_t currentHeight = dimensions.height();
290*c8dee2aaSAndroid Build Coastguard Worker for (unsigned int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) {
291*c8dee2aaSAndroid Build Coastguard Worker SkISize blockDimensions = CompressedDimensionsInBlocks(compression,
292*c8dee2aaSAndroid Build Coastguard Worker {currentWidth, currentHeight});
293*c8dee2aaSAndroid Build Coastguard Worker int32_t blockHeight = blockDimensions.height();
294*c8dee2aaSAndroid Build Coastguard Worker
295*c8dee2aaSAndroid Build Coastguard Worker const size_t trimRowBytes = CompressedRowBytes(compression, currentWidth);
296*c8dee2aaSAndroid Build Coastguard Worker const size_t srcRowBytes = trimRowBytes;
297*c8dee2aaSAndroid Build Coastguard Worker const auto [dstMipOffset, dstRowBytes] = levelOffsetsAndRowBytes[currentMipLevel];
298*c8dee2aaSAndroid Build Coastguard Worker
299*c8dee2aaSAndroid Build Coastguard Worker // copy data into the buffer, skipping any trailing bytes
300*c8dee2aaSAndroid Build Coastguard Worker const void* src = SkTAddOffset<const void>(data, srcMipOffsets[currentMipLevel]);
301*c8dee2aaSAndroid Build Coastguard Worker
302*c8dee2aaSAndroid Build Coastguard Worker writer.write(dstMipOffset, src, srcRowBytes, dstRowBytes, trimRowBytes, blockHeight);
303*c8dee2aaSAndroid Build Coastguard Worker
304*c8dee2aaSAndroid Build Coastguard Worker int32_t copyWidth = currentWidth;
305*c8dee2aaSAndroid Build Coastguard Worker int32_t copyHeight = currentHeight;
306*c8dee2aaSAndroid Build Coastguard Worker if (caps->fullCompressedUploadSizeMustAlignToBlockDims()) {
307*c8dee2aaSAndroid Build Coastguard Worker SkISize oneBlockDims = CompressedDimensions(compression, {1, 1});
308*c8dee2aaSAndroid Build Coastguard Worker copyWidth = SkAlignTo(copyWidth, oneBlockDims.fWidth);
309*c8dee2aaSAndroid Build Coastguard Worker copyHeight = SkAlignTo(copyHeight, oneBlockDims.fHeight);
310*c8dee2aaSAndroid Build Coastguard Worker }
311*c8dee2aaSAndroid Build Coastguard Worker
312*c8dee2aaSAndroid Build Coastguard Worker upload.fCopyData.push_back({
313*c8dee2aaSAndroid Build Coastguard Worker /*fBufferOffset=*/bufferInfo.fOffset + dstMipOffset,
314*c8dee2aaSAndroid Build Coastguard Worker /*fBufferRowBytes=*/dstRowBytes,
315*c8dee2aaSAndroid Build Coastguard Worker /*fRect=*/SkIRect::MakeXYWH(0, 0, copyWidth, copyHeight),
316*c8dee2aaSAndroid Build Coastguard Worker /*fMipLevel=*/currentMipLevel
317*c8dee2aaSAndroid Build Coastguard Worker });
318*c8dee2aaSAndroid Build Coastguard Worker
319*c8dee2aaSAndroid Build Coastguard Worker currentWidth = std::max(1, currentWidth / 2);
320*c8dee2aaSAndroid Build Coastguard Worker currentHeight = std::max(1, currentHeight / 2);
321*c8dee2aaSAndroid Build Coastguard Worker }
322*c8dee2aaSAndroid Build Coastguard Worker
323*c8dee2aaSAndroid Build Coastguard Worker ATRACE_ANDROID_FRAMEWORK("Upload Compressed %sTexture [%dx%d]",
324*c8dee2aaSAndroid Build Coastguard Worker mipLevelCount > 1 ? "MipMap " : "",
325*c8dee2aaSAndroid Build Coastguard Worker dimensions.width(),
326*c8dee2aaSAndroid Build Coastguard Worker dimensions.height());
327*c8dee2aaSAndroid Build Coastguard Worker
328*c8dee2aaSAndroid Build Coastguard Worker return upload;
329*c8dee2aaSAndroid Build Coastguard Worker }
330*c8dee2aaSAndroid Build Coastguard Worker
prepareResources(ResourceProvider * resourceProvider)331*c8dee2aaSAndroid Build Coastguard Worker bool UploadInstance::prepareResources(ResourceProvider* resourceProvider) {
332*c8dee2aaSAndroid Build Coastguard Worker // While most uploads are to already instantiated proxies (e.g. for client-created texture
333*c8dee2aaSAndroid Build Coastguard Worker // images) it is possible that writePixels() was issued as the first operation on a scratch
334*c8dee2aaSAndroid Build Coastguard Worker // Device, or that this is the first upload to the raster or text atlas proxies.
335*c8dee2aaSAndroid Build Coastguard Worker // TODO: Determine how to instantatiate textues in this case; atlas proxies shouldn't really be
336*c8dee2aaSAndroid Build Coastguard Worker // "scratch" because they aren't going to be reused for anything else in a Recording. At the
337*c8dee2aaSAndroid Build Coastguard Worker // same time, it could still go through the ScratchResourceManager and just never return them,
338*c8dee2aaSAndroid Build Coastguard Worker // which is no different from instantiating them directly with the ResourceProvider.
339*c8dee2aaSAndroid Build Coastguard Worker if (!TextureProxy::InstantiateIfNotLazy(resourceProvider, fTextureProxy.get())) {
340*c8dee2aaSAndroid Build Coastguard Worker SKGPU_LOG_E("Could not instantiate texture proxy for UploadTask!");
341*c8dee2aaSAndroid Build Coastguard Worker return false;
342*c8dee2aaSAndroid Build Coastguard Worker }
343*c8dee2aaSAndroid Build Coastguard Worker return true;
344*c8dee2aaSAndroid Build Coastguard Worker }
345*c8dee2aaSAndroid Build Coastguard Worker
addCommand(Context * context,CommandBuffer * commandBuffer,Task::ReplayTargetData replayData) const346*c8dee2aaSAndroid Build Coastguard Worker Task::Status UploadInstance::addCommand(Context* context,
347*c8dee2aaSAndroid Build Coastguard Worker CommandBuffer* commandBuffer,
348*c8dee2aaSAndroid Build Coastguard Worker Task::ReplayTargetData replayData) const {
349*c8dee2aaSAndroid Build Coastguard Worker using Status = Task::Status;
350*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fTextureProxy && fTextureProxy->isInstantiated());
351*c8dee2aaSAndroid Build Coastguard Worker
352*c8dee2aaSAndroid Build Coastguard Worker if (fConditionalContext && !fConditionalContext->needsUpload(context)) {
353*c8dee2aaSAndroid Build Coastguard Worker // Assume that if a conditional context says to dynamically not upload that another
354*c8dee2aaSAndroid Build Coastguard Worker // time through the tasks should try to upload again.
355*c8dee2aaSAndroid Build Coastguard Worker return Status::kSuccess;
356*c8dee2aaSAndroid Build Coastguard Worker }
357*c8dee2aaSAndroid Build Coastguard Worker
358*c8dee2aaSAndroid Build Coastguard Worker if (fTextureProxy->texture() != replayData.fTarget) {
359*c8dee2aaSAndroid Build Coastguard Worker // The CommandBuffer doesn't take ownership of the upload buffer here; it's owned by
360*c8dee2aaSAndroid Build Coastguard Worker // UploadBufferManager, which will transfer ownership in transferToCommandBuffer.
361*c8dee2aaSAndroid Build Coastguard Worker if (!commandBuffer->copyBufferToTexture(fBuffer,
362*c8dee2aaSAndroid Build Coastguard Worker fTextureProxy->refTexture(),
363*c8dee2aaSAndroid Build Coastguard Worker fCopyData.data(),
364*c8dee2aaSAndroid Build Coastguard Worker fCopyData.size())) {
365*c8dee2aaSAndroid Build Coastguard Worker return Status::kFail;
366*c8dee2aaSAndroid Build Coastguard Worker }
367*c8dee2aaSAndroid Build Coastguard Worker } else {
368*c8dee2aaSAndroid Build Coastguard Worker // Here we assume that multiple copies in a single UploadInstance are always used for
369*c8dee2aaSAndroid Build Coastguard Worker // mipmaps of a single image, and that we won't ever upload to a replay target's mipmaps
370*c8dee2aaSAndroid Build Coastguard Worker // directly.
371*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fCopyData.size() == 1);
372*c8dee2aaSAndroid Build Coastguard Worker const BufferTextureCopyData& copyData = fCopyData[0];
373*c8dee2aaSAndroid Build Coastguard Worker SkIRect dstRect = copyData.fRect;
374*c8dee2aaSAndroid Build Coastguard Worker dstRect.offset(replayData.fTranslation);
375*c8dee2aaSAndroid Build Coastguard Worker SkIRect croppedDstRect = dstRect;
376*c8dee2aaSAndroid Build Coastguard Worker
377*c8dee2aaSAndroid Build Coastguard Worker if (!replayData.fClip.isEmpty()) {
378*c8dee2aaSAndroid Build Coastguard Worker SkIRect dstClip = replayData.fClip;
379*c8dee2aaSAndroid Build Coastguard Worker dstClip.offset(replayData.fTranslation);
380*c8dee2aaSAndroid Build Coastguard Worker if (!croppedDstRect.intersect(dstClip)) {
381*c8dee2aaSAndroid Build Coastguard Worker // The replay clip can change on each insert, so subsequent replays may actually
382*c8dee2aaSAndroid Build Coastguard Worker // intersect the copy rect.
383*c8dee2aaSAndroid Build Coastguard Worker return Status::kSuccess;
384*c8dee2aaSAndroid Build Coastguard Worker }
385*c8dee2aaSAndroid Build Coastguard Worker }
386*c8dee2aaSAndroid Build Coastguard Worker
387*c8dee2aaSAndroid Build Coastguard Worker if (!croppedDstRect.intersect(SkIRect::MakeSize(fTextureProxy->dimensions()))) {
388*c8dee2aaSAndroid Build Coastguard Worker // The replay translation can change on each insert, so subsequent replays may
389*c8dee2aaSAndroid Build Coastguard Worker // actually intersect the copy rect.
390*c8dee2aaSAndroid Build Coastguard Worker return Status::kSuccess;
391*c8dee2aaSAndroid Build Coastguard Worker }
392*c8dee2aaSAndroid Build Coastguard Worker
393*c8dee2aaSAndroid Build Coastguard Worker BufferTextureCopyData transformedCopyData = copyData;
394*c8dee2aaSAndroid Build Coastguard Worker transformedCopyData.fBufferOffset +=
395*c8dee2aaSAndroid Build Coastguard Worker (croppedDstRect.y() - dstRect.y()) * copyData.fBufferRowBytes +
396*c8dee2aaSAndroid Build Coastguard Worker (croppedDstRect.x() - dstRect.x()) * fBytesPerPixel;
397*c8dee2aaSAndroid Build Coastguard Worker transformedCopyData.fRect = croppedDstRect;
398*c8dee2aaSAndroid Build Coastguard Worker
399*c8dee2aaSAndroid Build Coastguard Worker if (!commandBuffer->copyBufferToTexture(fBuffer,
400*c8dee2aaSAndroid Build Coastguard Worker fTextureProxy->refTexture(),
401*c8dee2aaSAndroid Build Coastguard Worker &transformedCopyData, 1)) {
402*c8dee2aaSAndroid Build Coastguard Worker return Status::kFail;
403*c8dee2aaSAndroid Build Coastguard Worker }
404*c8dee2aaSAndroid Build Coastguard Worker }
405*c8dee2aaSAndroid Build Coastguard Worker
406*c8dee2aaSAndroid Build Coastguard Worker // The conditional context will return false if the upload should not happen anymore. If there's
407*c8dee2aaSAndroid Build Coastguard Worker // no context assume that the upload should always be executed on replay.
408*c8dee2aaSAndroid Build Coastguard Worker if (!fConditionalContext || fConditionalContext->uploadSubmitted()) {
409*c8dee2aaSAndroid Build Coastguard Worker return Status::kSuccess;
410*c8dee2aaSAndroid Build Coastguard Worker } else {
411*c8dee2aaSAndroid Build Coastguard Worker return Status::kDiscard;
412*c8dee2aaSAndroid Build Coastguard Worker }
413*c8dee2aaSAndroid Build Coastguard Worker }
414*c8dee2aaSAndroid Build Coastguard Worker
415*c8dee2aaSAndroid Build Coastguard Worker //---------------------------------------------------------------------------
416*c8dee2aaSAndroid Build Coastguard Worker
recordUpload(Recorder * recorder,sk_sp<TextureProxy> textureProxy,const SkColorInfo & srcColorInfo,const SkColorInfo & dstColorInfo,SkSpan<const MipLevel> levels,const SkIRect & dstRect,std::unique_ptr<ConditionalUploadContext> condContext)417*c8dee2aaSAndroid Build Coastguard Worker bool UploadList::recordUpload(Recorder* recorder,
418*c8dee2aaSAndroid Build Coastguard Worker sk_sp<TextureProxy> textureProxy,
419*c8dee2aaSAndroid Build Coastguard Worker const SkColorInfo& srcColorInfo,
420*c8dee2aaSAndroid Build Coastguard Worker const SkColorInfo& dstColorInfo,
421*c8dee2aaSAndroid Build Coastguard Worker SkSpan<const MipLevel> levels,
422*c8dee2aaSAndroid Build Coastguard Worker const SkIRect& dstRect,
423*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<ConditionalUploadContext> condContext) {
424*c8dee2aaSAndroid Build Coastguard Worker UploadInstance instance = UploadInstance::Make(recorder, std::move(textureProxy),
425*c8dee2aaSAndroid Build Coastguard Worker srcColorInfo, dstColorInfo,
426*c8dee2aaSAndroid Build Coastguard Worker levels, dstRect, std::move(condContext));
427*c8dee2aaSAndroid Build Coastguard Worker if (!instance.isValid()) {
428*c8dee2aaSAndroid Build Coastguard Worker return false;
429*c8dee2aaSAndroid Build Coastguard Worker }
430*c8dee2aaSAndroid Build Coastguard Worker
431*c8dee2aaSAndroid Build Coastguard Worker fInstances.emplace_back(std::move(instance));
432*c8dee2aaSAndroid Build Coastguard Worker return true;
433*c8dee2aaSAndroid Build Coastguard Worker }
434*c8dee2aaSAndroid Build Coastguard Worker
435*c8dee2aaSAndroid Build Coastguard Worker //---------------------------------------------------------------------------
436*c8dee2aaSAndroid Build Coastguard Worker
Make(UploadList * uploadList)437*c8dee2aaSAndroid Build Coastguard Worker sk_sp<UploadTask> UploadTask::Make(UploadList* uploadList) {
438*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(uploadList);
439*c8dee2aaSAndroid Build Coastguard Worker if (!uploadList->size()) {
440*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
441*c8dee2aaSAndroid Build Coastguard Worker }
442*c8dee2aaSAndroid Build Coastguard Worker return sk_sp<UploadTask>(new UploadTask(std::move(uploadList->fInstances)));
443*c8dee2aaSAndroid Build Coastguard Worker }
444*c8dee2aaSAndroid Build Coastguard Worker
Make(UploadInstance instance)445*c8dee2aaSAndroid Build Coastguard Worker sk_sp<UploadTask> UploadTask::Make(UploadInstance instance) {
446*c8dee2aaSAndroid Build Coastguard Worker if (!instance.isValid()) {
447*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
448*c8dee2aaSAndroid Build Coastguard Worker }
449*c8dee2aaSAndroid Build Coastguard Worker return sk_sp<UploadTask>(new UploadTask(std::move(instance)));
450*c8dee2aaSAndroid Build Coastguard Worker }
451*c8dee2aaSAndroid Build Coastguard Worker
UploadTask(skia_private::TArray<UploadInstance> && instances)452*c8dee2aaSAndroid Build Coastguard Worker UploadTask::UploadTask(skia_private::TArray<UploadInstance>&& instances)
453*c8dee2aaSAndroid Build Coastguard Worker : fInstances(std::move(instances)) {}
454*c8dee2aaSAndroid Build Coastguard Worker
UploadTask(UploadInstance instance)455*c8dee2aaSAndroid Build Coastguard Worker UploadTask::UploadTask(UploadInstance instance) {
456*c8dee2aaSAndroid Build Coastguard Worker fInstances.emplace_back(std::move(instance));
457*c8dee2aaSAndroid Build Coastguard Worker }
458*c8dee2aaSAndroid Build Coastguard Worker
~UploadTask()459*c8dee2aaSAndroid Build Coastguard Worker UploadTask::~UploadTask() {}
460*c8dee2aaSAndroid Build Coastguard Worker
prepareResources(ResourceProvider * resourceProvider,ScratchResourceManager *,const RuntimeEffectDictionary *)461*c8dee2aaSAndroid Build Coastguard Worker Task::Status UploadTask::prepareResources(ResourceProvider* resourceProvider,
462*c8dee2aaSAndroid Build Coastguard Worker ScratchResourceManager*,
463*c8dee2aaSAndroid Build Coastguard Worker const RuntimeEffectDictionary*) {
464*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < fInstances.size(); ++i) {
465*c8dee2aaSAndroid Build Coastguard Worker // No upload should be invalidated before prepareResources() is called.
466*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fInstances[i].isValid());
467*c8dee2aaSAndroid Build Coastguard Worker if (!fInstances[i].prepareResources(resourceProvider)) {
468*c8dee2aaSAndroid Build Coastguard Worker return Status::kFail;
469*c8dee2aaSAndroid Build Coastguard Worker }
470*c8dee2aaSAndroid Build Coastguard Worker }
471*c8dee2aaSAndroid Build Coastguard Worker
472*c8dee2aaSAndroid Build Coastguard Worker return Status::kSuccess;
473*c8dee2aaSAndroid Build Coastguard Worker }
474*c8dee2aaSAndroid Build Coastguard Worker
addCommands(Context * context,CommandBuffer * commandBuffer,ReplayTargetData replayData)475*c8dee2aaSAndroid Build Coastguard Worker Task::Status UploadTask::addCommands(Context* context,
476*c8dee2aaSAndroid Build Coastguard Worker CommandBuffer* commandBuffer,
477*c8dee2aaSAndroid Build Coastguard Worker ReplayTargetData replayData) {
478*c8dee2aaSAndroid Build Coastguard Worker int discardCount = 0;
479*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < fInstances.size(); ++i) {
480*c8dee2aaSAndroid Build Coastguard Worker if (!fInstances[i].isValid()) {
481*c8dee2aaSAndroid Build Coastguard Worker discardCount++;
482*c8dee2aaSAndroid Build Coastguard Worker continue;
483*c8dee2aaSAndroid Build Coastguard Worker }
484*c8dee2aaSAndroid Build Coastguard Worker Status status = fInstances[i].addCommand(context, commandBuffer, replayData);
485*c8dee2aaSAndroid Build Coastguard Worker if (status == Status::kFail) {
486*c8dee2aaSAndroid Build Coastguard Worker return Status::kFail;
487*c8dee2aaSAndroid Build Coastguard Worker } else if (status == Status::kDiscard) {
488*c8dee2aaSAndroid Build Coastguard Worker fInstances[i] = UploadInstance::Invalid();
489*c8dee2aaSAndroid Build Coastguard Worker discardCount++;
490*c8dee2aaSAndroid Build Coastguard Worker }
491*c8dee2aaSAndroid Build Coastguard Worker }
492*c8dee2aaSAndroid Build Coastguard Worker
493*c8dee2aaSAndroid Build Coastguard Worker if (discardCount == fInstances.size()) {
494*c8dee2aaSAndroid Build Coastguard Worker return Status::kDiscard;
495*c8dee2aaSAndroid Build Coastguard Worker } else {
496*c8dee2aaSAndroid Build Coastguard Worker return Status::kSuccess;
497*c8dee2aaSAndroid Build Coastguard Worker }
498*c8dee2aaSAndroid Build Coastguard Worker }
499*c8dee2aaSAndroid Build Coastguard Worker
500*c8dee2aaSAndroid Build Coastguard Worker } // namespace skgpu::graphite
501