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