1/* 2 * Copyright 2018 Google Inc. 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 "GrMtlGpuCommandBuffer.h" 9 10#include "GrColor.h" 11#include "GrFixedClip.h" 12#include "GrMtlPipelineState.h" 13#include "GrMtlPipelineStateBuilder.h" 14#include "GrMtlRenderTarget.h" 15#include "GrRenderTargetPriv.h" 16 17GrMtlGpuRTCommandBuffer::GrMtlGpuRTCommandBuffer( 18 GrMtlGpu* gpu, GrRenderTarget* rt, GrSurfaceOrigin origin, const SkRect& bounds, 19 const GrGpuRTCommandBuffer::LoadAndStoreInfo& colorInfo, 20 const GrGpuRTCommandBuffer::StencilLoadAndStoreInfo& stencilInfo) 21 : INHERITED(rt, origin) 22 , fGpu(gpu) 23#ifdef SK_DEBUG 24 , fBounds(bounds) 25#endif 26 , fColorLoadAndStoreInfo(colorInfo) 27 , fStencilLoadAndStoreInfo(stencilInfo) 28 , fRenderPassDesc(this->createRenderPassDesc()) { 29 (void)fStencilLoadAndStoreInfo; // Silence unused var warning 30 const GrMtlStencilAttachment* stencil = static_cast<GrMtlStencilAttachment*>( 31 rt->renderTargetPriv().getStencilAttachment()); 32 if (stencil) { 33 fRenderPassDesc.stencilAttachment.texture = stencil->stencilView(); 34 } 35 if (fColorLoadAndStoreInfo.fLoadOp == GrLoadOp::kClear) { 36 fCommandBufferInfo.fBounds = SkRect::MakeWH(fRenderTarget->width(), 37 fRenderTarget->height()); 38 this->internalBegin(); 39 this->internalEnd(); 40 fRenderPassDesc.colorAttachments[0].loadAction = MTLLoadActionLoad; 41 } else { 42 fCommandBufferInfo.fBounds.setEmpty(); 43 } 44 switch (stencilInfo.fLoadOp) { 45 case GrLoadOp::kLoad: 46 fRenderPassDesc.stencilAttachment.loadAction = MTLLoadActionLoad; 47 break; 48 case GrLoadOp::kClear: 49 fCommandBufferInfo.fBounds = SkRect::MakeWH(fRenderTarget->width(), 50 fRenderTarget->height()); 51 fRenderPassDesc.stencilAttachment.loadAction = MTLLoadActionClear; 52 this->internalBegin(); 53 this->internalEnd(); 54 fRenderPassDesc.stencilAttachment.loadAction = MTLLoadActionLoad; 55 break; 56 case GrLoadOp::kDiscard: 57 fRenderPassDesc.stencilAttachment.loadAction = MTLLoadActionDontCare; 58 break; 59 } 60 switch (stencilInfo.fStoreOp) { 61 case GrStoreOp::kStore: 62 fRenderPassDesc.stencilAttachment.storeAction = MTLStoreActionStore; 63 break; 64 case GrStoreOp::kDiscard: 65 fRenderPassDesc.stencilAttachment.storeAction = MTLStoreActionDontCare; 66 break; 67 } 68} 69 70GrMtlGpuRTCommandBuffer::~GrMtlGpuRTCommandBuffer() { 71 SkASSERT(fActiveRenderCmdEncoder == nil); 72} 73 74void GrMtlGpuRTCommandBuffer::internalBegin() { 75 SkASSERT(fActiveRenderCmdEncoder == nil); 76 fActiveRenderCmdEncoder = 77 [fGpu->commandBuffer() 78 renderCommandEncoderWithDescriptor: fRenderPassDesc]; 79 SkASSERT(fActiveRenderCmdEncoder); 80 [fActiveRenderCmdEncoder setFrontFacingWinding: MTLWindingCounterClockwise]; 81} 82 83void GrMtlGpuRTCommandBuffer::internalEnd() { 84 SkASSERT(fActiveRenderCmdEncoder); 85 [fActiveRenderCmdEncoder endEncoding]; 86 fActiveRenderCmdEncoder = nil; 87 SkASSERT(fActiveRenderCmdEncoder == nil); 88} 89 90void GrMtlGpuRTCommandBuffer::submit() { 91 if (!fRenderTarget) { 92 return; 93 } 94 SkIRect iBounds; 95 fCommandBufferInfo.fBounds.roundOut(&iBounds); 96 fGpu->submitIndirectCommandBuffer(fRenderTarget, fOrigin, &iBounds); 97} 98 99void GrMtlGpuRTCommandBuffer::copy(GrSurface* src, GrSurfaceOrigin srcOrigin, 100 const SkIRect& srcRect, const SkIPoint& dstPoint) { 101 // We cannot have an active encoder when we call copy since it requires its own 102 // command encoder. 103 SkASSERT(fActiveRenderCmdEncoder == nil); 104 fGpu->copySurface(fRenderTarget, fOrigin, src, srcOrigin, srcRect, dstPoint); 105} 106 107GrMtlPipelineState* GrMtlGpuRTCommandBuffer::prepareDrawState( 108 const GrPrimitiveProcessor& primProc, 109 const GrPipeline& pipeline, 110 const GrPipeline::FixedDynamicState* fixedDynamicState, 111 const GrMesh meshes[], 112 int meshCount) { 113 // TODO: resolve textures and regenerate mipmaps as needed 114 bool hasPoints = false; 115 for (int i = 0; i < meshCount; ++i) { 116 if (meshes[i].primitiveType() == GrPrimitiveType::kPoints) { 117 hasPoints = true; 118 break; 119 } 120 } 121 GrProgramDesc desc; 122 if (!GrProgramDesc::Build(&desc, fRenderTarget->config(), primProc, hasPoints, 123 pipeline, fGpu)) { 124 return nullptr; 125 } 126 127 const GrTextureProxy* const* primProcProxies = nullptr; 128 if (fixedDynamicState) { 129 primProcProxies = fixedDynamicState->fPrimitiveProcessorTextures; 130 } 131 SkASSERT(SkToBool(primProcProxies) == SkToBool(primProc.numTextureSamplers())); 132 133 // TODO: use resource provider for pipeline 134 GrMtlPipelineState* pipelineState = 135 GrMtlPipelineStateBuilder::CreatePipelineState(fRenderTarget, fOrigin, primProc, 136 primProcProxies, pipeline, 137 &desc, fGpu); 138 if (!pipelineState) { 139 return nullptr; 140 } 141 // We cannot have an active encoder when we set the pipeline data since it requires its own 142 // command encoder. 143 SkASSERT(fActiveRenderCmdEncoder == nil); 144 pipelineState->setData(fRenderTarget, fOrigin, primProc, pipeline, primProcProxies); 145 146 return pipelineState; 147} 148 149void GrMtlGpuRTCommandBuffer::onDraw(const GrPrimitiveProcessor& primProc, 150 const GrPipeline& pipeline, 151 const GrPipeline::FixedDynamicState* fixedDynamicState, 152 const GrPipeline::DynamicStateArrays* dynamicStateArrays, 153 const GrMesh meshes[], 154 int meshCount, 155 const SkRect& bounds) { 156 if (!meshCount) { 157 return; 158 } 159 if (pipeline.isScissorEnabled()) { 160 return; // TODO: ScissorRects are not supported. 161 } 162 163 std::unique_ptr<GrMtlPipelineState> pipelineState( 164 this->prepareDrawState(primProc, pipeline, fixedDynamicState, meshes, meshCount)); 165 if (!pipelineState) { 166 return; 167 } 168 169 this->internalBegin(); 170 [fActiveRenderCmdEncoder setRenderPipelineState: pipelineState->mtlPipelineState()]; 171 172 pipelineState->bind(fActiveRenderCmdEncoder); 173 pipelineState->setBlendConstants(fActiveRenderCmdEncoder, fRenderTarget->config(), 174 pipeline.getXferProcessor()); 175 pipelineState->setDepthStencilState(fActiveRenderCmdEncoder); 176 177 for (int i = 0; i < meshCount; ++i) { 178 const GrMesh& mesh = meshes[i]; 179 SkASSERT(fActiveRenderCmdEncoder); 180 mesh.sendToGpu(this); 181 } 182 this->internalEnd(); 183 fCommandBufferInfo.fBounds.join(bounds); 184} 185 186void GrMtlGpuRTCommandBuffer::onClear(const GrFixedClip& clip, const SkPMColor4f& color) { 187 // if we end up here from absClear, the clear bounds may be bigger than the RT proxy bounds - 188 // but in that case, scissor should be enabled, so this check should still succeed 189 SkASSERT(!clip.scissorEnabled() || clip.scissorRect().contains(fBounds)); 190 fRenderPassDesc.colorAttachments[0].clearColor = MTLClearColorMake(color.fR, color.fG, color.fB, 191 color.fA); 192 fRenderPassDesc.colorAttachments[0].loadAction = MTLLoadActionClear; 193 this->internalBegin(); 194 this->internalEnd(); 195 fRenderPassDesc.colorAttachments[0].loadAction = MTLLoadActionLoad; 196} 197 198void GrMtlGpuRTCommandBuffer::onClearStencilClip(const GrFixedClip& clip, bool insideStencilMask) { 199 SkASSERT(!clip.hasWindowRectangles()); 200 201 GrStencilAttachment* sb = fRenderTarget->renderTargetPriv().getStencilAttachment(); 202 // this should only be called internally when we know we have a 203 // stencil buffer. 204 SkASSERT(sb); 205 int stencilBitCount = sb->bits(); 206 207 // The contract with the callers does not guarantee that we preserve all bits in the stencil 208 // during this clear. Thus we will clear the entire stencil to the desired value. 209 if (insideStencilMask) { 210 fRenderPassDesc.stencilAttachment.clearStencil = (1 << (stencilBitCount - 1)); 211 } else { 212 fRenderPassDesc.stencilAttachment.clearStencil = 0; 213 } 214 215 fRenderPassDesc.stencilAttachment.loadAction = MTLLoadActionClear; 216 this->internalBegin(); 217 this->internalEnd(); 218 fRenderPassDesc.stencilAttachment.loadAction = MTLLoadActionLoad; 219} 220 221MTLRenderPassDescriptor* GrMtlGpuRTCommandBuffer::createRenderPassDesc() const { 222 const static MTLLoadAction mtlLoadAction[] { 223 MTLLoadActionLoad, 224 MTLLoadActionClear, 225 MTLLoadActionDontCare 226 }; 227 GR_STATIC_ASSERT((int)GrLoadOp::kLoad == 0); 228 GR_STATIC_ASSERT((int)GrLoadOp::kClear == 1); 229 GR_STATIC_ASSERT((int)GrLoadOp::kDiscard == 2); 230 SkASSERT(fColorLoadAndStoreInfo.fLoadOp <= GrLoadOp::kDiscard); 231 232 const static MTLStoreAction mtlStoreAction[] { 233 MTLStoreActionStore, 234 MTLStoreActionDontCare 235 }; 236 GR_STATIC_ASSERT((int)GrStoreOp::kStore == 0); 237 GR_STATIC_ASSERT((int)GrStoreOp::kDiscard == 1); 238 SkASSERT(fColorLoadAndStoreInfo.fStoreOp <= GrStoreOp::kDiscard); 239 240 auto renderPassDesc = [[MTLRenderPassDescriptor alloc] init]; 241 renderPassDesc.colorAttachments[0].texture = 242 static_cast<GrMtlRenderTarget*>(fRenderTarget)->mtlRenderTexture(); 243 renderPassDesc.colorAttachments[0].slice = 0; 244 renderPassDesc.colorAttachments[0].level = 0; 245 const SkPMColor4f& clearColor = fColorLoadAndStoreInfo.fClearColor; 246 renderPassDesc.colorAttachments[0].clearColor = 247 MTLClearColorMake(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); 248 renderPassDesc.colorAttachments[0].loadAction = 249 mtlLoadAction[static_cast<int>(fColorLoadAndStoreInfo.fLoadOp)]; 250 renderPassDesc.colorAttachments[0].storeAction = 251 mtlStoreAction[static_cast<int>(fColorLoadAndStoreInfo.fStoreOp)]; 252 return renderPassDesc; 253} 254 255static MTLPrimitiveType gr_to_mtl_primitive(GrPrimitiveType primitiveType) { 256 const static MTLPrimitiveType mtlPrimitiveType[] { 257 MTLPrimitiveTypeTriangle, 258 MTLPrimitiveTypeTriangleStrip, 259 MTLPrimitiveTypePoint, 260 MTLPrimitiveTypeLine, 261 MTLPrimitiveTypeLineStrip 262 }; 263 GR_STATIC_ASSERT((int)GrPrimitiveType::kTriangles == 0); 264 GR_STATIC_ASSERT((int)GrPrimitiveType::kTriangleStrip == 1); 265 GR_STATIC_ASSERT((int)GrPrimitiveType::kPoints == 2); 266 GR_STATIC_ASSERT((int)GrPrimitiveType::kLines == 3); 267 GR_STATIC_ASSERT((int)GrPrimitiveType::kLineStrip == 4); 268 269 SkASSERT(primitiveType <= GrPrimitiveType::kLineStrip); 270 return mtlPrimitiveType[static_cast<int>(primitiveType)]; 271} 272 273void GrMtlGpuRTCommandBuffer::bindGeometry(const GrBuffer* vertexBuffer, 274 const GrBuffer* instanceBuffer) { 275 size_t bufferIndex = GrMtlUniformHandler::kLastUniformBinding + 1; 276 if (vertexBuffer) { 277 SkASSERT(!vertexBuffer->isCPUBacked()); 278 SkASSERT(!vertexBuffer->isMapped()); 279 280 auto mtlVertexBuffer = static_cast<const GrMtlBuffer*>(vertexBuffer)->mtlBuffer(); 281 SkASSERT(mtlVertexBuffer); 282 [fActiveRenderCmdEncoder setVertexBuffer: mtlVertexBuffer 283 offset: 0 284 atIndex: bufferIndex++]; 285 } 286 if (instanceBuffer) { 287 SkASSERT(!instanceBuffer->isCPUBacked()); 288 SkASSERT(!instanceBuffer->isMapped()); 289 290 auto mtlInstanceBuffer = static_cast<const GrMtlBuffer*>(instanceBuffer)->mtlBuffer(); 291 SkASSERT(mtlInstanceBuffer); 292 [fActiveRenderCmdEncoder setVertexBuffer: mtlInstanceBuffer 293 offset: 0 294 atIndex: bufferIndex++]; 295 } 296} 297 298void GrMtlGpuRTCommandBuffer::sendInstancedMeshToGpu(GrPrimitiveType primitiveType, 299 const GrBuffer* vertexBuffer, 300 int vertexCount, 301 int baseVertex, 302 const GrBuffer* instanceBuffer, 303 int instanceCount, 304 int baseInstance) { 305 this->bindGeometry(vertexBuffer, instanceBuffer); 306 307 SkASSERT(primitiveType != GrPrimitiveType::kLinesAdjacency); // Geometry shaders not supported. 308 [fActiveRenderCmdEncoder drawPrimitives: gr_to_mtl_primitive(primitiveType) 309 vertexStart: baseVertex 310 vertexCount: vertexCount 311 instanceCount: instanceCount 312 baseInstance: baseInstance]; 313} 314 315void GrMtlGpuRTCommandBuffer::sendIndexedInstancedMeshToGpu(GrPrimitiveType primitiveType, 316 const GrBuffer* indexBuffer, 317 int indexCount, 318 int baseIndex, 319 const GrBuffer* vertexBuffer, 320 int baseVertex, 321 const GrBuffer* instanceBuffer, 322 int instanceCount, 323 int baseInstance, 324 GrPrimitiveRestart restart) { 325 this->bindGeometry(vertexBuffer, instanceBuffer); 326 327 SkASSERT(primitiveType != GrPrimitiveType::kLinesAdjacency); // Geometry shaders not supported. 328 id<MTLBuffer> mtlIndexBuffer; 329 if (indexBuffer) { 330 SkASSERT(!indexBuffer->isCPUBacked()); 331 SkASSERT(!indexBuffer->isMapped()); 332 333 mtlIndexBuffer = static_cast<const GrMtlBuffer*>(indexBuffer)->mtlBuffer(); 334 SkASSERT(mtlIndexBuffer); 335 } 336 337 SkASSERT(restart == GrPrimitiveRestart::kNo); 338 [fActiveRenderCmdEncoder drawIndexedPrimitives: gr_to_mtl_primitive(primitiveType) 339 indexCount: indexCount 340 indexType: MTLIndexTypeUInt16 341 indexBuffer: mtlIndexBuffer 342 indexBufferOffset: sizeof(uint16_t) * baseIndex 343 instanceCount: instanceCount 344 baseVertex: baseVertex 345 baseInstance: baseInstance]; 346} 347