xref: /aosp_15_r20/external/skia/src/gpu/graphite/task/RenderPassTask.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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/graphite/Caps.h"
11 #include "src/gpu/graphite/CommandBuffer.h"
12 #include "src/gpu/graphite/ContextPriv.h"
13 #include "src/gpu/graphite/DrawPass.h"
14 #include "src/gpu/graphite/Log.h"
15 #include "src/gpu/graphite/ResourceProvider.h"
16 #include "src/gpu/graphite/ScratchResourceManager.h"
17 #include "src/gpu/graphite/Texture.h"
18 #include "src/gpu/graphite/TextureProxy.h"
19 
20 namespace skgpu::graphite {
21 
Make(DrawPassList passes,const RenderPassDesc & desc,sk_sp<TextureProxy> target,sk_sp<TextureProxy> dstCopy,SkIRect dstCopyBounds)22 sk_sp<RenderPassTask> RenderPassTask::Make(DrawPassList passes,
23                                            const RenderPassDesc& desc,
24                                            sk_sp<TextureProxy> target,
25                                            sk_sp<TextureProxy> dstCopy,
26                                            SkIRect dstCopyBounds) {
27     // For now we have one DrawPass per RenderPassTask
28     SkASSERT(passes.size() == 1);
29     // We should only have dst copy bounds if we have a dst copy texture, and the texture should be
30     // big enough to cover the copy bounds that will be sampled.
31     SkASSERT(dstCopyBounds.isEmpty() == !dstCopy);
32     SkASSERT(!dstCopy || (dstCopy->dimensions().width() >= dstCopyBounds.width() &&
33                           dstCopy->dimensions().height() >= dstCopyBounds.height()));
34     if (!target) {
35         return nullptr;
36     }
37 
38     if (desc.fColorAttachment.fTextureInfo.isValid()) {
39         // The color attachment's samples count must ether match the render pass's samples count
40         // or be 1 (when multisampled render to single sampled is used).
41         SkASSERT(desc.fSampleCount == desc.fColorAttachment.fTextureInfo.numSamples() ||
42                  1 == desc.fColorAttachment.fTextureInfo.numSamples());
43     }
44 
45     if (desc.fDepthStencilAttachment.fTextureInfo.isValid()) {
46         SkASSERT(desc.fSampleCount == desc.fDepthStencilAttachment.fTextureInfo.numSamples());
47     }
48 
49     return sk_sp<RenderPassTask>(new RenderPassTask(std::move(passes),
50                                                     desc,
51                                                     std::move(target),
52                                                     std::move(dstCopy),
53                                                     dstCopyBounds));
54 }
55 
RenderPassTask(DrawPassList passes,const RenderPassDesc & desc,sk_sp<TextureProxy> target,sk_sp<TextureProxy> dstCopy,SkIRect dstCopyBounds)56 RenderPassTask::RenderPassTask(DrawPassList passes,
57                                const RenderPassDesc& desc,
58                                sk_sp<TextureProxy> target,
59                                sk_sp<TextureProxy> dstCopy,
60                                SkIRect dstCopyBounds)
61         : fDrawPasses(std::move(passes))
62         , fRenderPassDesc(desc)
63         , fTarget(std::move(target))
64         , fDstCopy(std::move(dstCopy))
65         , fDstCopyBounds(dstCopyBounds) {}
66 
67 RenderPassTask::~RenderPassTask() = default;
68 
prepareResources(ResourceProvider * resourceProvider,ScratchResourceManager * scratchManager,const RuntimeEffectDictionary * runtimeDict)69 Task::Status RenderPassTask::prepareResources(ResourceProvider* resourceProvider,
70                                               ScratchResourceManager* scratchManager,
71                                               const RuntimeEffectDictionary* runtimeDict) {
72     SkASSERT(fTarget);
73     if (!TextureProxy::InstantiateIfNotLazy(scratchManager, fTarget.get())) {
74         SKGPU_LOG_W("Failed to instantiate RenderPassTask target. Will not create renderpass!");
75         SKGPU_LOG_W("Dimensions are (%d, %d).",
76                     fTarget->dimensions().width(), fTarget->dimensions().height());
77         return Status::kFail;
78     }
79 
80     // Assuming one draw pass per renderpasstask for now
81     SkASSERT(fDrawPasses.size() == 1);
82     for (const auto& drawPass: fDrawPasses) {
83         if (!drawPass->prepareResources(resourceProvider, runtimeDict, fRenderPassDesc)) {
84             return Status::kFail;
85         }
86     }
87 
88     // Once all internal resources have been prepared and instantiated, reclaim any pending returns
89     // from the scratch manager, since at the equivalent point in the task graph's addCommands()
90     // phase, the renderpass will have sampled from any scratch textures and their contents no
91     // longer have to be preserved.
92     scratchManager->notifyResourcesConsumed();
93     return Status::kSuccess;
94 }
95 
addCommands(Context * context,CommandBuffer * commandBuffer,ReplayTargetData replayData)96 Task::Status RenderPassTask::addCommands(Context* context,
97                                          CommandBuffer* commandBuffer,
98                                          ReplayTargetData replayData) {
99     // TBD: Expose the surfaces that will need to be attached within the renderpass?
100 
101     // Instantiate the target
102     SkASSERT(fTarget && fTarget->isInstantiated());
103     SkASSERT(!fDstCopy || fDstCopy->isInstantiated());
104 
105     // Set any replay translation and clip, as needed.
106     // The clip set here will intersect with any scissor set during this render pass.
107     const SkIRect renderTargetBounds = SkIRect::MakeSize(fTarget->dimensions());
108     if (fTarget->texture() == replayData.fTarget) {
109         // We're drawing to the final replay target, so apply replay translation and clip.
110         if (replayData.fClip.isEmpty()) {
111             // If no replay clip is defined, default to the render target bounds.
112             commandBuffer->setReplayTranslationAndClip(replayData.fTranslation,
113                                                        renderTargetBounds);
114         } else {
115             // If a replay clip is defined, intersect it with the render target bounds.
116             // If the intersection is empty, we can skip this entire render pass.
117             SkIRect replayClip = replayData.fClip;
118             if (!replayClip.intersect(renderTargetBounds)) {
119                 return Status::kSuccess;
120             }
121             commandBuffer->setReplayTranslationAndClip(replayData.fTranslation, replayClip);
122         }
123     } else {
124         // We're not drawing to the final replay target, so don't apply replay translation or clip.
125         // In this case as well, the clip we set defaults to the render target bounds.
126         commandBuffer->setReplayTranslationAndClip({0, 0}, renderTargetBounds);
127     }
128 
129     // We don't instantiate the MSAA or DS attachments in prepareResources because we want to use
130     // the discardable attachments from the Context.
131     ResourceProvider* resourceProvider = context->priv().resourceProvider();
132     sk_sp<Texture> colorAttachment;
133     sk_sp<Texture> resolveAttachment;
134     if (fRenderPassDesc.fColorResolveAttachment.fTextureInfo.isValid()) {
135         SkASSERT(fTarget->numSamples() == 1 &&
136                  fRenderPassDesc.fColorAttachment.fTextureInfo.numSamples() > 1);
137         colorAttachment = resourceProvider->findOrCreateDiscardableMSAAAttachment(
138                 fTarget->dimensions(), fRenderPassDesc.fColorAttachment.fTextureInfo);
139         if (!colorAttachment) {
140             SKGPU_LOG_W("Could not get Color attachment for RenderPassTask");
141             return Status::kFail;
142         }
143         resolveAttachment = fTarget->refTexture();
144     } else {
145         colorAttachment = fTarget->refTexture();
146     }
147 
148     sk_sp<Texture> depthStencilAttachment;
149     if (fRenderPassDesc.fDepthStencilAttachment.fTextureInfo.isValid()) {
150         // TODO: ensure this is a scratch/recycled texture
151         SkASSERT(fTarget->isInstantiated());
152         SkISize dimensions = context->priv().caps()->getDepthAttachmentDimensions(
153                 fTarget->texture()->textureInfo(), fTarget->dimensions());
154         depthStencilAttachment = resourceProvider->findOrCreateDepthStencilAttachment(
155                 dimensions, fRenderPassDesc.fDepthStencilAttachment.fTextureInfo);
156         if (!depthStencilAttachment) {
157             SKGPU_LOG_W("Could not get DepthStencil attachment for RenderPassTask");
158             return Status::kFail;
159         }
160     }
161 
162     // TODO(b/313629288) we always pass in the render target's dimensions as the viewport here.
163     // Using the dimensions of the logical device that we're drawing to could reduce flakiness in
164     // rendering.
165     if (commandBuffer->addRenderPass(fRenderPassDesc,
166                                      std::move(colorAttachment),
167                                      std::move(resolveAttachment),
168                                      std::move(depthStencilAttachment),
169                                      fDstCopy ? fDstCopy->texture() : nullptr,
170                                      fDstCopyBounds,
171                                      fTarget->dimensions(),
172                                      fDrawPasses)) {
173         return Status::kSuccess;
174     } else {
175         return Status::kFail;
176     }
177 }
178 
179 } // namespace skgpu::graphite
180