• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/core/SkTraceEvent.h"
11#include "src/gpu/mtl/GrMtlGpu.h"
12#include "src/gpu/mtl/GrMtlOpsRenderPass.h"
13#include "src/gpu/mtl/GrMtlPipelineState.h"
14#include "src/gpu/mtl/GrMtlRenderCommandEncoder.h"
15#include "src/gpu/mtl/GrMtlSemaphore.h"
16
17#if !__has_feature(objc_arc)
18#error This file must be compiled with Arc. Use -fobjc-arc flag
19#endif
20
21GR_NORETAIN_BEGIN
22
23sk_sp<GrMtlCommandBuffer> GrMtlCommandBuffer::Make(id<MTLCommandQueue> queue) {
24    id<MTLCommandBuffer> mtlCommandBuffer;
25    mtlCommandBuffer = [queue commandBuffer];
26    if (nil == mtlCommandBuffer) {
27        return nullptr;
28    }
29
30#ifdef SK_ENABLE_MTL_DEBUG_INFO
31    mtlCommandBuffer.label = @"GrMtlCommandBuffer::Make";
32#endif
33
34    return sk_sp<GrMtlCommandBuffer>(new GrMtlCommandBuffer(mtlCommandBuffer));
35}
36
37GrMtlCommandBuffer::~GrMtlCommandBuffer() {
38    this->endAllEncoding();
39    this->releaseResources();
40    this->callFinishedCallbacks();
41
42    fCmdBuffer = nil;
43}
44
45void GrMtlCommandBuffer::releaseResources() {
46    TRACE_EVENT0("skia.gpu", TRACE_FUNC);
47
48    fTrackedResources.reset();
49    fTrackedGrBuffers.reset();
50    fTrackedGrSurfaces.reset();
51}
52
53id<MTLBlitCommandEncoder> GrMtlCommandBuffer::getBlitCommandEncoder() {
54    if (fActiveBlitCommandEncoder) {
55        return fActiveBlitCommandEncoder;
56    }
57
58    this->endAllEncoding();
59#ifdef SK_BUILD_FOR_IOS
60    if (GrMtlIsAppInBackground()) {
61        fActiveBlitCommandEncoder = nil;
62        NSLog(@"GrMtlCommandBuffer: tried to create MTLBlitCommandEncoder while in background.");
63        return nil;
64    }
65#endif
66    fActiveBlitCommandEncoder = [fCmdBuffer blitCommandEncoder];
67    fHasWork = true;
68
69    return fActiveBlitCommandEncoder;
70}
71
72static bool compatible(const MTLRenderPassAttachmentDescriptor* first,
73                       const MTLRenderPassAttachmentDescriptor* second,
74                       const GrMtlPipelineState* pipelineState) {
75    // From the Metal Best Practices Guide:
76    // Check to see if the previous descriptor is compatible with the new one.
77    // They are compatible if:
78    // * they share the same rendertargets
79    // * the first's store actions are either Store or DontCare
80    // * the second's load actions are either Load or DontCare
81    // * the second doesn't sample from any rendertargets in the first
82    bool renderTargetsMatch = (first.texture == second.texture);
83    bool storeActionsValid = first.storeAction == MTLStoreActionStore ||
84                             first.storeAction == MTLStoreActionDontCare;
85    bool loadActionsValid = second.loadAction == MTLLoadActionLoad ||
86                            second.loadAction == MTLLoadActionDontCare;
87    bool secondDoesntSampleFirst = (!pipelineState ||
88                                    pipelineState->doesntSampleAttachment(first));
89
90    // Since we are trying to use the same encoder rather than merging two,
91    // we have to check to see if both store actions are mutually compatible.
92    bool secondStoreValid = true;
93    if (second.storeAction == MTLStoreActionDontCare) {
94        secondStoreValid = (first.storeAction == MTLStoreActionDontCare);
95        // TODO: if first.storeAction is Store and second.loadAction is Load,
96        // we could reset the active RenderCommandEncoder's store action to DontCare
97    } else if (second.storeAction == MTLStoreActionStore) {
98        if (@available(macOS 10.12, iOS 10.0, tvOS 10.0, *)) {
99            secondStoreValid = (first.storeAction == MTLStoreActionStore ||
100                                first.storeAction == MTLStoreActionStoreAndMultisampleResolve);
101        } else {
102            secondStoreValid = (first.storeAction == MTLStoreActionStore);
103        }
104        // TODO: if the first store action is DontCare we could reset the active
105        // RenderCommandEncoder's store action to Store, but it's not clear if it's worth it.
106    } else if (second.storeAction == MTLStoreActionMultisampleResolve) {
107        if (@available(macOS 10.12, iOS 10.0, tvOS 10.0, *)) {
108            secondStoreValid = (first.resolveTexture == second.resolveTexture) &&
109                               (first.storeAction == MTLStoreActionMultisampleResolve ||
110                                first.storeAction == MTLStoreActionStoreAndMultisampleResolve);
111        } else {
112            secondStoreValid = (first.resolveTexture == second.resolveTexture) &&
113                               (first.storeAction == MTLStoreActionMultisampleResolve);
114        }
115        // When we first check whether store actions are valid we don't consider resolves,
116        // so we need to reset that here.
117        storeActionsValid = secondStoreValid;
118    } else {
119        if (@available(macOS 10.12, iOS 10.0, tvOS 10.0, *)) {
120            if (second.storeAction == MTLStoreActionStoreAndMultisampleResolve) {
121                secondStoreValid = (first.resolveTexture == second.resolveTexture) &&
122                                   (first.storeAction == MTLStoreActionStoreAndMultisampleResolve);
123                // TODO: if the first store action is simply MultisampleResolve we could reset
124                // the active RenderCommandEncoder's store action to StoreAndMultisampleResolve,
125                // but it's not clear if it's worth it.
126
127                // When we first check whether store actions are valid we don't consider resolves,
128                // so we need to reset that here.
129                storeActionsValid = secondStoreValid;
130            }
131        }
132    }
133
134    return renderTargetsMatch &&
135           (nil == first.texture ||
136            (storeActionsValid && loadActionsValid && secondDoesntSampleFirst && secondStoreValid));
137}
138
139GrMtlRenderCommandEncoder* GrMtlCommandBuffer::getRenderCommandEncoder(
140        MTLRenderPassDescriptor* descriptor, const GrMtlPipelineState* pipelineState,
141        GrMtlOpsRenderPass* opsRenderPass) {
142    if (nil != fPreviousRenderPassDescriptor) {
143        if (compatible(fPreviousRenderPassDescriptor.colorAttachments[0],
144                       descriptor.colorAttachments[0], pipelineState) &&
145            compatible(fPreviousRenderPassDescriptor.stencilAttachment,
146                       descriptor.stencilAttachment, pipelineState)) {
147            return fActiveRenderCommandEncoder.get();
148        }
149    }
150
151    return this->getRenderCommandEncoder(descriptor, opsRenderPass);
152}
153
154GrMtlRenderCommandEncoder* GrMtlCommandBuffer::getRenderCommandEncoder(
155        MTLRenderPassDescriptor* descriptor,
156        GrMtlOpsRenderPass* opsRenderPass) {
157    this->endAllEncoding();
158#ifdef SK_BUILD_FOR_IOS
159    if (GrMtlIsAppInBackground()) {
160        fActiveRenderCommandEncoder = nullptr;
161        NSLog(@"GrMtlCommandBuffer: tried to create MTLRenderCommandEncoder while in background.");
162        return nullptr;
163    }
164#endif
165    fActiveRenderCommandEncoder = GrMtlRenderCommandEncoder::Make(
166            [fCmdBuffer renderCommandEncoderWithDescriptor:descriptor]);
167    if (opsRenderPass) {
168        opsRenderPass->initRenderState(fActiveRenderCommandEncoder.get());
169    }
170    fPreviousRenderPassDescriptor = descriptor;
171    fHasWork = true;
172
173    return fActiveRenderCommandEncoder.get();
174}
175
176bool GrMtlCommandBuffer::commit(bool waitUntilCompleted) {
177    this->endAllEncoding();
178#ifdef SK_BUILD_FOR_IOS
179    if (GrMtlIsAppInBackground()) {
180        NSLog(@"GrMtlCommandBuffer: Tried to commit command buffer while in background.\n");
181        return false;
182    }
183#endif
184    [fCmdBuffer commit];
185    if (waitUntilCompleted) {
186        this->waitUntilCompleted();
187    }
188
189    if (fCmdBuffer.status == MTLCommandBufferStatusError) {
190        NSString* description = fCmdBuffer.error.localizedDescription;
191        const char* errorString = [description UTF8String];
192        SkDebugf("Error submitting command buffer: %s\n", errorString);
193    }
194
195    return (fCmdBuffer.status != MTLCommandBufferStatusError);
196}
197
198void GrMtlCommandBuffer::endAllEncoding() {
199    if (fActiveRenderCommandEncoder) {
200        fActiveRenderCommandEncoder->endEncoding();
201        fActiveRenderCommandEncoder.reset();
202        fPreviousRenderPassDescriptor = nil;
203    }
204    if (fActiveBlitCommandEncoder) {
205        [fActiveBlitCommandEncoder endEncoding];
206        fActiveBlitCommandEncoder = nil;
207    }
208}
209
210void GrMtlCommandBuffer::encodeSignalEvent(sk_sp<GrMtlEvent> event, uint64_t eventValue) {
211    SkASSERT(fCmdBuffer);
212    this->endAllEncoding(); // ensure we don't have any active command encoders
213    if (@available(macOS 10.14, iOS 12.0, *)) {
214        [fCmdBuffer encodeSignalEvent:event->mtlEvent() value:eventValue];
215        this->addResource(std::move(event));
216    }
217    fHasWork = true;
218}
219
220void GrMtlCommandBuffer::encodeWaitForEvent(sk_sp<GrMtlEvent> event, uint64_t eventValue) {
221    SkASSERT(fCmdBuffer);
222    this->endAllEncoding(); // ensure we don't have any active command encoders
223                            // TODO: not sure if needed but probably
224    if (@available(macOS 10.14, iOS 12.0, *)) {
225        [fCmdBuffer encodeWaitForEvent:event->mtlEvent() value:eventValue];
226        this->addResource(std::move(event));
227    }
228    fHasWork = true;
229}
230
231GR_NORETAIN_END
232