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;
|
||||||
|
@ -39,7 +44,7 @@ using namespace metal;
|
||||||
|
|
||||||
typedef struct{
|
typedef struct{
|
||||||
float2 xy [[attribute(0)]];
|
float2 xy [[attribute(0)]];
|
||||||
float3 uvw [[attribute(1)]];
|
float3 uvw [[attribute(1)]];
|
||||||
uint32_t color [[attribute(2)]];
|
uint32_t color [[attribute(2)]];
|
||||||
float half_thickness [[attribute(3)]];
|
float half_thickness [[attribute(3)]];
|
||||||
} Vertex;
|
} Vertex;
|
||||||
|
@ -50,11 +55,11 @@ typedef struct{
|
||||||
float4 position [[position]];
|
float4 position [[position]];
|
||||||
|
|
||||||
// NOTE(yuval): Fragment shader inputs
|
// NOTE(yuval): Fragment shader inputs
|
||||||
float4 color;
|
float4 color;
|
||||||
float3 uvw;
|
float3 uvw;
|
||||||
float2 xy;
|
float2 xy;
|
||||||
float2 adjusted_half_dim;
|
float2 adjusted_half_dim;
|
||||||
float half_thickness;
|
float half_thickness;
|
||||||
} Rasterizer_Data;
|
} Rasterizer_Data;
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
|
@ -62,32 +67,32 @@ float4 position [[position]];
|
||||||
vertex Rasterizer_Data
|
vertex Rasterizer_Data
|
||||||
vertex_shader(Vertex in [[stage_in]],
|
vertex_shader(Vertex in [[stage_in]],
|
||||||
constant float4x4 &proj [[buffer(1)]]){
|
constant float4x4 &proj [[buffer(1)]]){
|
||||||
Rasterizer_Data out;
|
Rasterizer_Data out;
|
||||||
|
|
||||||
// NOTE(yuval): Calculate position in NDC
|
// NOTE(yuval): Calculate position in NDC
|
||||||
out.position = proj * float4(in.xy, 0.0, 1.0);
|
out.position = proj * float4(in.xy, 0.0, 1.0);
|
||||||
|
|
||||||
// NOTE(yuval): Convert color to float4 format
|
// NOTE(yuval): Convert color to float4 format
|
||||||
out.color.b = ((float((in.color ) & 0xFFu)) / 255.0);
|
out.color.b = ((float((in.color ) & 0xFFu)) / 255.0);
|
||||||
out.color.g = ((float((in.color >> 8u) & 0xFFu)) / 255.0);
|
out.color.g = ((float((in.color >> 8u) & 0xFFu)) / 255.0);
|
||||||
out.color.r = ((float((in.color >> 16u) & 0xFFu)) / 255.0);
|
out.color.r = ((float((in.color >> 16u) & 0xFFu)) / 255.0);
|
||||||
out.color.a = ((float((in.color >> 24u) & 0xFFu)) / 255.0);
|
out.color.a = ((float((in.color >> 24u) & 0xFFu)) / 255.0);
|
||||||
|
|
||||||
// NOTE(yuval): Pass uvw coordinates to the fragment shader
|
// NOTE(yuval): Pass uvw coordinates to the fragment shader
|
||||||
out.uvw = in.uvw;
|
out.uvw = in.uvw;
|
||||||
|
|
||||||
// NOTE(yuval): Calculate adjusted half dim
|
// NOTE(yuval): Calculate adjusted half dim
|
||||||
float2 center = in.uvw.xy;
|
float2 center = in.uvw.xy;
|
||||||
float2 half_dim = abs(in.xy - center);
|
float2 half_dim = abs(in.xy - center);
|
||||||
out.adjusted_half_dim = (half_dim - in.uvw.zz + float2(0.5, 0.5));
|
out.adjusted_half_dim = (half_dim - in.uvw.zz + float2(0.5, 0.5));
|
||||||
|
|
||||||
// NOTE(yuval): Pass half_thickness to the fragment shader
|
// NOTE(yuval): Pass half_thickness to the fragment shader
|
||||||
out.half_thickness = in.half_thickness;
|
out.half_thickness = in.half_thickness;
|
||||||
|
|
||||||
// NOTE(yuval): Pass xy to the fragment shader
|
// NOTE(yuval): Pass xy to the fragment shader
|
||||||
out.xy = in.xy;
|
out.xy = in.xy;
|
||||||
|
|
||||||
return(out);
|
return(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
|
@ -115,32 +120,28 @@ sd = (abs(sd + in.half_thickness) - in.half_thickness);
|
||||||
float shape_value = (1.0 - smoothstep(-1.0, 0.0, sd));
|
float shape_value = (1.0 - smoothstep(-1.0, 0.0, sd));
|
||||||
shape_value *= has_thickness;
|
shape_value *= has_thickness;
|
||||||
|
|
||||||
// TOOD(yuval): Add sample_value to alpha
|
// TOOD(yuval): Add sample_value to alpha
|
||||||
float4 out_color = in.color;// float4(in.color.xyz, in.color.a * (shape_value));
|
float4 out_color = in.color;// float4(in.color.xyz, in.color.a * (shape_value));
|
||||||
return(out_color);
|
return(out_color);
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
|
|
||||||
@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]
|
||||||
|
@ -249,7 +251,7 @@ shape_value *= has_thickness;
|
||||||
i32 width = _target->width;
|
i32 width = _target->width;
|
||||||
i32 height = _target->height;
|
i32 height = _target->height;
|
||||||
|
|
||||||
Font_Set* font_set = (Font_Set*)_target->font_set;
|
Font_Set *font_set = (Font_Set*)_target->font_set;
|
||||||
|
|
||||||
// NOTE(yuval): Create the command buffer
|
// NOTE(yuval): Create the command buffer
|
||||||
id<MTLCommandBuffer> command_buffer = [command_queue commandBuffer];
|
id<MTLCommandBuffer> command_buffer = [command_queue commandBuffer];
|
||||||
|
@ -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
|
||||||
|
|
|
@ -460,8 +460,8 @@ this only gets called for window creation and other extraordinary events.
|
||||||
return(YES);
|
return(YES);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)keyDown:(NSEvent *)event{
|
- (void)keyDown:(NSEvent*)event{
|
||||||
NSString* characters = [event characters];
|
NSString *characters = [event characters];
|
||||||
if ([characters length] != 0) {
|
if ([characters length] != 0) {
|
||||||
u32 character_code = [characters characterAtIndex:0];
|
u32 character_code = [characters characterAtIndex:0];
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue