1 /*
2  * Copyright 2020 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/d3d/GrD3DOpsRenderPass.h"
9 
10 #include "src/gpu/GrBackendUtils.h"
11 #include "src/gpu/GrOpFlushState.h"
12 #include "src/gpu/GrProgramDesc.h"
13 #include "src/gpu/GrRenderTarget.h"
14 #include "src/gpu/GrStencilSettings.h"
15 #include "src/gpu/d3d/GrD3DBuffer.h"
16 #include "src/gpu/d3d/GrD3DCommandSignature.h"
17 #include "src/gpu/d3d/GrD3DGpu.h"
18 #include "src/gpu/d3d/GrD3DPipelineState.h"
19 #include "src/gpu/d3d/GrD3DPipelineStateBuilder.h"
20 #include "src/gpu/d3d/GrD3DRenderTarget.h"
21 #include "src/gpu/d3d/GrD3DTexture.h"
22 
23 #ifdef SK_DEBUG
24 #include "include/gpu/GrDirectContext.h"
25 #include "src/gpu/GrDirectContextPriv.h"
26 #endif
27 
GrD3DOpsRenderPass(GrD3DGpu * gpu)28 GrD3DOpsRenderPass::GrD3DOpsRenderPass(GrD3DGpu* gpu) : fGpu(gpu) {}
29 
set(GrRenderTarget * rt,GrSurfaceOrigin origin,const SkIRect & bounds,const GrOpsRenderPass::LoadAndStoreInfo & colorInfo,const GrOpsRenderPass::StencilLoadAndStoreInfo & stencilInfo,const SkTArray<GrSurfaceProxy *,true> & sampledProxies)30 bool GrD3DOpsRenderPass::set(GrRenderTarget* rt, GrSurfaceOrigin origin, const SkIRect& bounds,
31                              const GrOpsRenderPass::LoadAndStoreInfo& colorInfo,
32                              const GrOpsRenderPass::StencilLoadAndStoreInfo& stencilInfo,
33                              const SkTArray<GrSurfaceProxy*, true>& sampledProxies) {
34     SkASSERT(!fRenderTarget);
35     SkASSERT(fGpu == rt->getContext()->priv().getGpu());
36 
37     this->INHERITED::set(rt, origin);
38 
39     fBounds = bounds;
40 
41     fColorLoadOp = colorInfo.fLoadOp;
42     fClearColor = colorInfo.fClearColor;
43 
44     // TODO
45 
46     return true;
47 }
48 
~GrD3DOpsRenderPass()49 GrD3DOpsRenderPass::~GrD3DOpsRenderPass() {}
50 
gpu()51 GrGpu* GrD3DOpsRenderPass::gpu() { return fGpu; }
52 
onBegin()53 void GrD3DOpsRenderPass::onBegin() {
54     GrD3DRenderTarget* d3dRT = static_cast<GrD3DRenderTarget*>(fRenderTarget);
55     if (d3dRT->numSamples() > 1) {
56         d3dRT->msaaTextureResource()->setResourceState(fGpu, D3D12_RESOURCE_STATE_RENDER_TARGET);
57     } else {
58         d3dRT->setResourceState(fGpu, D3D12_RESOURCE_STATE_RENDER_TARGET);
59     }
60     fGpu->currentCommandList()->setRenderTarget(d3dRT);
61 
62     if (GrLoadOp::kClear == fColorLoadOp) {
63         // Passing in nullptr for the rect clears the entire d3d RT. Is this correct? Does the load
64         // op respect the logical bounds of a RT?
65         fGpu->currentCommandList()->clearRenderTargetView(d3dRT, fClearColor, nullptr);
66     }
67 
68     if (auto stencil = d3dRT->getStencilAttachment()) {
69         GrD3DAttachment* d3dStencil = static_cast<GrD3DAttachment*>(stencil);
70         d3dStencil->setResourceState(fGpu, D3D12_RESOURCE_STATE_DEPTH_WRITE);
71         if (fStencilLoadOp == GrLoadOp::kClear) {
72             fGpu->currentCommandList()->clearDepthStencilView(d3dStencil, 0, nullptr);
73         }
74     }
75 }
76 
set_stencil_ref(GrD3DGpu * gpu,const GrProgramInfo & info)77 void set_stencil_ref(GrD3DGpu* gpu, const GrProgramInfo& info) {
78     GrStencilSettings stencilSettings = info.nonGLStencilSettings();
79     if (!stencilSettings.isDisabled()) {
80         unsigned int stencilRef = 0;
81         if (stencilSettings.isTwoSided()) {
82             SkASSERT(stencilSettings.postOriginCCWFace(info.origin()).fRef ==
83                      stencilSettings.postOriginCWFace(info.origin()).fRef);
84             stencilRef = stencilSettings.postOriginCCWFace(info.origin()).fRef;
85         } else {
86             stencilRef = stencilSettings.singleSidedFace().fRef;
87         }
88         gpu->currentCommandList()->setStencilRef(stencilRef);
89     }
90 }
91 
set_blend_factor(GrD3DGpu * gpu,const GrProgramInfo & info)92 void set_blend_factor(GrD3DGpu* gpu, const GrProgramInfo& info) {
93     const GrXferProcessor& xferProcessor = info.pipeline().getXferProcessor();
94     const GrSwizzle& swizzle = info.pipeline().writeSwizzle();
95     const GrXferProcessor::BlendInfo& blendInfo = xferProcessor.getBlendInfo();
96     GrBlendCoeff srcCoeff = blendInfo.fSrcBlend;
97     GrBlendCoeff dstCoeff = blendInfo.fDstBlend;
98     float floatColors[4];
99     if (GrBlendCoeffRefsConstant(srcCoeff) || GrBlendCoeffRefsConstant(dstCoeff)) {
100         // Swizzle the blend to match what the shader will output.
101         SkPMColor4f blendConst = swizzle.applyTo(blendInfo.fBlendConstant);
102         floatColors[0] = blendConst.fR;
103         floatColors[1] = blendConst.fG;
104         floatColors[2] = blendConst.fB;
105         floatColors[3] = blendConst.fA;
106     } else {
107         memset(floatColors, 0, 4 * sizeof(float));
108     }
109     gpu->currentCommandList()->setBlendFactor(floatColors);
110 }
111 
set_primitive_topology(GrD3DGpu * gpu,const GrProgramInfo & info)112 void set_primitive_topology(GrD3DGpu* gpu, const GrProgramInfo& info) {
113     D3D12_PRIMITIVE_TOPOLOGY topology = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED;
114     switch (info.primitiveType()) {
115         case GrPrimitiveType::kTriangles:
116             topology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
117             break;
118         case GrPrimitiveType::kTriangleStrip:
119             topology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
120             break;
121         case GrPrimitiveType::kPoints:
122             topology = D3D_PRIMITIVE_TOPOLOGY_POINTLIST;
123             break;
124         case GrPrimitiveType::kLines:
125             topology = D3D_PRIMITIVE_TOPOLOGY_LINELIST;
126             break;
127         case GrPrimitiveType::kLineStrip:
128             topology = D3D_PRIMITIVE_TOPOLOGY_LINESTRIP;
129             break;
130         case GrPrimitiveType::kPatches: // Unsupported
131         case GrPrimitiveType::kPath: // Unsupported
132         default:
133             SkUNREACHABLE;
134     }
135     gpu->currentCommandList()->setPrimitiveTopology(topology);
136 }
137 
set_scissor_rects(GrD3DGpu * gpu,const GrRenderTarget * renderTarget,GrSurfaceOrigin rtOrigin,const SkIRect & scissorRect)138 void set_scissor_rects(GrD3DGpu* gpu, const GrRenderTarget* renderTarget, GrSurfaceOrigin rtOrigin,
139                        const SkIRect& scissorRect) {
140     SkASSERT(scissorRect.isEmpty() ||
141              SkIRect::MakeWH(renderTarget->width(), renderTarget->height()).contains(scissorRect));
142 
143     D3D12_RECT scissor;
144     scissor.left = scissorRect.fLeft;
145     scissor.right = scissorRect.fRight;
146     if (kTopLeft_GrSurfaceOrigin == rtOrigin) {
147         scissor.top = scissorRect.fTop;
148     } else {
149         SkASSERT(kBottomLeft_GrSurfaceOrigin == rtOrigin);
150         scissor.top = renderTarget->height() - scissorRect.fBottom;
151     }
152     scissor.bottom = scissor.top + scissorRect.height();
153 
154     SkASSERT(scissor.left >= 0);
155     SkASSERT(scissor.top >= 0);
156     gpu->currentCommandList()->setScissorRects(1, &scissor);
157 }
158 
set_viewport(GrD3DGpu * gpu,const GrRenderTarget * renderTarget)159 void set_viewport(GrD3DGpu* gpu, const GrRenderTarget* renderTarget) {
160     D3D12_VIEWPORT viewport;
161     viewport.TopLeftX = 0.0f;
162     viewport.TopLeftY = 0.0f;
163     viewport.Width = SkIntToScalar(renderTarget->width());
164     viewport.Height = SkIntToScalar(renderTarget->height());
165     viewport.MinDepth = 0.0f;
166     viewport.MaxDepth = 1.0f;
167     gpu->currentCommandList()->setViewports(1, &viewport);
168 }
169 
onBindPipeline(const GrProgramInfo & info,const SkRect & drawBounds)170 bool GrD3DOpsRenderPass::onBindPipeline(const GrProgramInfo& info, const SkRect& drawBounds) {
171     SkRect rtRect = SkRect::Make(fBounds);
172     if (rtRect.intersect(drawBounds)) {
173         rtRect.roundOut(&fCurrentPipelineBounds);
174     } else {
175         fCurrentPipelineBounds.setEmpty();
176     }
177 
178     fCurrentPipelineState =
179             fGpu->resourceProvider().findOrCreateCompatiblePipelineState(fRenderTarget, info);
180     if (!fCurrentPipelineState) {
181         return false;
182     }
183 
184     fGpu->currentCommandList()->setGraphicsRootSignature(fCurrentPipelineState->rootSignature());
185     fGpu->currentCommandList()->setPipelineState(fCurrentPipelineState);
186     if (info.pipeline().isHWAntialiasState()) {
187         fGpu->currentCommandList()->setDefaultSamplePositions();
188     } else {
189         fGpu->currentCommandList()->setCenteredSamplePositions(fRenderTarget->numSamples());
190     }
191 
192     fCurrentPipelineState->setAndBindConstants(fGpu, fRenderTarget, info);
193 
194     set_stencil_ref(fGpu, info);
195     set_blend_factor(fGpu, info);
196     set_primitive_topology(fGpu, info);
197     if (!info.pipeline().isScissorTestEnabled()) {
198         // "Disable" scissor by setting it to the full pipeline bounds.
199         set_scissor_rects(fGpu, fRenderTarget, fOrigin, fCurrentPipelineBounds);
200     }
201     set_viewport(fGpu, fRenderTarget);
202 
203     return true;
204 }
205 
onSetScissorRect(const SkIRect & scissor)206 void GrD3DOpsRenderPass::onSetScissorRect(const SkIRect& scissor) {
207     SkIRect combinedScissorRect;
208     if (!combinedScissorRect.intersect(fCurrentPipelineBounds, scissor)) {
209         combinedScissorRect = SkIRect::MakeEmpty();
210     }
211 
212     set_scissor_rects(fGpu, fRenderTarget, fOrigin, combinedScissorRect);
213 }
214 
update_resource_state(GrTexture * tex,GrRenderTarget * rt,GrD3DGpu * gpu)215 void update_resource_state(GrTexture* tex, GrRenderTarget* rt, GrD3DGpu* gpu) {
216     SkASSERT(!tex->isProtected() || (rt->isProtected() && gpu->protectedContext()));
217     GrD3DTexture* d3dTex = static_cast<GrD3DTexture*>(tex);
218     SkASSERT(d3dTex);
219     d3dTex->setResourceState(gpu, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
220 }
221 
onBindTextures(const GrPrimitiveProcessor & primProc,const GrSurfaceProxy * const primProcTextures[],const GrPipeline & pipeline)222 bool GrD3DOpsRenderPass::onBindTextures(const GrPrimitiveProcessor& primProc,
223                                         const GrSurfaceProxy* const primProcTextures[],
224                                         const GrPipeline& pipeline) {
225     SkASSERT(fCurrentPipelineState);
226 
227     // update textures to sampled resource state
228     for (int i = 0; i < primProc.numTextureSamplers(); ++i) {
229         update_resource_state(primProcTextures[i]->peekTexture(), fRenderTarget, fGpu);
230     }
231 
232     pipeline.visitTextureEffects([&](const GrTextureEffect& te) {
233         update_resource_state(te.texture(), fRenderTarget, fGpu);
234     });
235 
236     if (GrTexture* dstTexture = pipeline.peekDstTexture()) {
237         update_resource_state(dstTexture, fRenderTarget, fGpu);
238     }
239 
240     // TODO: possibly check for success once we start binding properly
241     fCurrentPipelineState->setAndBindTextures(fGpu, primProc, primProcTextures, pipeline);
242 
243     return true;
244 }
245 
onBindBuffers(sk_sp<const GrBuffer> indexBuffer,sk_sp<const GrBuffer> instanceBuffer,sk_sp<const GrBuffer> vertexBuffer,GrPrimitiveRestart primRestart)246 void GrD3DOpsRenderPass::onBindBuffers(sk_sp<const GrBuffer> indexBuffer,
247                                        sk_sp<const GrBuffer> instanceBuffer,
248                                        sk_sp<const GrBuffer> vertexBuffer,
249                                        GrPrimitiveRestart primRestart) {
250     SkASSERT(GrPrimitiveRestart::kNo == primRestart);
251     SkASSERT(fCurrentPipelineState);
252     SkASSERT(!fGpu->caps()->usePrimitiveRestart());  // Ignore primitiveRestart parameter.
253 
254     GrD3DDirectCommandList* currCmdList = fGpu->currentCommandList();
255     SkASSERT(currCmdList);
256 
257     fCurrentPipelineState->bindBuffers(fGpu, std::move(indexBuffer), std::move(instanceBuffer),
258                                        std::move(vertexBuffer), currCmdList);
259 }
260 
onDrawInstanced(int instanceCount,int baseInstance,int vertexCount,int baseVertex)261 void GrD3DOpsRenderPass::onDrawInstanced(int instanceCount, int baseInstance, int vertexCount,
262                                             int baseVertex) {
263     SkASSERT(fCurrentPipelineState);
264     fGpu->currentCommandList()->drawInstanced(vertexCount, instanceCount, baseVertex, baseInstance);
265     fGpu->stats()->incNumDraws();
266 }
267 
onDrawIndexedInstanced(int indexCount,int baseIndex,int instanceCount,int baseInstance,int baseVertex)268 void GrD3DOpsRenderPass::onDrawIndexedInstanced(int indexCount, int baseIndex, int instanceCount,
269                                                 int baseInstance, int baseVertex) {
270     SkASSERT(fCurrentPipelineState);
271     fGpu->currentCommandList()->drawIndexedInstanced(indexCount, instanceCount, baseIndex,
272                                                      baseVertex, baseInstance);
273     fGpu->stats()->incNumDraws();
274 }
275 
onDrawIndirect(const GrBuffer * buffer,size_t offset,int drawCount)276 void GrD3DOpsRenderPass::onDrawIndirect(const GrBuffer* buffer, size_t offset, int drawCount) {
277     constexpr unsigned int kSlot = 0;
278     sk_sp<GrD3DCommandSignature> cmdSig = fGpu->resourceProvider().findOrCreateCommandSignature(
279             GrD3DCommandSignature::ForIndexed::kNo, kSlot);
280     fGpu->currentCommandList()->executeIndirect(cmdSig, drawCount,
281                                                 static_cast<const GrD3DBuffer*>(buffer), offset);
282     fGpu->stats()->incNumDraws();
283 }
284 
onDrawIndexedIndirect(const GrBuffer * buffer,size_t offset,int drawCount)285 void GrD3DOpsRenderPass::onDrawIndexedIndirect(const GrBuffer* buffer, size_t offset,
286                                                int drawCount) {
287     constexpr unsigned int kSlot = 0;
288     sk_sp<GrD3DCommandSignature> cmdSig = fGpu->resourceProvider().findOrCreateCommandSignature(
289             GrD3DCommandSignature::ForIndexed::kYes, kSlot);
290     fGpu->currentCommandList()->executeIndirect(cmdSig, drawCount,
291                                                 static_cast<const GrD3DBuffer*>(buffer), offset);
292     fGpu->stats()->incNumDraws();
293 }
294 
295 
scissor_to_d3d_clear_rect(const GrScissorState & scissor,const GrSurface * surface,GrSurfaceOrigin origin)296 static D3D12_RECT scissor_to_d3d_clear_rect(const GrScissorState& scissor,
297                                             const GrSurface* surface,
298                                             GrSurfaceOrigin origin) {
299     D3D12_RECT clearRect;
300     // Flip rect if necessary
301     SkIRect d3dRect;
302     if (!scissor.enabled()) {
303         d3dRect.setXYWH(0, 0, surface->width(), surface->height());
304     } else if (kBottomLeft_GrSurfaceOrigin != origin) {
305         d3dRect = scissor.rect();
306     } else {
307         d3dRect.setLTRB(scissor.rect().fLeft, surface->height() - scissor.rect().fBottom,
308                         scissor.rect().fRight, surface->height() - scissor.rect().fTop);
309     }
310     clearRect.left = d3dRect.fLeft;
311     clearRect.right = d3dRect.fRight;
312     clearRect.top = d3dRect.fTop;
313     clearRect.bottom = d3dRect.fBottom;
314     return clearRect;
315 }
316 
onClear(const GrScissorState & scissor,const SkPMColor4f & color)317 void GrD3DOpsRenderPass::onClear(const GrScissorState& scissor, const SkPMColor4f& color) {
318     D3D12_RECT clearRect = scissor_to_d3d_clear_rect(scissor, fRenderTarget, fOrigin);
319     auto d3dRT = static_cast<GrD3DRenderTarget*>(fRenderTarget);
320     SkASSERT(d3dRT->grD3DResourceState()->getResourceState() == D3D12_RESOURCE_STATE_RENDER_TARGET);
321     fGpu->currentCommandList()->clearRenderTargetView(d3dRT, color, &clearRect);
322 }
323 
onClearStencilClip(const GrScissorState & scissor,bool insideStencilMask)324 void GrD3DOpsRenderPass::onClearStencilClip(const GrScissorState& scissor, bool insideStencilMask) {
325     GrAttachment* sb = fRenderTarget->getStencilAttachment();
326     // this should only be called internally when we know we have a
327     // stencil buffer.
328     SkASSERT(sb);
329     int stencilBitCount = GrBackendFormatStencilBits(sb->backendFormat());
330 
331     // The contract with the callers does not guarantee that we preserve all bits in the stencil
332     // during this clear. Thus we will clear the entire stencil to the desired value.
333 
334     uint8_t stencilColor = 0;
335     if (insideStencilMask) {
336         stencilColor = (1 << (stencilBitCount - 1));
337     }
338 
339     D3D12_RECT clearRect = scissor_to_d3d_clear_rect(scissor, fRenderTarget, fOrigin);
340 
341     auto d3dStencil = static_cast<GrD3DAttachment*>(sb);
342     fGpu->currentCommandList()->clearDepthStencilView(d3dStencil, stencilColor, &clearRect);
343 }
344 
inlineUpload(GrOpFlushState * state,GrDeferredTextureUploadFn & upload)345 void GrD3DOpsRenderPass::inlineUpload(GrOpFlushState* state, GrDeferredTextureUploadFn& upload) {
346     // If we ever start using copy command lists for doing uploads, then we'll need to make sure
347     // we submit our main command list before doing the copy here and then start a new main command
348     // list.
349     state->doUpload(upload);
350 }
351