Timothy Liang | 5422f9a | 2018-08-10 10:57:55 -0400 | [diff] [blame] | 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" |
Ethan Nicholas | 0106351 | 2018-10-08 16:58:25 -0400 | [diff] [blame] | 11 | #include "GrFixedClip.h" |
Timothy Liang | 5422f9a | 2018-08-10 10:57:55 -0400 | [diff] [blame] | 12 | #include "GrMtlPipelineState.h" |
| 13 | #include "GrMtlPipelineStateBuilder.h" |
| 14 | #include "GrMtlRenderTarget.h" |
Ethan Nicholas | 0106351 | 2018-10-08 16:58:25 -0400 | [diff] [blame] | 15 | #include "GrRenderTargetPriv.h" |
Timothy Liang | 5422f9a | 2018-08-10 10:57:55 -0400 | [diff] [blame] | 16 | |
| 17 | GrMtlGpuRTCommandBuffer::GrMtlGpuRTCommandBuffer( |
Ethan Nicholas | 56d19a5 | 2018-10-15 11:26:20 -0400 | [diff] [blame] | 18 | GrMtlGpu* gpu, GrRenderTarget* rt, GrSurfaceOrigin origin, const SkRect& bounds, |
Timothy Liang | 5422f9a | 2018-08-10 10:57:55 -0400 | [diff] [blame] | 19 | const GrGpuRTCommandBuffer::LoadAndStoreInfo& colorInfo, |
| 20 | const GrGpuRTCommandBuffer::StencilLoadAndStoreInfo& stencilInfo) |
| 21 | : INHERITED(rt, origin) |
| 22 | , fGpu(gpu) |
Ethan Nicholas | 391d344 | 2018-10-15 13:00:59 -0400 | [diff] [blame] | 23 | #ifdef SK_DEBUG |
Ethan Nicholas | 56d19a5 | 2018-10-15 11:26:20 -0400 | [diff] [blame] | 24 | , fBounds(bounds) |
Ethan Nicholas | 391d344 | 2018-10-15 13:00:59 -0400 | [diff] [blame] | 25 | #endif |
Timothy Liang | 5422f9a | 2018-08-10 10:57:55 -0400 | [diff] [blame] | 26 | , fColorLoadAndStoreInfo(colorInfo) |
| 27 | , fStencilLoadAndStoreInfo(stencilInfo) |
| 28 | , fRenderPassDesc(this->createRenderPassDesc()) { |
| 29 | (void)fStencilLoadAndStoreInfo; // Silence unused var warning |
Ethan Nicholas | 0106351 | 2018-10-08 16:58:25 -0400 | [diff] [blame] | 30 | const GrMtlStencilAttachment* stencil = static_cast<GrMtlStencilAttachment*>( |
| 31 | rt->renderTargetPriv().getStencilAttachment()); |
| 32 | if (stencil) { |
| 33 | fRenderPassDesc.stencilAttachment.texture = stencil->stencilView(); |
| 34 | } |
Timothy Liang | 5422f9a | 2018-08-10 10:57:55 -0400 | [diff] [blame] | 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 | } |
Ethan Nicholas | 0106351 | 2018-10-08 16:58:25 -0400 | [diff] [blame] | 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 | } |
Timothy Liang | 5422f9a | 2018-08-10 10:57:55 -0400 | [diff] [blame] | 68 | } |
| 69 | |
| 70 | GrMtlGpuRTCommandBuffer::~GrMtlGpuRTCommandBuffer() { |
| 71 | SkASSERT(fActiveRenderCmdEncoder == nil); |
| 72 | } |
| 73 | |
| 74 | void GrMtlGpuRTCommandBuffer::internalBegin() { |
| 75 | SkASSERT(fActiveRenderCmdEncoder == nil); |
| 76 | fActiveRenderCmdEncoder = |
| 77 | [fGpu->commandBuffer() |
| 78 | renderCommandEncoderWithDescriptor: fRenderPassDesc]; |
| 79 | SkASSERT(fActiveRenderCmdEncoder); |
| 80 | [fActiveRenderCmdEncoder setFrontFacingWinding: MTLWindingCounterClockwise]; |
| 81 | } |
| 82 | |
| 83 | void GrMtlGpuRTCommandBuffer::internalEnd() { |
| 84 | SkASSERT(fActiveRenderCmdEncoder); |
| 85 | [fActiveRenderCmdEncoder endEncoding]; |
| 86 | fActiveRenderCmdEncoder = nil; |
| 87 | SkASSERT(fActiveRenderCmdEncoder == nil); |
| 88 | } |
| 89 | |
| 90 | void GrMtlGpuRTCommandBuffer::submit() { |
| 91 | if (!fRenderTarget) { |
| 92 | return; |
| 93 | } |
| 94 | SkIRect iBounds; |
| 95 | fCommandBufferInfo.fBounds.roundOut(&iBounds); |
| 96 | fGpu->submitIndirectCommandBuffer(fRenderTarget, fOrigin, &iBounds); |
| 97 | } |
| 98 | |
| 99 | void 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 | |
| 107 | GrMtlPipelineState* 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; |
Greg Daniel | 7a82edf | 2018-12-04 10:54:34 -0500 | [diff] [blame^] | 122 | if (!GrProgramDesc::Build(&desc, primProc, hasPoints, pipeline, fGpu)) { |
Timothy Liang | 5422f9a | 2018-08-10 10:57:55 -0400 | [diff] [blame] | 123 | return nullptr; |
| 124 | } |
| 125 | desc.finalize(); |
| 126 | |
Timothy Liang | 5422f9a | 2018-08-10 10:57:55 -0400 | [diff] [blame] | 127 | const GrTextureProxy* const* primProcProxies = nullptr; |
| 128 | if (fixedDynamicState) { |
| 129 | primProcProxies = fixedDynamicState->fPrimitiveProcessorTextures; |
| 130 | } |
Greg Daniel | 9a51a86 | 2018-11-30 10:18:14 -0500 | [diff] [blame] | 131 | SkASSERT(SkToBool(primProcProxies) == SkToBool(primProc.numTextureSamplers())); |
Timothy Liang | 5422f9a | 2018-08-10 10:57:55 -0400 | [diff] [blame] | 132 | |
Greg Daniel | 9a51a86 | 2018-11-30 10:18:14 -0500 | [diff] [blame] | 133 | // TODO: use resource provider for pipeline |
| 134 | GrMtlPipelineState* pipelineState = |
| 135 | GrMtlPipelineStateBuilder::CreatePipelineState(primProc, primProcProxies, pipeline, |
| 136 | &desc, fGpu); |
| 137 | if (!pipelineState) { |
| 138 | return nullptr; |
| 139 | } |
Timothy Liang | 5422f9a | 2018-08-10 10:57:55 -0400 | [diff] [blame] | 140 | // We cannot have an active encoder when we set the pipeline data since it requires its own |
| 141 | // command encoder. |
| 142 | SkASSERT(fActiveRenderCmdEncoder == nil); |
| 143 | pipelineState->setData(primProc, pipeline, primProcProxies); |
| 144 | |
| 145 | return pipelineState; |
| 146 | } |
| 147 | |
| 148 | void GrMtlGpuRTCommandBuffer::onDraw(const GrPrimitiveProcessor& primProc, |
| 149 | const GrPipeline& pipeline, |
| 150 | const GrPipeline::FixedDynamicState* fixedDynamicState, |
| 151 | const GrPipeline::DynamicStateArrays* dynamicStateArrays, |
| 152 | const GrMesh meshes[], |
| 153 | int meshCount, |
| 154 | const SkRect& bounds) { |
| 155 | SkASSERT(pipeline.renderTarget() == fRenderTarget); |
| 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); |
Timothy Liang | de0be80 | 2018-08-10 13:48:08 -0400 | [diff] [blame] | 173 | pipelineState->setBlendConstants(fActiveRenderCmdEncoder, fRenderTarget->config(), |
| 174 | pipeline.getXferProcessor()); |
Ethan Nicholas | 0106351 | 2018-10-08 16:58:25 -0400 | [diff] [blame] | 175 | pipelineState->setDepthStencilState(fActiveRenderCmdEncoder); |
Timothy Liang | 5422f9a | 2018-08-10 10:57:55 -0400 | [diff] [blame] | 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 | |
Brian Osman | 9a9baae | 2018-11-05 15:06:26 -0500 | [diff] [blame] | 186 | void GrMtlGpuRTCommandBuffer::onClear(const GrFixedClip& clip, const SkPMColor4f& color) { |
Ethan Nicholas | 56d19a5 | 2018-10-15 11:26:20 -0400 | [diff] [blame] | 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)); |
Brian Osman | 9a9baae | 2018-11-05 15:06:26 -0500 | [diff] [blame] | 190 | fRenderPassDesc.colorAttachments[0].clearColor = MTLClearColorMake(color.fR, color.fG, color.fB, |
| 191 | color.fA); |
Ethan Nicholas | 56d19a5 | 2018-10-15 11:26:20 -0400 | [diff] [blame] | 192 | fRenderPassDesc.colorAttachments[0].loadAction = MTLLoadActionClear; |
| 193 | this->internalBegin(); |
| 194 | this->internalEnd(); |
| 195 | fRenderPassDesc.colorAttachments[0].loadAction = MTLLoadActionLoad; |
| 196 | } |
| 197 | |
Ethan Nicholas | 0106351 | 2018-10-08 16:58:25 -0400 | [diff] [blame] | 198 | void 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 | |
Timothy Liang | 5422f9a | 2018-08-10 10:57:55 -0400 | [diff] [blame] | 221 | MTLRenderPassDescriptor* 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; |
Brian Osman | 9a9baae | 2018-11-05 15:06:26 -0500 | [diff] [blame] | 245 | const SkPMColor4f& clearColor = fColorLoadAndStoreInfo.fClearColor; |
Timothy Liang | 5422f9a | 2018-08-10 10:57:55 -0400 | [diff] [blame] | 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 | |
| 255 | static 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 | |
| 273 | void 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 | |
| 298 | void 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 | |
| 315 | void 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 | } |