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