Implemented my own vertex buffers management, also started working on textures.
parent
a34d95b848
commit
a18ef3197a
|
@ -10,14 +10,19 @@
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
|
|
||||||
// TODO(yuval): Convert this to a struct when I handle my own caching solution
|
struct Metal_Buffer{
|
||||||
@interface Metal_Buffer : NSObject
|
Node node;
|
||||||
@property (nonatomic, strong) id<MTLBuffer> buffer;
|
|
||||||
@property (nonatomic, readonly) u32 size;
|
|
||||||
@property (nonatomic, assign) NSTimeInterval last_reuse_time;
|
|
||||||
|
|
||||||
- (instancetype)initWithSize:(u32)size usingDevice:(id<MTLDevice>)device;
|
id<MTLBuffer> buffer;
|
||||||
@end
|
u32 size;
|
||||||
|
u64 last_reuse_time;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Metal_Texture{
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
@interface Metal_Renderer : NSObject<MTKViewDelegate>
|
@interface Metal_Renderer : NSObject<MTKViewDelegate>
|
||||||
@property (nonatomic) Render_Target *target;
|
@property (nonatomic) Render_Target *target;
|
||||||
|
@ -123,24 +128,20 @@ shape_value *= has_thickness;
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
|
|
||||||
@implementation Metal_Buffer
|
function Metal_Buffer*
|
||||||
- (instancetype)initWithSize:(u32)size usingDevice:(id<MTLDevice>)device{
|
metal__make_buffer(u32 size, id<MTLDevice> device){
|
||||||
self = [super init];
|
Metal_Buffer *result = (Metal_Buffer*)malloc(sizeof(Metal_Buffer));
|
||||||
if (self == nil){
|
|
||||||
return(nil);
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE(yuval): Create the vertex buffer
|
// NOTE(yuval): Create the vertex buffer
|
||||||
MTLResourceOptions options = MTLCPUCacheModeWriteCombined|MTLResourceStorageModeManaged;
|
MTLResourceOptions options = MTLCPUCacheModeWriteCombined|MTLResourceStorageModeManaged;
|
||||||
_buffer = [device newBufferWithLength:size options:options];
|
result->buffer = [device newBufferWithLength:size options:options];
|
||||||
_size = size;
|
result->size = size;
|
||||||
|
|
||||||
// NOTE(yuval): Set the last_reuse_time to the current time
|
// 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<MTLCommandQueue> command_queue;
|
||||||
id<MTLCaptureScope> capture_scope;
|
id<MTLCaptureScope> capture_scope;
|
||||||
|
|
||||||
NSMutableArray<Metal_Buffer*> *buffer_cache;
|
Node buffer_cache;
|
||||||
NSTimeInterval last_buffer_cache_purge_time;
|
u64 last_buffer_cache_purge_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (nonnull instancetype)initWithMetalKitView:(nonnull MTKView*)mtk_view{
|
- (nonnull instancetype)initWithMetalKitView:(nonnull MTKView*)mtk_view{
|
||||||
|
@ -182,6 +183,7 @@ shape_value *= has_thickness;
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert(error == nil);
|
Assert(error == nil);
|
||||||
|
Assert((vertex_function != nil) && (fragment_function != nil));
|
||||||
|
|
||||||
// NOTE(yuval): Configure the pipeline descriptor
|
// NOTE(yuval): Configure the pipeline descriptor
|
||||||
{
|
{
|
||||||
|
@ -226,8 +228,8 @@ shape_value *= has_thickness;
|
||||||
command_queue = [device newCommandQueue];
|
command_queue = [device newCommandQueue];
|
||||||
|
|
||||||
// NOTE(yuval): Initialize buffer caching
|
// NOTE(yuval): Initialize buffer caching
|
||||||
buffer_cache = [NSMutableArray array];
|
dll_init_sentinel(&buffer_cache);
|
||||||
last_buffer_cache_purge_time = [NSDate date].timeIntervalSince1970;
|
last_buffer_cache_purge_time = system_now_time();
|
||||||
|
|
||||||
// NOTE(yuval): Create a capture scope for gpu frame capture
|
// NOTE(yuval): Create a capture scope for gpu frame capture
|
||||||
capture_scope = [[MTLCaptureManager sharedCaptureManager]
|
capture_scope = [[MTLCaptureManager sharedCaptureManager]
|
||||||
|
@ -297,7 +299,7 @@ shape_value *= has_thickness;
|
||||||
Metal_Buffer *buffer = [self get_reusable_buffer_with_size:vertex_buffer_size];
|
Metal_Buffer *buffer = [self get_reusable_buffer_with_size:vertex_buffer_size];
|
||||||
|
|
||||||
// NOTE(yuval): Pass the vertex buffer to the vertex shader
|
// NOTE(yuval): Pass the vertex buffer to the vertex shader
|
||||||
[render_encoder setVertexBuffer:buffer.buffer
|
[render_encoder setVertexBuffer:buffer->buffer
|
||||||
offset:0
|
offset:0
|
||||||
atIndex:0];
|
atIndex:0];
|
||||||
|
|
||||||
|
@ -344,7 +346,7 @@ shape_value *= has_thickness;
|
||||||
// NOTE(yuval): Copy the vertex data to the vertex buffer
|
// 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;
|
u8 *cursor = group_buffer_contents;
|
||||||
for (Render_Vertex_Array_Node *node = group->vertex_list.first;
|
for (Render_Vertex_Array_Node *node = group->vertex_list.first;
|
||||||
node;
|
node;
|
||||||
|
@ -356,7 +358,7 @@ shape_value *= has_thickness;
|
||||||
|
|
||||||
NSUInteger data_size = (NSUInteger)(cursor - group_buffer_contents);
|
NSUInteger data_size = (NSUInteger)(cursor - group_buffer_contents);
|
||||||
NSRange modify_range = NSMakeRange(buffer_offset, data_size);
|
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
|
// 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
|
vertexStart:0
|
||||||
vertexCount:vertex_count];
|
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{
|
- (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
|
// 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
|
// NOTE(yuval): Purge old buffers that haven't been useful for a while
|
||||||
if ((now - last_buffer_cache_purge_time) > 1.0){
|
if ((now - last_buffer_cache_purge_time) > 1000000){
|
||||||
NSMutableArray *survivors = [NSMutableArray array];
|
Node prev_buffer_cache = buffer_cache;
|
||||||
for (Metal_Buffer *candidate in buffer_cache){
|
dll_init_sentinel(&buffer_cache);
|
||||||
if (candidate.last_reuse_time > last_buffer_cache_purge_time){
|
|
||||||
[survivors addObject:candidate];
|
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;
|
last_buffer_cache_purge_time = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE(yuval): See if we have a buffer we can reuse
|
// NOTE(yuval): See if we have a buffer we can reuse
|
||||||
Metal_Buffer *best_candidate = 0;
|
Metal_Buffer *best_candidate = 0;
|
||||||
for (Metal_Buffer *candidate in buffer_cache){
|
for (Node *node = buffer_cache.next;
|
||||||
if ((candidate.size >= size) && ((!best_candidate) || (best_candidate.last_reuse_time > candidate.last_reuse_time))){
|
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;
|
best_candidate = candidate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Metal_Buffer *result;
|
Metal_Buffer *result;
|
||||||
if (best_candidate){
|
if (best_candidate){
|
||||||
[buffer_cache removeObject:best_candidate];
|
// NOTE(yuval): A best candidate has been found! Remove it from the buffer list and set its last reuse time.
|
||||||
best_candidate.last_reuse_time = now;
|
dll_remove(&best_candidate->node);
|
||||||
|
best_candidate->last_reuse_time = now;
|
||||||
result = best_candidate;
|
result = best_candidate;
|
||||||
} else{
|
} 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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)add_reusable_buffer:(Metal_Buffer*)buffer{
|
- (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
|
// 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
|
@end
|
||||||
|
|
Loading…
Reference in New Issue