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