175 lines
6.9 KiB
C++
175 lines
6.9 KiB
C++
/*
|
|
* 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
|