| /* |
| * 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/CommandBuffer.h" |
| |
| #include "src/core/SkTraceEvent.h" |
| #include "src/gpu/RefCntedCallback.h" |
| #include "src/gpu/graphite/Buffer.h" |
| #include "src/gpu/graphite/ComputePipeline.h" |
| #include "src/gpu/graphite/DrawPass.h" |
| #include "src/gpu/graphite/GraphicsPipeline.h" |
| #include "src/gpu/graphite/Log.h" |
| #include "src/gpu/graphite/RenderPassDesc.h" |
| #include "src/gpu/graphite/ResourceProvider.h" |
| #include "src/gpu/graphite/Sampler.h" |
| #include "src/gpu/graphite/Texture.h" |
| #include "src/gpu/graphite/TextureProxy.h" |
| |
| namespace skgpu::graphite { |
| |
| CommandBuffer::CommandBuffer() {} |
| |
| CommandBuffer::~CommandBuffer() { |
| this->releaseResources(); |
| } |
| |
| void CommandBuffer::releaseResources() { |
| TRACE_EVENT0("skia.gpu", TRACE_FUNC); |
| |
| fTrackedUsageResources.clear(); |
| fCommandBufferResources.clear(); |
| } |
| |
| void CommandBuffer::resetCommandBuffer() { |
| TRACE_EVENT0("skia.gpu", TRACE_FUNC); |
| |
| this->releaseResources(); |
| this->onResetCommandBuffer(); |
| fBuffersToAsyncMap.clear(); |
| } |
| |
| void CommandBuffer::trackResource(sk_sp<Resource> resource) { |
| fTrackedUsageResources.push_back(std::move(resource)); |
| } |
| |
| void CommandBuffer::trackCommandBufferResource(sk_sp<Resource> resource) { |
| fCommandBufferResources.push_back(std::move(resource)); |
| } |
| |
| void CommandBuffer::addFinishedProc(sk_sp<RefCntedCallback> finishedProc) { |
| fFinishedProcs.push_back(std::move(finishedProc)); |
| } |
| |
| void CommandBuffer::callFinishedProcs(bool success) { |
| if (!success) { |
| for (int i = 0; i < fFinishedProcs.size(); ++i) { |
| fFinishedProcs[i]->setFailureResult(); |
| } |
| } |
| fFinishedProcs.clear(); |
| } |
| |
| void CommandBuffer::addBuffersToAsyncMapOnSubmit(SkSpan<const sk_sp<Buffer>> buffers) { |
| for (size_t i = 0; i < buffers.size(); ++i) { |
| SkASSERT(buffers[i]); |
| fBuffersToAsyncMap.push_back(buffers[i]); |
| } |
| } |
| |
| SkSpan<const sk_sp<Buffer>> CommandBuffer::buffersToAsyncMapOnSubmit() const { |
| return fBuffersToAsyncMap; |
| } |
| |
| bool CommandBuffer::addRenderPass(const RenderPassDesc& renderPassDesc, |
| sk_sp<Texture> colorTexture, |
| sk_sp<Texture> resolveTexture, |
| sk_sp<Texture> depthStencilTexture, |
| const Texture* dstCopy, |
| SkIRect dstCopyBounds, |
| SkISize viewportDims, |
| const DrawPassList& drawPasses) { |
| TRACE_EVENT0("skia.gpu", TRACE_FUNC); |
| |
| fColorAttachmentSize = colorTexture->dimensions(); |
| SkIRect colorAttachmentBounds = SkIRect::MakeSize(fColorAttachmentSize); |
| |
| SkIRect renderPassBounds; |
| for (const auto& drawPass : drawPasses) { |
| renderPassBounds.join(drawPass->bounds()); |
| } |
| if (renderPassDesc.fColorAttachment.fLoadOp == LoadOp::kClear) { |
| renderPassBounds.join(colorAttachmentBounds); |
| } |
| renderPassBounds.offset(fReplayTranslation.x(), fReplayTranslation.y()); |
| if (!renderPassBounds.intersect(colorAttachmentBounds)) { |
| // The entire RenderPass is offscreen given the replay translation so skip adding the pass |
| // at all |
| return true; |
| } |
| |
| dstCopyBounds.offset(fReplayTranslation.x(), fReplayTranslation.y()); |
| if (!dstCopyBounds.intersect(colorAttachmentBounds)) { |
| // The draws within the RenderPass that would sample from the dstCopy have been translated |
| // off screen. Set the bounds to empty and let the GPU clipping do its job. |
| dstCopyBounds = SkIRect::MakeEmpty(); |
| } |
| // Save the dstCopy texture so that it can be embedded into texture bind commands later on. |
| // Stash the texture's full dimensions on the rect so we can calculate normalized coords later. |
| fDstCopy.first = dstCopy; |
| fDstCopyBounds = dstCopy ? SkIRect::MakePtSize(dstCopyBounds.topLeft(), dstCopy->dimensions()) |
| : SkIRect::MakeEmpty(); |
| if (dstCopy && !fDstCopy.second) { |
| // Only lookup the sampler the first time we require a dstCopy. The texture can change |
| // on subsequent passes but it will always use the same nearest neighbor sampling. |
| sk_sp<Sampler> nearestNeighbor = this->resourceProvider()->findOrCreateCompatibleSampler( |
| {SkFilterMode::kNearest, SkTileMode::kClamp}); |
| fDstCopy.second = nearestNeighbor.get(); |
| this->trackResource(std::move(nearestNeighbor)); |
| } |
| |
| // We don't intersect the viewport with the render pass bounds or target size because it just |
| // defines a linear transform, which we don't want to change just because a portion of it maps |
| // to a region that gets clipped. |
| SkIRect viewport = SkIRect::MakePtSize(fReplayTranslation, viewportDims); |
| if (!this->onAddRenderPass(renderPassDesc, |
| renderPassBounds, |
| colorTexture.get(), |
| resolveTexture.get(), |
| depthStencilTexture.get(), |
| viewport, |
| drawPasses)) { |
| return false; |
| } |
| |
| if (colorTexture) { |
| this->trackCommandBufferResource(std::move(colorTexture)); |
| } |
| if (resolveTexture) { |
| this->trackCommandBufferResource(std::move(resolveTexture)); |
| } |
| if (depthStencilTexture) { |
| this->trackCommandBufferResource(std::move(depthStencilTexture)); |
| } |
| // We just assume if you are adding a render pass that the render pass will actually do work. In |
| // theory we could have a discard load that doesn't submit any draws, clears, etc. But hopefully |
| // something so trivial would be caught before getting here. |
| SkDEBUGCODE(fHasWork = true;) |
| |
| return true; |
| } |
| |
| bool CommandBuffer::addComputePass(DispatchGroupSpan dispatchGroups) { |
| TRACE_EVENT0("skia.gpu", TRACE_FUNC); |
| |
| if (!this->onAddComputePass(dispatchGroups)) { |
| return false; |
| } |
| |
| SkDEBUGCODE(fHasWork = true;) |
| |
| return true; |
| } |
| |
| bool CommandBuffer::copyBufferToBuffer(const Buffer* srcBuffer, |
| size_t srcOffset, |
| sk_sp<Buffer> dstBuffer, |
| size_t dstOffset, |
| size_t size) { |
| SkASSERT(srcBuffer); |
| SkASSERT(dstBuffer); |
| |
| if (!this->onCopyBufferToBuffer(srcBuffer, srcOffset, dstBuffer.get(), dstOffset, size)) { |
| return false; |
| } |
| |
| this->trackResource(std::move(dstBuffer)); |
| |
| SkDEBUGCODE(fHasWork = true;) |
| |
| return true; |
| } |
| |
| bool CommandBuffer::copyTextureToBuffer(sk_sp<Texture> texture, |
| SkIRect srcRect, |
| sk_sp<Buffer> buffer, |
| size_t bufferOffset, |
| size_t bufferRowBytes) { |
| SkASSERT(texture); |
| SkASSERT(buffer); |
| |
| if (!this->onCopyTextureToBuffer(texture.get(), srcRect, buffer.get(), bufferOffset, |
| bufferRowBytes)) { |
| return false; |
| } |
| |
| this->trackCommandBufferResource(std::move(texture)); |
| this->trackResource(std::move(buffer)); |
| |
| SkDEBUGCODE(fHasWork = true;) |
| |
| return true; |
| } |
| |
| bool CommandBuffer::copyBufferToTexture(const Buffer* buffer, |
| sk_sp<Texture> texture, |
| const BufferTextureCopyData* copyData, |
| int count) { |
| SkASSERT(buffer); |
| SkASSERT(texture); |
| SkASSERT(count > 0 && copyData); |
| |
| if (!this->onCopyBufferToTexture(buffer, texture.get(), copyData, count)) { |
| return false; |
| } |
| |
| this->trackCommandBufferResource(std::move(texture)); |
| |
| SkDEBUGCODE(fHasWork = true;) |
| |
| return true; |
| } |
| |
| bool CommandBuffer::copyTextureToTexture(sk_sp<Texture> src, |
| SkIRect srcRect, |
| sk_sp<Texture> dst, |
| SkIPoint dstPoint, |
| int mipLevel) { |
| SkASSERT(src); |
| SkASSERT(dst); |
| if (src->textureInfo().isProtected() == Protected::kYes && |
| dst->textureInfo().isProtected() != Protected::kYes) { |
| SKGPU_LOG_E("Can't copy from protected memory to non-protected"); |
| return false; |
| } |
| |
| if (!this->onCopyTextureToTexture(src.get(), srcRect, dst.get(), dstPoint, mipLevel)) { |
| return false; |
| } |
| |
| this->trackCommandBufferResource(std::move(src)); |
| this->trackCommandBufferResource(std::move(dst)); |
| |
| SkDEBUGCODE(fHasWork = true;) |
| |
| return true; |
| } |
| |
| bool CommandBuffer::synchronizeBufferToCpu(sk_sp<Buffer> buffer) { |
| SkASSERT(buffer); |
| |
| bool didResultInWork = false; |
| if (!this->onSynchronizeBufferToCpu(buffer.get(), &didResultInWork)) { |
| return false; |
| } |
| |
| if (didResultInWork) { |
| this->trackResource(std::move(buffer)); |
| SkDEBUGCODE(fHasWork = true;) |
| } |
| |
| return true; |
| } |
| |
| bool CommandBuffer::clearBuffer(const Buffer* buffer, size_t offset, size_t size) { |
| SkASSERT(buffer); |
| |
| if (!this->onClearBuffer(buffer, offset, size)) { |
| return false; |
| } |
| |
| SkDEBUGCODE(fHasWork = true;) |
| |
| return true; |
| } |
| |
| } // namespace skgpu::graphite |