1/*
2 * Copyright 2019 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 "src/gpu/mtl/GrMtlCommandBuffer.h"
9
10#include "src/gpu/mtl/GrMtlGpu.h"
11#include "src/gpu/mtl/GrMtlOpsRenderPass.h"
12#include "src/gpu/mtl/GrMtlPipelineState.h"
13
14#if !__has_feature(objc_arc)
15#error This file must be compiled with Arc. Use -fobjc-arc flag
16#endif
17
18GR_NORETAIN_BEGIN
19
20sk_sp<GrMtlCommandBuffer> GrMtlCommandBuffer::Make(id<MTLCommandQueue> queue) {
21    id<MTLCommandBuffer> mtlCommandBuffer;
22    mtlCommandBuffer = [queue commandBuffer];
23    if (nil == mtlCommandBuffer) {
24        return nullptr;
25    }
26
27    mtlCommandBuffer.label = @"GrMtlCommandBuffer::Create";
28
29    return sk_sp<GrMtlCommandBuffer>(new GrMtlCommandBuffer(mtlCommandBuffer));
30}
31
32GrMtlCommandBuffer::~GrMtlCommandBuffer() {
33    this->endAllEncoding();
34    fTrackedGrBuffers.reset();
35    this->callFinishedCallbacks();
36
37    fCmdBuffer = nil;
38}
39
40id<MTLBlitCommandEncoder> GrMtlCommandBuffer::getBlitCommandEncoder() {
41    if (fActiveBlitCommandEncoder) {
42        return fActiveBlitCommandEncoder;
43    }
44
45    this->endAllEncoding();
46    fActiveBlitCommandEncoder = [fCmdBuffer blitCommandEncoder];
47    fHasWork = true;
48
49    return fActiveBlitCommandEncoder;
50}
51
52static bool compatible(const MTLRenderPassAttachmentDescriptor* first,
53                       const MTLRenderPassAttachmentDescriptor* second,
54                       const GrMtlPipelineState* pipelineState) {
55    // Check to see if the previous descriptor is compatible with the new one.
56    // They are compatible if:
57    // * they share the same rendertargets
58    // * the first's store actions are either Store or DontCare
59    // * the second's load actions are either Load or DontCare
60    // * the second doesn't sample from any rendertargets in the first
61    bool renderTargetsMatch = (first.texture == second.texture);
62    bool storeActionsValid = first.storeAction == MTLStoreActionStore ||
63                             first.storeAction == MTLStoreActionDontCare;
64    bool loadActionsValid = second.loadAction == MTLLoadActionLoad ||
65                            second.loadAction == MTLLoadActionDontCare;
66    bool secondDoesntSampleFirst = (!pipelineState ||
67                                    pipelineState->doesntSampleAttachment(first)) &&
68                                   second.storeAction != MTLStoreActionMultisampleResolve;
69
70    return renderTargetsMatch &&
71           (nil == first.texture ||
72            (storeActionsValid && loadActionsValid && secondDoesntSampleFirst));
73}
74
75id<MTLRenderCommandEncoder> GrMtlCommandBuffer::getRenderCommandEncoder(
76        MTLRenderPassDescriptor* descriptor, const GrMtlPipelineState* pipelineState,
77        GrMtlOpsRenderPass* opsRenderPass) {
78    if (nil != fPreviousRenderPassDescriptor) {
79        if (compatible(fPreviousRenderPassDescriptor.colorAttachments[0],
80                       descriptor.colorAttachments[0], pipelineState) &&
81            compatible(fPreviousRenderPassDescriptor.stencilAttachment,
82                       descriptor.stencilAttachment, pipelineState)) {
83            return fActiveRenderCommandEncoder;
84        }
85    }
86
87    this->endAllEncoding();
88    fActiveRenderCommandEncoder = [fCmdBuffer renderCommandEncoderWithDescriptor:descriptor];
89    if (opsRenderPass) {
90        opsRenderPass->initRenderState(fActiveRenderCommandEncoder);
91    }
92    fPreviousRenderPassDescriptor = descriptor;
93    fHasWork = true;
94
95    return fActiveRenderCommandEncoder;
96}
97
98bool GrMtlCommandBuffer::commit(bool waitUntilCompleted) {
99    this->endAllEncoding();
100    [fCmdBuffer commit];
101    if (waitUntilCompleted) {
102        this->waitUntilCompleted();
103    }
104
105    if (fCmdBuffer.status == MTLCommandBufferStatusError) {
106        NSString* description = fCmdBuffer.error.localizedDescription;
107        const char* errorString = [description UTF8String];
108        SkDebugf("Error submitting command buffer: %s\n", errorString);
109    }
110
111    return (fCmdBuffer.status != MTLCommandBufferStatusError);
112}
113
114void GrMtlCommandBuffer::endAllEncoding() {
115    if (fActiveRenderCommandEncoder) {
116        [fActiveRenderCommandEncoder endEncoding];
117        fActiveRenderCommandEncoder = nil;
118        fPreviousRenderPassDescriptor = nil;
119    }
120    if (fActiveBlitCommandEncoder) {
121        [fActiveBlitCommandEncoder endEncoding];
122        fActiveBlitCommandEncoder = nil;
123    }
124}
125
126void GrMtlCommandBuffer::encodeSignalEvent(id<MTLEvent> event, uint64_t eventValue) {
127    SkASSERT(fCmdBuffer);
128    this->endAllEncoding(); // ensure we don't have any active command encoders
129    if (@available(macOS 10.14, iOS 12.0, *)) {
130        [fCmdBuffer encodeSignalEvent:event value:eventValue];
131    }
132    fHasWork = true;
133}
134
135void GrMtlCommandBuffer::encodeWaitForEvent(id<MTLEvent> event, uint64_t eventValue) {
136    SkASSERT(fCmdBuffer);
137    this->endAllEncoding(); // ensure we don't have any active command encoders
138                            // TODO: not sure if needed but probably
139    if (@available(macOS 10.14, iOS 12.0, *)) {
140        [fCmdBuffer encodeWaitForEvent:event value:eventValue];
141    }
142    fHasWork = true;
143}
144
145GR_NORETAIN_END
146