2 * Copyright 2022 Google LLC
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "src/gpu/graphite/UploadTask.h"
10 #include "include/gpu/graphite/Recorder.h"
11 #include "src/core/SkTraceEvent.h"
12 #include "src/gpu/graphite/Buffer.h"
13 #include "src/gpu/graphite/Caps.h"
14 #include "src/gpu/graphite/CommandBuffer.h"
15 #include "src/gpu/graphite/Log.h"
16 #include "src/gpu/graphite/RecorderPriv.h"
17 #include "src/gpu/graphite/ResourceProvider.h"
18 #include "src/gpu/graphite/Texture.h"
19 #include "src/gpu/graphite/TextureProxy.h"
20 #include "src/gpu/graphite/UploadBufferManager.h"
22 namespace skgpu::graphite {
24 UploadInstance::UploadInstance(const Buffer* buffer,
25 sk_sp<TextureProxy> textureProxy,
26 std::vector<BufferTextureCopyData> copyData)
27 : fBuffer(buffer), fTextureProxy(textureProxy), fCopyData(copyData) {}
29 size_t compute_combined_buffer_size(int mipLevelCount,
31 size_t minTransferBufferAlignment,
32 const SkISize& baseDimensions,
33 SkTArray<size_t>* individualMipOffsets) {
34 SkASSERT(individualMipOffsets && !individualMipOffsets->count());
35 SkASSERT(mipLevelCount >= 1);
37 individualMipOffsets->push_back(0);
39 size_t combinedBufferSize = baseDimensions.width() * bytesPerPixel * baseDimensions.height();
40 SkISize levelDimensions = baseDimensions;
42 for (int currentMipLevel = 1; currentMipLevel < mipLevelCount; ++currentMipLevel) {
43 levelDimensions = {std::max(1, levelDimensions.width() /2),
44 std::max(1, levelDimensions.height()/2)};
46 size_t trimmedSize = levelDimensions.area() * bytesPerPixel;
47 combinedBufferSize = SkAlignTo(combinedBufferSize, minTransferBufferAlignment);
48 SkASSERT((0 == combinedBufferSize % 4) && (0 == combinedBufferSize % bytesPerPixel));
50 individualMipOffsets->push_back(combinedBufferSize);
51 combinedBufferSize += trimmedSize;
54 SkASSERT(individualMipOffsets->count() == mipLevelCount);
55 return combinedBufferSize;
58 UploadInstance UploadInstance::Make(Recorder* recorder,
59 sk_sp<TextureProxy> textureProxy,
60 SkColorType dataColorType,
61 const std::vector<MipLevel>& levels,
62 const SkIRect& dstRect) {
63 const Caps* caps = recorder->priv().caps();
64 SkASSERT(caps->isTexturable(textureProxy->textureInfo()));
66 unsigned int mipLevelCount = levels.size();
67 // The assumption is either that we have no mipmaps, or that our rect is the entire texture
68 SkASSERT(mipLevelCount == 1 || dstRect == SkIRect::MakeSize(textureProxy->dimensions()));
70 // We assume that if the texture has mip levels, we either upload to all the levels or just the
72 SkASSERT(mipLevelCount == 1 || mipLevelCount == textureProxy->textureInfo().numMipLevels());
74 if (dstRect.isEmpty()) {
78 SkASSERT(caps->areColorTypeAndTextureInfoCompatible(dataColorType,
79 textureProxy->textureInfo()));
81 if (mipLevelCount == 1 && !levels[0].fPixels) {
82 return {}; // no data to upload
85 for (unsigned int i = 0; i < mipLevelCount; ++i) {
86 // We do not allow any gaps in the mip data
87 if (!levels[i].fPixels) {
92 size_t bpp = SkColorTypeBytesPerPixel(dataColorType);
93 size_t minAlignment = caps->getTransferBufferAlignment(bpp);
94 SkTArray<size_t> individualMipOffsets(mipLevelCount);
95 size_t combinedBufferSize = compute_combined_buffer_size(mipLevelCount, bpp, minAlignment,
96 dstRect.size(), &individualMipOffsets);
97 SkASSERT(combinedBufferSize);
99 UploadBufferManager* bufferMgr = recorder->priv().uploadBufferManager();
100 auto [writer, bufferInfo] = bufferMgr->getUploadWriter(combinedBufferSize, minAlignment);
102 std::vector<BufferTextureCopyData> copyData(mipLevelCount);
104 if (!bufferInfo.fBuffer) {
107 size_t baseOffset = bufferInfo.fOffset;
109 int currentWidth = dstRect.width();
110 int currentHeight = dstRect.height();
111 for (unsigned int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) {
112 const size_t trimRowBytes = currentWidth * bpp;
113 const size_t rowBytes = levels[currentMipLevel].fRowBytes;
114 const size_t mipOffset = individualMipOffsets[currentMipLevel];
116 // copy data into the buffer, skipping any trailing bytes
117 const char* src = (const char*)levels[currentMipLevel].fPixels;
118 writer.write(mipOffset, src, rowBytes, trimRowBytes, currentHeight);
120 copyData[currentMipLevel].fBufferOffset = baseOffset + mipOffset;
121 copyData[currentMipLevel].fBufferRowBytes = trimRowBytes;
122 copyData[currentMipLevel].fRect = {
123 dstRect.left(), dstRect.top(), // TODO: can we recompute this for mips?
124 dstRect.left() + currentWidth, dstRect.top() + currentHeight
126 copyData[currentMipLevel].fMipLevel = currentMipLevel;
128 currentWidth = std::max(1, currentWidth/2);
129 currentHeight = std::max(1, currentHeight/2);
132 ATRACE_ANDROID_FRAMEWORK("Upload %sTexture [%ux%u]",
133 mipLevelCount > 1 ? "MipMap " : "",
134 dstRect.width(), dstRect.height());
136 return {bufferInfo.fBuffer, std::move(textureProxy), std::move(copyData)};
139 void UploadInstance::addCommand(ResourceProvider* resourceProvider,
140 CommandBuffer* commandBuffer) const {
141 if (!fTextureProxy) {
142 SKGPU_LOG_E("No texture proxy specified for UploadTask");
145 if (!fTextureProxy->instantiate(resourceProvider)) {
146 SKGPU_LOG_E("Could not instantiate texture proxy for UploadTask!");
150 // The CommandBuffer doesn't take ownership of the upload buffer here; it's owned by
151 // UploadBufferManager, which will transfer ownership in transferToCommandBuffer.
152 commandBuffer->copyBufferToTexture(
153 fBuffer, fTextureProxy->refTexture(), fCopyData.data(), fCopyData.size());
156 //---------------------------------------------------------------------------
158 bool UploadList::recordUpload(Recorder* recorder,
159 sk_sp<TextureProxy> textureProxy,
160 SkColorType dataColorType,
161 const std::vector<MipLevel>& levels,
162 const SkIRect& dstRect) {
163 UploadInstance instance = UploadInstance::Make(recorder, std::move(textureProxy), dataColorType,
165 if (!instance.isValid()) {
169 fInstances.push_back(instance);
173 //---------------------------------------------------------------------------
175 sk_sp<UploadTask> UploadTask::Make(UploadList* uploadList) {
176 SkASSERT(uploadList && uploadList->fInstances.size() > 0);
177 return sk_sp<UploadTask>(new UploadTask(std::move(uploadList->fInstances)));
180 sk_sp<UploadTask> UploadTask::Make(const UploadInstance& instance) {
181 if (!instance.isValid()) {
184 return sk_sp<UploadTask>(new UploadTask(instance));
187 UploadTask::UploadTask(std::vector<UploadInstance> instances) : fInstances(std::move(instances)) {}
189 UploadTask::UploadTask(const UploadInstance& instance) {
190 fInstances.push_back(instance);
193 UploadTask::~UploadTask() {}
195 bool UploadTask::addCommands(ResourceProvider* resourceProvider,
196 CommandBuffer* commandBuffer) {
197 for (unsigned int i = 0; i < fInstances.size(); ++i) {
198 fInstances[i].addCommand(resourceProvider, commandBuffer);
204 } // namespace skgpu::graphite