// 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<MTLBuffer>)destination
from:(id<MTLBuffer>)source
blocking:(bool)blocking
commandBuffer:(id<MTLCommandBuffer>)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<MTLBuffer>)destination
destinationOffset:(int)destinationOffset
from:(id<MTLBuffer>)source
sourceOffset:(int)sourceOffset
bytes:(size_t)bytes
blocking:(bool)blocking
commandBuffer:(id<MTLCommandBuffer>)commandBuffer {
id<MTLBlitCommandEncoder> 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<MTLCommandBuffer>)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<MTLCommandBuffer>) {
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