Implemented my own vertex buffers management, also started working on textures.

master
Yuval Dolev 2020-01-08 01:45:36 +02:00
parent a34d95b848
commit a18ef3197a
2 changed files with 88 additions and 79 deletions

View File

@ -10,14 +10,19 @@
////////////////////////////////
// TODO(yuval): Convert this to a struct when I handle my own caching solution
@interface Metal_Buffer : NSObject
@property (nonatomic, strong) id<MTLBuffer> buffer;
@property (nonatomic, readonly) u32 size;
@property (nonatomic, assign) NSTimeInterval last_reuse_time;
struct Metal_Buffer{
Node node;
- (instancetype)initWithSize:(u32)size usingDevice:(id<MTLDevice>)device;
@end
id<MTLBuffer> buffer;
u32 size;
u64 last_reuse_time;
};
struct Metal_Texture{
};
////////////////////////////////
@interface Metal_Renderer : NSObject<MTKViewDelegate>
@property (nonatomic) Render_Target *target;
@ -123,24 +128,20 @@ shape_value *= has_thickness;
////////////////////////////////
@implementation Metal_Buffer
- (instancetype)initWithSize:(u32)size usingDevice:(id<MTLDevice>)device{
self = [super init];
if (self == nil){
return(nil);
}
function Metal_Buffer*
metal__make_buffer(u32 size, id<MTLDevice> device){
Metal_Buffer *result = (Metal_Buffer*)malloc(sizeof(Metal_Buffer));
// NOTE(yuval): Create the vertex buffer
MTLResourceOptions options = MTLCPUCacheModeWriteCombined|MTLResourceStorageModeManaged;
_buffer = [device newBufferWithLength:size options:options];
_size = size;
result->buffer = [device newBufferWithLength:size options:options];
result->size = size;
// NOTE(yuval): Set the last_reuse_time to the current time
_last_reuse_time = [NSDate date].timeIntervalSince1970;
result->last_reuse_time = system_now_time();
return(self);
return result;
}
@end
////////////////////////////////
@ -150,8 +151,8 @@ shape_value *= has_thickness;
id<MTLCommandQueue> command_queue;
id<MTLCaptureScope> capture_scope;
NSMutableArray<Metal_Buffer*> *buffer_cache;
NSTimeInterval last_buffer_cache_purge_time;
Node buffer_cache;
u64 last_buffer_cache_purge_time;
}
- (nonnull instancetype)initWithMetalKitView:(nonnull MTKView*)mtk_view{
@ -182,6 +183,7 @@ shape_value *= has_thickness;
}
Assert(error == nil);
Assert((vertex_function != nil) && (fragment_function != nil));
// NOTE(yuval): Configure the pipeline descriptor
{
@ -226,8 +228,8 @@ shape_value *= has_thickness;
command_queue = [device newCommandQueue];
// NOTE(yuval): Initialize buffer caching
buffer_cache = [NSMutableArray array];
last_buffer_cache_purge_time = [NSDate date].timeIntervalSince1970;
dll_init_sentinel(&buffer_cache);
last_buffer_cache_purge_time = system_now_time();
// NOTE(yuval): Create a capture scope for gpu frame capture
capture_scope = [[MTLCaptureManager sharedCaptureManager]
@ -297,7 +299,7 @@ shape_value *= has_thickness;
Metal_Buffer *buffer = [self get_reusable_buffer_with_size:vertex_buffer_size];
// NOTE(yuval): Pass the vertex buffer to the vertex shader
[render_encoder setVertexBuffer:buffer.buffer
[render_encoder setVertexBuffer:buffer->buffer
offset:0
atIndex:0];
@ -344,7 +346,7 @@ shape_value *= has_thickness;
// NOTE(yuval): Copy the vertex data to the vertex buffer
{
u8 *group_buffer_contents = (u8*)[buffer.buffer contents] + buffer_offset;
u8 *group_buffer_contents = (u8*)[buffer->buffer contents] + buffer_offset;
u8 *cursor = group_buffer_contents;
for (Render_Vertex_Array_Node *node = group->vertex_list.first;
node;
@ -356,7 +358,7 @@ shape_value *= has_thickness;
NSUInteger data_size = (NSUInteger)(cursor - group_buffer_contents);
NSRange modify_range = NSMakeRange(buffer_offset, data_size);
[buffer.buffer didModifyRange:modify_range];
[buffer->buffer didModifyRange:modify_range];
}
// NOTE(yuval): Set the vertex buffer offset to the beginning of the group's vertices
@ -367,7 +369,7 @@ shape_value *= has_thickness;
vertexStart:0
vertexCount:vertex_count];
buffer_offset += (vertex_count * sizeof(Render_Vertex)); //((((vertex_count * sizeof(Render_Vertex)) + 256) / 256) * 256);
buffer_offset += (vertex_count * sizeof(Render_Vertex));
}
}
@ -392,46 +394,53 @@ shape_value *= has_thickness;
- (Metal_Buffer*)get_reusable_buffer_with_size:(NSUInteger)size{
// NOTE(yuval): This routine is a modified version of Dear ImGui's MetalContext::dequeueReusableBufferOfLength in imgui_impl_metal.mm
NSTimeInterval now = [NSDate date].timeIntervalSince1970;
u64 now = system_now_time();
// NOTE(yuval): Purge old buffers that haven't been useful for a while
if ((now - last_buffer_cache_purge_time) > 1.0){
NSMutableArray *survivors = [NSMutableArray array];
for (Metal_Buffer *candidate in buffer_cache){
if (candidate.last_reuse_time > last_buffer_cache_purge_time){
[survivors addObject:candidate];
if ((now - last_buffer_cache_purge_time) > 1000000){
Node prev_buffer_cache = buffer_cache;
dll_init_sentinel(&buffer_cache);
for (Node *node = prev_buffer_cache.next;
node != &buffer_cache;
node = node->next){
Metal_Buffer *candidate = CastFromMember(Metal_Buffer, node, node);
if (candidate->last_reuse_time > last_buffer_cache_purge_time){
dll_insert(&buffer_cache, node);
}
}
buffer_cache = [survivors mutableCopy];
last_buffer_cache_purge_time = now;
}
// NOTE(yuval): See if we have a buffer we can reuse
Metal_Buffer *best_candidate = 0;
for (Metal_Buffer *candidate in buffer_cache){
if ((candidate.size >= size) && ((!best_candidate) || (best_candidate.last_reuse_time > candidate.last_reuse_time))){
for (Node *node = buffer_cache.next;
node != &buffer_cache;
node = node->next){
Metal_Buffer *candidate = CastFromMember(Metal_Buffer, node, node);
if ((candidate->size >= size) && ((!best_candidate) || (best_candidate->last_reuse_time > candidate->last_reuse_time))){
best_candidate = candidate;
}
}
Metal_Buffer *result;
if (best_candidate){
[buffer_cache removeObject:best_candidate];
best_candidate.last_reuse_time = now;
// NOTE(yuval): A best candidate has been found! Remove it from the buffer list and set its last reuse time.
dll_remove(&best_candidate->node);
best_candidate->last_reuse_time = now;
result = best_candidate;
} else{
result = [[Metal_Buffer alloc] initWithSize:size usingDevice:device];
// NOTE(yuval): No luck; make a new buffer.
result = metal__make_buffer(size, device);
}
// NOTE(yuval): No luck; make a new buffer
return result;
}
- (void)add_reusable_buffer:(Metal_Buffer*)buffer{
// NOTE(yuval): This routine is a modified version of Dear ImGui's MetalContext::enqueueReusableBuffer in imgui_impl_metal.mm
[buffer_cache addObject:buffer];
dll_insert(&buffer_cache, &buffer->node);
}
@end