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