/* * Copyright 2021 Google LLC * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "src/gpu/graphite/task/CopyTask.h" #include "src/gpu/graphite/Buffer.h" #include "src/gpu/graphite/CommandBuffer.h" #include "src/gpu/graphite/Log.h" #include "src/gpu/graphite/Texture.h" #include "src/gpu/graphite/TextureProxy.h" namespace skgpu::graphite { sk_sp CopyBufferToBufferTask::Make(const Buffer* srcBuffer, size_t srcOffset, sk_sp dstBuffer, size_t dstOffset, size_t size) { SkASSERT(srcBuffer); SkASSERT(size <= srcBuffer->size() - srcOffset); SkASSERT(dstBuffer); SkASSERT(size <= dstBuffer->size() - dstOffset); return sk_sp(new CopyBufferToBufferTask(srcBuffer, srcOffset, std::move(dstBuffer), dstOffset, size)); } CopyBufferToBufferTask::CopyBufferToBufferTask(const Buffer* srcBuffer, size_t srcOffset, sk_sp dstBuffer, size_t dstOffset, size_t size) : fSrcBuffer(srcBuffer) , fSrcOffset(srcOffset) , fDstBuffer(std::move(dstBuffer)) , fDstOffset(dstOffset) , fSize(size) {} CopyBufferToBufferTask::~CopyBufferToBufferTask() = default; Task::Status CopyBufferToBufferTask::prepareResources(ResourceProvider*, ScratchResourceManager*, const RuntimeEffectDictionary*) { return Status::kSuccess; } Task::Status CopyBufferToBufferTask::addCommands(Context*, CommandBuffer* commandBuffer, ReplayTargetData) { if (commandBuffer->copyBufferToBuffer(fSrcBuffer, fSrcOffset, fDstBuffer, fDstOffset, fSize)) { return Status::kSuccess; } else { return Status::kFail; } } sk_sp CopyTextureToBufferTask::Make(sk_sp textureProxy, SkIRect srcRect, sk_sp buffer, size_t bufferOffset, size_t bufferRowBytes) { if (!textureProxy) { return nullptr; } return sk_sp(new CopyTextureToBufferTask(std::move(textureProxy), srcRect, std::move(buffer), bufferOffset, bufferRowBytes)); } CopyTextureToBufferTask::CopyTextureToBufferTask(sk_sp textureProxy, SkIRect srcRect, sk_sp buffer, size_t bufferOffset, size_t bufferRowBytes) : fTextureProxy(std::move(textureProxy)) , fSrcRect(srcRect) , fBuffer(std::move(buffer)) , fBufferOffset(bufferOffset) , fBufferRowBytes(bufferRowBytes) { } CopyTextureToBufferTask::~CopyTextureToBufferTask() {} Task::Status CopyTextureToBufferTask::prepareResources(ResourceProvider* resourceProvider, ScratchResourceManager*, const RuntimeEffectDictionary*) { // If the source texture hasn't been instantiated yet, it means there was no prior task that // could have initialized its contents so a readback to a buffer does not make sense. SkASSERT(fTextureProxy->isInstantiated() || fTextureProxy->isLazy()); // TODO: The copy is also a consumer of the source, so it should participate in returning // scratch resources like RenderPassTask does. For now, though, all copy tasks side step reuse // entirely and they cannot participate until they've been moved into scoping tasks like // DrawTask first. return Status::kSuccess; } Task::Status CopyTextureToBufferTask::addCommands(Context*, CommandBuffer* commandBuffer, ReplayTargetData) { if (commandBuffer->copyTextureToBuffer(fTextureProxy->refTexture(), fSrcRect, std::move(fBuffer), fBufferOffset, fBufferRowBytes)) { // TODO(b/332681367): CopyTextureToBuffer is currently only used for readback operations, // which are a one-time event. Should this just default to returning kDiscard? return Status::kSuccess; } else { return Status::kFail; } } //-------------------------------------------------------------------------------------------------- sk_sp CopyTextureToTextureTask::Make(sk_sp srcProxy, SkIRect srcRect, sk_sp dstProxy, SkIPoint dstPoint, int dstLevel) { if (!srcProxy || !dstProxy) { return nullptr; } return sk_sp(new CopyTextureToTextureTask(std::move(srcProxy), srcRect, std::move(dstProxy), dstPoint, dstLevel)); } CopyTextureToTextureTask::CopyTextureToTextureTask(sk_sp srcProxy, SkIRect srcRect, sk_sp dstProxy, SkIPoint dstPoint, int dstLevel) : fSrcProxy(std::move(srcProxy)) , fSrcRect(srcRect) , fDstProxy(std::move(dstProxy)) , fDstPoint(dstPoint) , fDstLevel(dstLevel) {} CopyTextureToTextureTask::~CopyTextureToTextureTask() {} Task::Status CopyTextureToTextureTask::prepareResources(ResourceProvider* resourceProvider, ScratchResourceManager*, const RuntimeEffectDictionary*) { // Do not instantiate the src proxy. If the source texture hasn't been instantiated yet, it // means there was no prior task that could have initialized its contents so propagating the // undefined contents to the dst does not make sense. // TODO(b/333729316): Assert that fSrcProxy is instantiated or lazy; right now it may not be // instantatiated if this is a dst readback copy for a scratch Device. In that case, a // RenderPassTask will immediately follow this copy task and instantiate the source proxy so // that addCommands() has a texture to operate on. That said, the texture's contents will be // undefined when the copy is executed ideally it just shouldn't happen. // TODO: The copy is also a consumer of the source, so it should participate in returning // scratch resources like RenderPassTask does. For now, though, all copy tasks side step reuse // entirely and they cannot participate until they've been moved into scoping tasks like // DrawTask first. In particular, for texture-to-texture copies, they should be scoped to not // invoke pending listeners for a subsequent RenderPassTask. // TODO: Use the scratch resource manager to instantiate fDstProxy, although the details of when // that texture can be returned need to be worked out. While brittle, all current use cases // of scratch texture-to-texture copies have the dst used immediately by the next task, so it // could just add a pending listener that returns the texture w/o any read counting. if (!TextureProxy::InstantiateIfNotLazy(resourceProvider, fDstProxy.get())) { SKGPU_LOG_E("Could not instantiate dst texture proxy for CopyTextureToTextureTask!"); return Status::kFail; } return Status::kSuccess; } Task::Status CopyTextureToTextureTask::addCommands(Context*, CommandBuffer* commandBuffer, ReplayTargetData) { // prepareResources() doesn't instantiate the source assuming that a prior task will have do so // as part of initializing the texture contents. SkASSERT(fSrcProxy->isInstantiated()); if (commandBuffer->copyTextureToTexture(fSrcProxy->refTexture(), fSrcRect, fDstProxy->refTexture(), fDstPoint, fDstLevel)) { // TODO(b/332681367): The calling context should be able to specify whether or not this copy // is a repeatable operation (e.g. dst readback copy for blending) or one time (e.g. client // asked for a copy of an image or surface). return Status::kSuccess; } else { return Status::kFail; } } } // namespace skgpu::graphite