// Copyright 2019 The MediaPipe Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #import "mediapipe/gpu/MPPMetalUtil.h" #include "absl/time/clock.h" #include "absl/time/time.h" @implementation MPPMetalUtil + (void)blitMetalBufferTo:(id)destination from:(id)source blocking:(bool)blocking commandBuffer:(id)commandBuffer { size_t bytes = MIN(destination.length, source.length); [self blitMetalBufferTo:destination destinationOffset:0 from:source sourceOffset:0 bytes:bytes blocking:blocking commandBuffer:commandBuffer]; } + (void)blitMetalBufferTo:(id)destination destinationOffset:(int)destinationOffset from:(id)source sourceOffset:(int)sourceOffset bytes:(size_t)bytes blocking:(bool)blocking commandBuffer:(id)commandBuffer { id blit_command = [commandBuffer blitCommandEncoder]; [blit_command copyFromBuffer:source sourceOffset:sourceOffset toBuffer:destination destinationOffset:destinationOffset size:bytes]; [blit_command endEncoding]; if (blocking) { [MPPMetalUtil commitCommandBufferAndWait:commandBuffer]; } else { [commandBuffer commit]; } } + (void)commitCommandBufferAndWait:(id)commandBuffer { #if !defined(MEDIAPIPE_DISABLE_ACTIVE_WAIT) // The bufferCompleted variable doesn't require atomic access. // std::atomic<> can't be used here because the variable must be captured // with the block. Also std::atomic<> orders changes of the variable but // in this case any kind of out-of-order execution will be serialized. __block volatile bool bufferCompleted = false; [commandBuffer addCompletedHandler:^(id) { bufferCompleted = true; }]; [commandBuffer commit]; absl::Time start_time = absl::Now(); while (!bufferCompleted) { auto duration = absl::Now() - start_time; // If the spin-lock takes more than 5 ms then go to blocking wait: // - it frees the CPU core for another threads: increase the performance/decrease power // consumption. // - if a driver thread that notifies that the GPU buffer is completed has lower priority then // the CPU core is allocated for the thread. if (duration >= absl::Milliseconds(5)) { [commandBuffer waitUntilCompleted]; break; } } #else [commandBuffer commit]; [commandBuffer waitUntilCompleted]; #endif } @end