• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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