1 /*
2 * Copyright 2021 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "src/gpu/graphite/task/RenderPassTask.h"
9
10 #include "src/gpu/SkBackingFit.h"
11 #include "src/gpu/graphite/Caps.h"
12 #include "src/gpu/graphite/CommandBuffer.h"
13 #include "src/gpu/graphite/ContextPriv.h"
14 #include "src/gpu/graphite/DrawPass.h"
15 #include "src/gpu/graphite/Log.h"
16 #include "src/gpu/graphite/ResourceProvider.h"
17 #include "src/gpu/graphite/ScratchResourceManager.h"
18 #include "src/gpu/graphite/Texture.h"
19 #include "src/gpu/graphite/TextureProxy.h"
20
21 namespace skgpu::graphite {
22
23 namespace {
24
get_msaa_size(const SkISize & targetSize,const Caps & caps)25 SkISize get_msaa_size(const SkISize& targetSize, const Caps& caps) {
26 if (caps.differentResolveAttachmentSizeSupport()) {
27 // Use approx size for better reuse.
28 return GetApproxSize(targetSize);
29 }
30
31 return targetSize;
32 }
33
34 } // anonymous namespace
35
Make(DrawPassList passes,const RenderPassDesc & desc,sk_sp<TextureProxy> target,sk_sp<TextureProxy> dstCopy,SkIRect dstReadBounds)36 sk_sp<RenderPassTask> RenderPassTask::Make(DrawPassList passes,
37 const RenderPassDesc& desc,
38 sk_sp<TextureProxy> target,
39 sk_sp<TextureProxy> dstCopy,
40 SkIRect dstReadBounds) {
41 // For now we have one DrawPass per RenderPassTask
42 SkASSERT(passes.size() == 1);
43 // If we have a dst copy texture, ensure it is big enough to cover the copy bounds that
44 // will be sampled.
45 SkASSERT(!dstCopy || (dstCopy->dimensions().width() >= dstReadBounds.width() &&
46 dstCopy->dimensions().height() >= dstReadBounds.height()));
47 if (!target) {
48 return nullptr;
49 }
50
51 if (desc.fColorAttachment.fTextureInfo.isValid()) {
52 // The color attachment's samples count must ether match the render pass's samples count
53 // or be 1 (when multisampled render to single sampled is used).
54 SkASSERT(desc.fSampleCount == desc.fColorAttachment.fTextureInfo.numSamples() ||
55 1 == desc.fColorAttachment.fTextureInfo.numSamples());
56 }
57
58 if (desc.fDepthStencilAttachment.fTextureInfo.isValid()) {
59 SkASSERT(desc.fSampleCount == desc.fDepthStencilAttachment.fTextureInfo.numSamples());
60 }
61
62 return sk_sp<RenderPassTask>(new RenderPassTask(std::move(passes),
63 desc,
64 std::move(target),
65 std::move(dstCopy),
66 dstReadBounds));
67 }
68
RenderPassTask(DrawPassList passes,const RenderPassDesc & desc,sk_sp<TextureProxy> target,sk_sp<TextureProxy> dstCopy,SkIRect dstReadBounds)69 RenderPassTask::RenderPassTask(DrawPassList passes,
70 const RenderPassDesc& desc,
71 sk_sp<TextureProxy> target,
72 sk_sp<TextureProxy> dstCopy,
73 SkIRect dstReadBounds)
74 : fDrawPasses(std::move(passes))
75 , fRenderPassDesc(desc)
76 , fTarget(std::move(target))
77 , fDstCopy(std::move(dstCopy))
78 , fDstReadBounds(dstReadBounds) {}
79
80 RenderPassTask::~RenderPassTask() = default;
81
prepareResources(ResourceProvider * resourceProvider,ScratchResourceManager * scratchManager,const RuntimeEffectDictionary * runtimeDict)82 Task::Status RenderPassTask::prepareResources(ResourceProvider* resourceProvider,
83 ScratchResourceManager* scratchManager,
84 const RuntimeEffectDictionary* runtimeDict) {
85 SkASSERT(fTarget);
86
87 bool instantiated;
88 if (scratchManager->pendingReadCount(fTarget.get()) == 0) {
89 // TODO(b/389908339, b/338976898): If there are no pending reads on a scratch texture
90 // instantiation request, it means that the scratch Device was caught by a
91 // Recorder::flushTrackedDevices() event but hasn't actually been restored to its parent. In
92 // this case, the eventual read of the surface will be in another Recording and it can't be
93 // allocated as a true scratch resource.
94 //
95 // Without pending reads, DrawTask does not track its lifecycle to return the scratch
96 // resource, so we need to match that and instantiate with a regular non-shareable resource.
97 instantiated = TextureProxy::InstantiateIfNotLazy(resourceProvider, fTarget.get());
98 } else {
99 instantiated = TextureProxy::InstantiateIfNotLazy(scratchManager, fTarget.get());
100 }
101 if (!instantiated) {
102 SKGPU_LOG_W("Failed to instantiate RenderPassTask target. Will not create renderpass!");
103 SKGPU_LOG_W("Dimensions are (%d, %d).",
104 fTarget->dimensions().width(), fTarget->dimensions().height());
105 return Status::kFail;
106 }
107
108 // Assuming one draw pass per renderpasstask for now
109 SkASSERT(fDrawPasses.size() == 1);
110 for (const auto& drawPass: fDrawPasses) {
111 if (!drawPass->prepareResources(resourceProvider, runtimeDict, fRenderPassDesc)) {
112 return Status::kFail;
113 }
114 }
115
116 // Once all internal resources have been prepared and instantiated, reclaim any pending returns
117 // from the scratch manager, since at the equivalent point in the task graph's addCommands()
118 // phase, the renderpass will have sampled from any scratch textures and their contents no
119 // longer have to be preserved.
120 scratchManager->notifyResourcesConsumed();
121 return Status::kSuccess;
122 }
123
addCommands(Context * context,CommandBuffer * commandBuffer,ReplayTargetData replayData)124 Task::Status RenderPassTask::addCommands(Context* context,
125 CommandBuffer* commandBuffer,
126 ReplayTargetData replayData) {
127 // TBD: Expose the surfaces that will need to be attached within the renderpass?
128
129 // Instantiate the target
130 SkASSERT(fTarget && fTarget->isInstantiated());
131 SkASSERT(!fDstCopy || fDstCopy->isInstantiated());
132
133 // Only apply the replay translation and clip if we're drawing to the final replay target.
134 const SkIRect renderTargetBounds = SkIRect::MakeSize(fTarget->dimensions());
135 if (fTarget->texture() == replayData.fTarget) {
136 // The clip set here will intersect with the render target bounds, and then any scissor set
137 // during this render pass. If there is no intersection between the clip and the render
138 // target bounds, we can skip this entire render pass.
139 if (!commandBuffer->setReplayTranslationAndClip(
140 replayData.fTranslation, replayData.fClip, renderTargetBounds)) {
141 return Status::kSuccess;
142 }
143
144 } else {
145 // An empty clip is ignored, and will default to the render target bounds.
146 constexpr SkIVector kNoReplayTranslation = {0, 0};
147 constexpr SkIRect kNoReplayClip = SkIRect::MakeEmpty();
148 commandBuffer->setReplayTranslationAndClip(
149 kNoReplayTranslation, kNoReplayClip, renderTargetBounds);
150 }
151
152 // We don't instantiate the MSAA or DS attachments in prepareResources because we want to use
153 // the discardable attachments from the Context.
154 ResourceProvider* resourceProvider = context->priv().resourceProvider();
155 sk_sp<Texture> colorAttachment;
156 sk_sp<Texture> resolveAttachment;
157 if (fRenderPassDesc.fColorResolveAttachment.fTextureInfo.isValid()) {
158 SkASSERT(fTarget->numSamples() == 1 &&
159 fRenderPassDesc.fColorAttachment.fTextureInfo.numSamples() > 1);
160 colorAttachment = resourceProvider->findOrCreateDiscardableMSAAAttachment(
161 get_msaa_size(fTarget->dimensions(), *context->priv().caps()),
162 fRenderPassDesc.fColorAttachment.fTextureInfo);
163 if (!colorAttachment) {
164 SKGPU_LOG_W("Could not get Color attachment for RenderPassTask");
165 return Status::kFail;
166 }
167 resolveAttachment = fTarget->refTexture();
168 } else {
169 colorAttachment = fTarget->refTexture();
170 }
171
172 sk_sp<Texture> depthStencilAttachment;
173 if (fRenderPassDesc.fDepthStencilAttachment.fTextureInfo.isValid()) {
174 // TODO: ensure this is a scratch/recycled texture
175 SkASSERT(fTarget->isInstantiated());
176 SkISize dimensions = context->priv().caps()->getDepthAttachmentDimensions(
177 colorAttachment->textureInfo(), colorAttachment->dimensions());
178
179 depthStencilAttachment = resourceProvider->findOrCreateDepthStencilAttachment(
180 dimensions, fRenderPassDesc.fDepthStencilAttachment.fTextureInfo);
181 if (!depthStencilAttachment) {
182 SKGPU_LOG_W("Could not get DepthStencil attachment for RenderPassTask");
183 return Status::kFail;
184 }
185 }
186
187 // TODO(b/313629288) we always pass in the render target's dimensions as the viewport here.
188 // Using the dimensions of the logical device that we're drawing to could reduce flakiness in
189 // rendering.
190 if (commandBuffer->addRenderPass(fRenderPassDesc,
191 std::move(colorAttachment),
192 std::move(resolveAttachment),
193 std::move(depthStencilAttachment),
194 fDstCopy ? fDstCopy->texture() : nullptr,
195 fDstReadBounds,
196 fTarget->dimensions(),
197 fDrawPasses)) {
198 return Status::kSuccess;
199 } else {
200 return Status::kFail;
201 }
202 }
203
204 } // namespace skgpu::graphite
205