metal: fix buffer overwrite flickering

This commit is contained in:
a dinosaur 2024-09-13 20:57:37 +10:00
parent 2496f9e2a3
commit 9fd2016999
2 changed files with 54 additions and 34 deletions

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
.idea/ .idea/
cmake-build-*/ cmake-build-*/
build/ build/
xcode/
.DS_Store .DS_Store

View File

@ -43,9 +43,11 @@
id<MTLRenderPipelineState> _pso; id<MTLRenderPipelineState> _pso;
MTLViewport _viewport; MTLViewport _viewport;
vector_float4 _drawColourF; vector_float4 _drawColourF;
dispatch_semaphore_t _inFlightSemaphore;
unsigned _vtxListCount, _vtxListReserve, _idxListCount, _idxListReserve; unsigned _vtxListCount[3], _vtxListReserve[3], _idxListCount[3], _idxListReserve[3];
id<MTLBuffer> _vtxMtlBuffer, _idxMtlBuffer; id<MTLBuffer> _vtxMtlBuffer[3], _idxMtlBuffer[3], *_vtxFront, *_idxFront;
int _frame;
} }
- (id) init:(SDL_Window*)window - (id) init:(SDL_Window*)window
@ -54,10 +56,14 @@
return nil; return nil;
self.drawColour = BLACK; self.drawColour = BLACK;
_vtxListReserve = 0; _vtxListReserve[0] = _vtxListReserve[1] = _vtxListReserve[2] = 0;
_idxListReserve = 0; _idxListReserve[0] = _idxListReserve[1] = _idxListReserve[2] = 0;
_vtxMtlBuffer = nil; _vtxMtlBuffer[0] = _vtxMtlBuffer[1] = _vtxMtlBuffer[2] = nil;
_idxMtlBuffer = nil; _idxMtlBuffer[0] = _idxMtlBuffer[1] = _idxMtlBuffer[2] = nil;
_vtxFront = &_vtxMtlBuffer[0];
_idxFront = &_idxMtlBuffer[0];
_frame = 0;
// Create Metal view // Create Metal view
_window = window; _window = window;
@ -119,6 +125,9 @@
// Set viewport // Set viewport
[self setView:[self getDrawSize]]; [self setView:[self getDrawSize]];
// Allow up to 3 frames in-flight
_inFlightSemaphore = dispatch_semaphore_create(3);
return self; return self;
} }
@ -131,10 +140,10 @@
- (unsigned) resizeBuffer:(id <MTLBuffer>*)buf itemSize:(unsigned)nsz requiredNum:(unsigned)count - (unsigned) resizeBuffer:(id <MTLBuffer>*)buf itemSize:(unsigned)nsz requiredNum:(unsigned)count
{ {
unsigned reserve; unsigned long reserve;
if (*buf) if (*buf)
if (count * nsz <= (reserve = [*buf length])) if (count * nsz <= (reserve = [*buf length]))
return reserve; return (unsigned)reserve;
// Calculate new capacity // Calculate new capacity
unsigned newCapacity = (count + DRAWLIST_CHUNK_SIZE - 1) / DRAWLIST_CHUNK_SIZE * DRAWLIST_CHUNK_SIZE; unsigned newCapacity = (count + DRAWLIST_CHUNK_SIZE - 1) / DRAWLIST_CHUNK_SIZE * DRAWLIST_CHUNK_SIZE;
@ -185,55 +194,57 @@
{ {
_passDesc.colorAttachments[0].clearColor = MTLClearColorMake( _passDesc.colorAttachments[0].clearColor = MTLClearColorMake(
_drawColourF[0], _drawColourF[1], _drawColourF[2], _drawColourF[3]); _drawColourF[0], _drawColourF[1], _drawColourF[2], _drawColourF[3]);
_vtxListCount = 0; _vtxListCount[_frame] = 0;
_idxListCount = 0; _idxListCount[_frame] = 0;
} }
- (void) reserveVertices:(unsigned)count - (void) reserveVertices:(unsigned)count
{ {
count += _vtxListCount; count += _vtxListCount[_frame];
if (count * sizeof(ShaderVertex) > _vtxListReserve) if (count * sizeof(ShaderVertex) > _vtxListReserve[_frame])
_vtxListReserve = [self resizeBuffer:&_vtxMtlBuffer itemSize:sizeof(ShaderVertex) requiredNum:count]; _vtxListReserve[_frame] = [self resizeBuffer:_vtxFront itemSize:sizeof(ShaderVertex) requiredNum:count];
} }
- (void) reserveIndices:(unsigned)count - (void) reserveIndices:(unsigned)count
{ {
count += _idxListCount; count += _idxListCount[_frame];
if (count * sizeof(uint16_t) > _idxListReserve) if (count * sizeof(uint16_t) > _idxListReserve[_frame])
_idxListReserve = [self resizeBuffer:&_idxMtlBuffer itemSize:sizeof(uint16_t) requiredNum:count]; _idxListReserve[_frame] = [self resizeBuffer:_idxFront itemSize:sizeof(uint16_t) requiredNum:count];
} }
- (uint16_t) queueVertex:(float)x :(float)y - (uint16_t) queueVertex:(float)x :(float)y
{ {
if (_vtxListCount * sizeof(ShaderVertex) >= _vtxListReserve) if (_vtxListCount[_frame] * sizeof(ShaderVertex) >= _vtxListReserve[_frame])
[self reserveVertices:1]; [self reserveVertices:1];
((ShaderVertex*)_vtxMtlBuffer.contents)[_vtxListCount] = (ShaderVertex){ ((ShaderVertex*)(*_vtxFront).contents)[_vtxListCount[_frame]] = (ShaderVertex){
.position = { x, y }, .position = { x, y },
.colour = _drawColourF }; .colour = _drawColourF };
return _vtxListCount++; return _vtxListCount[_frame]++;
} }
- (uint16_t) queueIndex:(uint16_t)idx - (uint16_t) queueIndex:(uint16_t)idx
{ {
if (_idxListCount * sizeof(uint16_t) >= _idxListReserve) if (_idxListCount[_frame] * sizeof(uint16_t) >= _idxListReserve[_frame])
[self reserveIndices:1]; [self reserveIndices:1];
((uint16_t*)_idxMtlBuffer.contents)[_idxListCount++] = idx; ((uint16_t*)(*_idxFront).contents)[_idxListCount[_frame]++] = idx;
return idx; return idx;
} }
- (void) queueIndices:(uint16_t*)idcs count:(unsigned)count - (void) queueIndices:(uint16_t*)idcs count:(unsigned)count
{ {
if ((_idxListCount + count) * sizeof(uint16_t) >= _idxListReserve) if ((_idxListCount[_frame] + count) * sizeof(uint16_t) >= _idxListReserve[_frame])
[self reserveIndices:count]; [self reserveIndices:count];
memcpy(&((uint16_t*)_idxMtlBuffer.contents)[_idxListCount], idcs, count * sizeof(uint16_t)); memcpy(&((uint16_t*)(*_idxFront).contents)[_idxListCount[_frame]], idcs, count * sizeof(uint16_t));
_idxListCount += count; _idxListCount[_frame] += count;
} }
- (void) present - (void) present
{ {
dispatch_semaphore_wait(_inFlightSemaphore, DISPATCH_TIME_FOREVER);
// Synchronise buffers // Synchronise buffers
[_vtxMtlBuffer didModifyRange:(NSRange){ .location = 0, .length = _vtxListCount * sizeof(ShaderVertex) }]; [*_vtxFront didModifyRange:(NSRange){ .location = 0, .length = _vtxListCount[_frame] * sizeof(ShaderVertex) }];
[_idxMtlBuffer didModifyRange:(NSRange){ .location = 0, .length = _idxListCount * sizeof(uint16_t) }]; [*_idxFront didModifyRange:(NSRange){ .location = 0, .length = _idxListCount[_frame] * sizeof(uint16_t) }];
@autoreleasepool @autoreleasepool
{ {
@ -241,29 +252,37 @@
_passDesc.colorAttachments[0].texture = rt.texture; _passDesc.colorAttachments[0].texture = rt.texture;
id<MTLCommandBuffer> cmdBuf = [_queue commandBuffer]; id<MTLCommandBuffer> cmdBuf = [_queue commandBuffer];
id<MTLRenderCommandEncoder> enc = [cmdBuf renderCommandEncoderWithDescriptor:_passDesc]; [cmdBuf addCompletedHandler:^(id<MTLCommandBuffer> _Nonnull _)
{
dispatch_semaphore_signal(_inFlightSemaphore);
}];
id<MTLRenderCommandEncoder> enc = [cmdBuf renderCommandEncoderWithDescriptor:_passDesc];
[enc setViewport:_viewport]; [enc setViewport:_viewport];
[enc setCullMode:MTLCullModeNone]; [enc setCullMode:MTLCullModeNone];
[enc setRenderPipelineState:_pso]; [enc setRenderPipelineState:_pso];
if (_vtxMtlBuffer && _idxMtlBuffer) if (*_vtxFront && *_idxFront)
{ {
[enc setVertexBuffer:_vtxMtlBuffer offset:0 atIndex:ShaderInputIdxVerticies]; [enc setVertexBuffer:*_vtxFront offset:0 atIndex:ShaderInputIdxVerticies];
const vector_float2 viewportScale = { (float)(1.0 / _viewport.width), (float)(1.0 / _viewport.height) }; const vector_float2 viewportScale = { (float)(1.0 / _viewport.width), (float)(1.0 / _viewport.height) };
[enc setVertexBytes:&viewportScale length:sizeof(vector_float2) atIndex:ShaderInputViewportScale]; [enc setVertexBytes:&viewportScale length:sizeof(vector_float2) atIndex:ShaderInputViewportScale];
[enc drawIndexedPrimitives:MTLPrimitiveTypeLine [enc drawIndexedPrimitives:MTLPrimitiveTypeLine
indexCount:_idxListCount indexType:MTLIndexTypeUInt16 indexCount:_idxListCount[_frame] indexType:MTLIndexTypeUInt16
indexBuffer:_idxMtlBuffer indexBufferOffset:0]; indexBuffer:*_idxFront indexBufferOffset:0];
_vtxListCount = 0; _vtxListCount[_frame] = _idxListCount[_frame] = 0;
_idxListCount = 0;
} }
[enc endEncoding]; [enc endEncoding];
[cmdBuf presentDrawable:rt]; [cmdBuf presentDrawable:rt];
[cmdBuf commit]; [cmdBuf commit];
} }
if (++_frame == 3)
_frame = 0;
_vtxFront = &_vtxMtlBuffer[_frame];
_idxFront = &_idxMtlBuffer[_frame];
} }
@end @end
@ -324,7 +343,7 @@ void DrawRect(int x, int y, int w, int h)
[renderer reserveVertices:4]; [renderer reserveVertices:4];
vector_float2 vector_float2
f00 = { x, y }, f10 = { x + w, y }, f00 = { x, y }, f10 = { x + w, y },
f01 = { x, y + h}, f11 = { x + w, y + h }; f01 = { x, y + h }, f11 = { x + w, y + h };
uint16_t i00 = [renderer queueVertex:f00[0] :f00[1]]; uint16_t i00 = [renderer queueVertex:f00[0] :f00[1]];
uint16_t i10 = [renderer queueVertex:f10[0] :f10[1]]; uint16_t i10 = [renderer queueVertex:f10[0] :f10[1]];
uint16_t i11 = [renderer queueVertex:f11[0] :f11[1]]; uint16_t i11 = [renderer queueVertex:f11[0] :f11[1]];