| /* |
| * Copyright 2022 Google LLC |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "experimental/graphite/src/UploadTask.h" |
| |
| #include "experimental/graphite/include/Recorder.h" |
| #include "experimental/graphite/src/Buffer.h" |
| #include "experimental/graphite/src/Caps.h" |
| #include "experimental/graphite/src/CommandBuffer.h" |
| #include "experimental/graphite/src/Log.h" |
| #include "experimental/graphite/src/RecorderPriv.h" |
| #include "experimental/graphite/src/ResourceProvider.h" |
| #include "experimental/graphite/src/Texture.h" |
| #include "experimental/graphite/src/TextureProxy.h" |
| #include "src/core/SkConvertPixels.h" |
| |
| namespace skgpu { |
| |
| void UploadCommand::addCommand(ResourceProvider* resourceProvider, |
| CommandBuffer* commandBuffer) const { |
| if (!fTextureProxy) { |
| SKGPU_LOG_E("No texture proxy specified for UploadTask"); |
| return; |
| } |
| if (!fTextureProxy->instantiate(resourceProvider)) { |
| SKGPU_LOG_E("Could not instantiate texture proxy for UploadTask!"); |
| return; |
| } |
| |
| commandBuffer->copyBufferToTexture(std::move(fBuffer), |
| fTextureProxy->refTexture(), |
| fCopyData.data(), |
| fCopyData.size()); |
| } |
| |
| //--------------------------------------------------------------------------- |
| |
| size_t compute_combined_buffer_size(int mipLevelCount, |
| size_t bytesPerPixel, |
| size_t minTransferBufferAlignment, |
| const SkISize& baseDimensions, |
| SkTArray<size_t>* individualMipOffsets) { |
| SkASSERT(individualMipOffsets && !individualMipOffsets->count()); |
| SkASSERT(mipLevelCount >= 1); |
| |
| individualMipOffsets->push_back(0); |
| |
| size_t combinedBufferSize = baseDimensions.width() * bytesPerPixel * baseDimensions.height(); |
| SkISize levelDimensions = baseDimensions; |
| |
| for (int currentMipLevel = 1; currentMipLevel < mipLevelCount; ++currentMipLevel) { |
| levelDimensions = {std::max(1, levelDimensions.width() /2), |
| std::max(1, levelDimensions.height()/2)}; |
| |
| size_t trimmedSize = levelDimensions.area() * bytesPerPixel; |
| combinedBufferSize = SkAlignTo(combinedBufferSize, minTransferBufferAlignment); |
| SkASSERT((0 == combinedBufferSize % 4) && (0 == combinedBufferSize % bytesPerPixel)); |
| |
| individualMipOffsets->push_back(combinedBufferSize); |
| combinedBufferSize += trimmedSize; |
| } |
| |
| SkASSERT(individualMipOffsets->count() == mipLevelCount); |
| return combinedBufferSize; |
| } |
| |
| bool UploadList::appendUpload(Recorder* recorder, |
| sk_sp<TextureProxy> textureProxy, |
| SkColorType dataColorType, |
| const std::vector<MipLevel>& levels, |
| const SkIRect& dstRect) { |
| const Caps* caps = recorder->priv().caps(); |
| SkASSERT(caps->isTexturable(textureProxy->textureInfo())); |
| |
| unsigned int mipLevelCount = levels.size(); |
| // The assumption is either that we have no mipmaps, or that our rect is the entire texture |
| SkASSERT(mipLevelCount == 1 || dstRect == SkIRect::MakeSize(textureProxy->dimensions())); |
| |
| // We assume that if the texture has mip levels, we either upload to all the levels or just the |
| // first. |
| SkASSERT(mipLevelCount == 1 || mipLevelCount == textureProxy->textureInfo().numMipLevels()); |
| |
| if (dstRect.isEmpty()) { |
| return false; |
| } |
| |
| SkASSERT(caps->areColorTypeAndTextureInfoCompatible(dataColorType, |
| textureProxy->textureInfo())); |
| |
| if (mipLevelCount == 1 && !levels[0].fPixels) { |
| return true; // no data to upload |
| } |
| |
| for (unsigned int i = 0; i < mipLevelCount; ++i) { |
| // We do not allow any gaps in the mip data |
| if (!levels[i].fPixels) { |
| return false; |
| } |
| } |
| |
| size_t bpp = SkColorTypeBytesPerPixel(dataColorType); |
| size_t minAlignment = caps->getTransferBufferAlignment(bpp); |
| SkTArray<size_t> individualMipOffsets(mipLevelCount); |
| size_t combinedBufferSize = compute_combined_buffer_size(mipLevelCount, bpp, minAlignment, |
| dstRect.size(), &individualMipOffsets); |
| SkASSERT(combinedBufferSize); |
| |
| // TODO: get staging buffer or {void* offset, sk_sp<Buffer> buffer} pair. |
| ResourceProvider* resourceProvider = recorder->priv().resourceProvider(); |
| sk_sp<Buffer> buffer = resourceProvider->findOrCreateBuffer(combinedBufferSize, |
| BufferType::kXferCpuToGpu, |
| PrioritizeGpuReads::kNo); |
| |
| std::vector<BufferTextureCopyData> copyData(mipLevelCount); |
| |
| if (!buffer) { |
| return false; |
| } |
| char* bufferData = (char*) buffer->map(); // TODO: get from staging buffer instead |
| size_t baseOffset = 0; |
| |
| int currentWidth = dstRect.width(); |
| int currentHeight = dstRect.height(); |
| for (unsigned int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) { |
| const size_t trimRowBytes = currentWidth * bpp; |
| const size_t rowBytes = levels[currentMipLevel].fRowBytes; |
| |
| // copy data into the buffer, skipping any trailing bytes |
| char* dst = bufferData + individualMipOffsets[currentMipLevel]; |
| const char* src = (const char*)levels[currentMipLevel].fPixels; |
| SkRectMemcpy(dst, trimRowBytes, src, rowBytes, trimRowBytes, currentHeight); |
| |
| copyData[currentMipLevel].fBufferOffset = |
| baseOffset + individualMipOffsets[currentMipLevel]; |
| copyData[currentMipLevel].fBufferRowBytes = trimRowBytes; |
| copyData[currentMipLevel].fRect = { |
| dstRect.left(), dstRect.top(), // TODO: can we recompute this for mips? |
| dstRect.left() + currentWidth, dstRect.top() + currentHeight |
| }; |
| copyData[currentMipLevel].fMipLevel = currentMipLevel; |
| |
| currentWidth = std::max(1, currentWidth/2); |
| currentHeight = std::max(1, currentHeight/2); |
| } |
| |
| buffer->unmap(); |
| |
| fCommands.push_back({std::move(buffer), std::move(textureProxy), std::move(copyData)}); |
| |
| return true; |
| } |
| |
| //--------------------------------------------------------------------------- |
| |
| sk_sp<UploadTask> UploadTask::Make(UploadList* uploadList) { |
| return sk_sp<UploadTask>(new UploadTask(std::move(uploadList->fCommands))); |
| } |
| |
| UploadTask::UploadTask(std::vector<UploadCommand> commands) : fCommands(std::move(commands)) {} |
| |
| UploadTask::~UploadTask() {} |
| |
| void UploadTask::addCommands(ResourceProvider* resourceProvider, |
| CommandBuffer* commandBuffer) { |
| for (unsigned int i = 0; i < fCommands.size(); ++i) { |
| fCommands[i].addCommand(resourceProvider, commandBuffer); |
| } |
| } |
| |
| } // namespace skgpu |