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 29GrD3DOpsRenderPass::GrD3DOpsRenderPass(GrD3DGpu* gpu) : fGpu(gpu) {} 30 31bool 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 50GrD3DOpsRenderPass::~GrD3DOpsRenderPass() {} 51 52GrGpu* GrD3DOpsRenderPass::gpu() { return fGpu; } 53 54void 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 78void 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 93void 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 113void 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 139void 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 160void 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 171bool 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 202void 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 211void 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 218bool 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 242void 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 257void 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 264void 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 272void 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 281void 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 292static 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 313void 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 320void 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 341void 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 353void 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