/* * 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* 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, SkColorType dataColorType, const std::vector& 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 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} pair. ResourceProvider* resourceProvider = recorder->priv().resourceProvider(); sk_sp buffer = resourceProvider->findOrCreateBuffer(combinedBufferSize, BufferType::kXferCpuToGpu, PrioritizeGpuReads::kNo); std::vector 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::Make(UploadList* uploadList) { return sk_sp(new UploadTask(std::move(uploadList->fCommands))); } UploadTask::UploadTask(std::vector 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