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