| /* |
| * Copyright 2019 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "src/gpu/mtl/GrMtlCommandBuffer.h" |
| |
| #include "src/gpu/mtl/GrMtlGpu.h" |
| #include "src/gpu/mtl/GrMtlOpsRenderPass.h" |
| #include "src/gpu/mtl/GrMtlPipelineState.h" |
| |
| #if !__has_feature(objc_arc) |
| #error This file must be compiled with Arc. Use -fobjc-arc flag |
| #endif |
| |
| sk_sp<GrMtlCommandBuffer> GrMtlCommandBuffer::Make(id<MTLCommandQueue> queue) { |
| id<MTLCommandBuffer> mtlCommandBuffer; |
| mtlCommandBuffer = [queue commandBuffer]; |
| if (nil == mtlCommandBuffer) { |
| return nullptr; |
| } |
| |
| mtlCommandBuffer.label = @"GrMtlCommandBuffer::Create"; |
| |
| return sk_sp<GrMtlCommandBuffer>(new GrMtlCommandBuffer(mtlCommandBuffer)); |
| } |
| |
| GrMtlCommandBuffer::~GrMtlCommandBuffer() { |
| this->endAllEncoding(); |
| fTrackedGrBuffers.reset(); |
| this->callFinishedCallbacks(); |
| |
| fCmdBuffer = nil; |
| } |
| |
| id<MTLBlitCommandEncoder> GrMtlCommandBuffer::getBlitCommandEncoder() { |
| if (fActiveBlitCommandEncoder) { |
| return fActiveBlitCommandEncoder; |
| } |
| |
| this->endAllEncoding(); |
| fActiveBlitCommandEncoder = [fCmdBuffer blitCommandEncoder]; |
| fHasWork = true; |
| |
| return fActiveBlitCommandEncoder; |
| } |
| |
| static bool compatible(const MTLRenderPassAttachmentDescriptor* first, |
| const MTLRenderPassAttachmentDescriptor* second, |
| const GrMtlPipelineState* pipelineState) { |
| // Check to see if the previous descriptor is compatible with the new one. |
| // They are compatible if: |
| // * they share the same rendertargets |
| // * the first's store actions are either Store or DontCare |
| // * the second's load actions are either Load or DontCare |
| // * the second doesn't sample from any rendertargets in the first |
| bool renderTargetsMatch = (first.texture == second.texture); |
| bool storeActionsValid = first.storeAction == MTLStoreActionStore || |
| first.storeAction == MTLStoreActionDontCare; |
| bool loadActionsValid = second.loadAction == MTLLoadActionLoad || |
| second.loadAction == MTLLoadActionDontCare; |
| bool secondDoesntSampleFirst = (!pipelineState || |
| pipelineState->doesntSampleAttachment(first)) && |
| second.storeAction != MTLStoreActionMultisampleResolve; |
| |
| return renderTargetsMatch && |
| (nil == first.texture || |
| (storeActionsValid && loadActionsValid && secondDoesntSampleFirst)); |
| } |
| |
| id<MTLRenderCommandEncoder> GrMtlCommandBuffer::getRenderCommandEncoder( |
| MTLRenderPassDescriptor* descriptor, const GrMtlPipelineState* pipelineState, |
| GrMtlOpsRenderPass* opsRenderPass) { |
| if (nil != fPreviousRenderPassDescriptor) { |
| if (compatible(fPreviousRenderPassDescriptor.colorAttachments[0], |
| descriptor.colorAttachments[0], pipelineState) && |
| compatible(fPreviousRenderPassDescriptor.stencilAttachment, |
| descriptor.stencilAttachment, pipelineState)) { |
| return fActiveRenderCommandEncoder; |
| } |
| } |
| |
| this->endAllEncoding(); |
| fActiveRenderCommandEncoder = [fCmdBuffer renderCommandEncoderWithDescriptor:descriptor]; |
| if (opsRenderPass) { |
| opsRenderPass->initRenderState(fActiveRenderCommandEncoder); |
| } |
| fPreviousRenderPassDescriptor = descriptor; |
| fHasWork = true; |
| |
| return fActiveRenderCommandEncoder; |
| } |
| |
| bool GrMtlCommandBuffer::commit(bool waitUntilCompleted) { |
| this->endAllEncoding(); |
| [fCmdBuffer commit]; |
| if (waitUntilCompleted) { |
| this->waitUntilCompleted(); |
| } |
| |
| if (fCmdBuffer.status == MTLCommandBufferStatusError) { |
| NSString* description = fCmdBuffer.error.localizedDescription; |
| const char* errorString = [description UTF8String]; |
| SkDebugf("Error submitting command buffer: %s\n", errorString); |
| } |
| |
| return (fCmdBuffer.status != MTLCommandBufferStatusError); |
| } |
| |
| void GrMtlCommandBuffer::endAllEncoding() { |
| if (fActiveRenderCommandEncoder) { |
| [fActiveRenderCommandEncoder endEncoding]; |
| fActiveRenderCommandEncoder = nil; |
| fPreviousRenderPassDescriptor = nil; |
| } |
| if (fActiveBlitCommandEncoder) { |
| [fActiveBlitCommandEncoder endEncoding]; |
| fActiveBlitCommandEncoder = nil; |
| } |
| } |
| |
| void GrMtlCommandBuffer::encodeSignalEvent(id<MTLEvent> event, uint64_t eventValue) { |
| SkASSERT(fCmdBuffer); |
| this->endAllEncoding(); // ensure we don't have any active command encoders |
| if (@available(macOS 10.14, iOS 12.0, *)) { |
| [fCmdBuffer encodeSignalEvent:event value:eventValue]; |
| } |
| fHasWork = true; |
| } |
| |
| void GrMtlCommandBuffer::encodeWaitForEvent(id<MTLEvent> event, uint64_t eventValue) { |
| SkASSERT(fCmdBuffer); |
| this->endAllEncoding(); // ensure we don't have any active command encoders |
| // TODO: not sure if needed but probably |
| if (@available(macOS 10.14, iOS 12.0, *)) { |
| [fCmdBuffer encodeWaitForEvent:event value:eventValue]; |
| } |
| fHasWork = true; |
| } |
| |