Apple предлагает пример синхронизации процессора и графического процессора , в котором показано, как синхронизировать доступ к общим ресурсам между процессором и графическим процессором.Для этого он использует семафор, который хранится в переменной экземпляра:
@implementation AAPLRenderer
{
dispatch_semaphore_t _inFlightSemaphore;
// other ivars
}
Этот семафор затем определяется другим методом:
- (nonnull instancetype)initWithMetalKitView:(nonnull MTKView *)mtkView
{
self = [super init];
if(self)
{
_device = mtkView.device;
_inFlightSemaphore = dispatch_semaphore_create(MaxBuffersInFlight);
// further initializations
}
return self;
}
MaxBuffersInFlight
определяется следующим образом:
// The max number of command buffers in flight
static const NSUInteger MaxBuffersInFlight = 3;
Наконец, семафор используется следующим образом:
/// Called whenever the view needs to render
- (void)drawInMTKView:(nonnull MTKView *)view
{
// Wait to ensure only MaxBuffersInFlight number of frames are getting processed
// by any stage in the Metal pipeline (App, Metal, Drivers, GPU, etc)
dispatch_semaphore_wait(_inFlightSemaphore, DISPATCH_TIME_FOREVER);
// Iterate through our Metal buffers, and cycle back to the first when we've written to MaxBuffersInFlight
_currentBuffer = (_currentBuffer + 1) % MaxBuffersInFlight;
// Update data in our buffers
[self updateState];
// Create a new command buffer for each render pass to the current drawable
id<MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];
commandBuffer.label = @"MyCommand";
// Add completion hander which signals _inFlightSemaphore when Metal and the GPU has fully
// finished processing the commands we're encoding this frame. This indicates when the
// dynamic buffers filled with our vertices, that we're writing to this frame, will no longer
// be needed by Metal and the GPU, meaning we can overwrite the buffer contents without
// corrupting the rendering.
__block dispatch_semaphore_t block_sema = _inFlightSemaphore;
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> buffer)
{
dispatch_semaphore_signal(block_sema);
}];
// rest of the method
}
Что я не понимаю, так это необходимость строки
__block dispatch_semaphore_t block_sema = _inFlightSemaphore;
Почемумне нужно скопировать переменную экземпляра в локальную переменную и пометить эту локальную переменную __block
.Если я просто отброшу эту локальную переменную и вместо этого напишу
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> buffer)
{
dispatch_semaphore_signal(_inFlightSemaphore);
}];
, то , кажется, также будет работать.Я также попытался пометить переменную экземпляра __block
следующим образом:
__block dispatch_semaphore_t _bufferAccessSemaphore;
Это компилируется с Clang, и , кажется, также работает.Но поскольку речь идет о предотвращении состояния гонки, я хочу быть уверенным в том, что он работает.
Итак, вопрос в том, почему Apple создает эту локальную копию семафора, помеченную __block
?Действительно ли это необходимо, или подход с прямым доступом к переменной экземпляра работает так же хорошо?
В качестве примечания, ответ на этот SO вопрос отмечает, что маркировка переменных экземпляра __block
не может быть сделано.Ответ в соответствии с gcc, но почему Clang допускает это, если этого не следует делать?