Fix font rendering on mac
This commit is contained in:
parent
6469fe5a0f
commit
9e58435637
|
@ -164,10 +164,10 @@ ft__bad_rect_pack_next(Bad_Rect_Pack *pack, Vec2_i32 dim){
|
|||
// NOTE(simon, 28/02/24): We are now sure that the character will fit.
|
||||
pack->current_line_h = Max(pack->current_line_h, dim.y);
|
||||
|
||||
result = pack->p;
|
||||
pack->p.x += dim.x;
|
||||
result = pack->p;
|
||||
pack->p.x += dim.x;
|
||||
pack->dim.x = Max( pack->dim.x, pack->p.x );
|
||||
}
|
||||
}
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
@ -330,55 +330,66 @@ ft__font_make_face(Arena *arena, Face_Description *description, f32 scale_factor
|
|||
Texture_Kind texture_kind = TextureKind_Mono;
|
||||
u32 texture = graphics_get_texture(pack.dim, texture_kind);
|
||||
|
||||
Vec3_f32 texture_dim = V3f32(pack.dim);
|
||||
face->texture_dim = texture_dim;
|
||||
/* NOTE simon (06/01/25): This assumes that every platforms don't use 0 as a valid texture id.
|
||||
This is valid for OpenGL and the DX11 implementaion. Someone needs to check the MAC versions. */
|
||||
if (texture != 0 ){
|
||||
|
||||
{
|
||||
Vec3_i32 p = V3i32((i32)face->white.uv.x0, (i32)face->white.uv.y0, (i32)face->white.w);
|
||||
Vec3_i32 dim = V3i32(white.dim.x, white.dim.y, 1);
|
||||
graphics_fill_texture(texture_kind, texture, p, dim, white.data);
|
||||
face->white.uv.x1 = (face->white.uv.x0 + face->white.uv.x1)/texture_dim.x;
|
||||
face->white.uv.y1 = (face->white.uv.y0 + face->white.uv.y1)/texture_dim.y;
|
||||
face->white.uv.x0 = face->white.uv.x0/texture_dim.x;
|
||||
face->white.uv.y0 = face->white.uv.y0/texture_dim.y;
|
||||
face->white.w /= texture_dim.z;
|
||||
}
|
||||
face->texture_kind = texture_kind;
|
||||
face->texture = texture;
|
||||
|
||||
for (u16 i = 0; i < index_count; i += 1){
|
||||
Vec3_i32 p = V3i32((i32)face->bounds[i].uv.x0, (i32)face->bounds[i].uv.y0, (i32)face->bounds[i].w);
|
||||
Vec3_i32 dim = V3i32(glyph_bitmaps[i].dim.x, glyph_bitmaps[i].dim.y, 1);
|
||||
graphics_fill_texture(texture_kind, texture, p, dim, glyph_bitmaps[i].data);
|
||||
face->bounds[i].uv.x1 = (face->bounds[i].uv.x0 + face->bounds[i].uv.x1)/texture_dim.x;
|
||||
face->bounds[i].uv.y1 = (face->bounds[i].uv.y0 + face->bounds[i].uv.y1)/texture_dim.y;
|
||||
face->bounds[i].uv.x0 = face->bounds[i].uv.x0/texture_dim.x;
|
||||
face->bounds[i].uv.y0 = face->bounds[i].uv.y0/texture_dim.y;
|
||||
}
|
||||
Vec3_f32 texture_dim = V3f32(pack.dim);
|
||||
face->texture_dim = texture_dim;
|
||||
|
||||
{
|
||||
Face_Advance_Map *advance_map = &face->advance_map;
|
||||
{
|
||||
Vec3_i32 p = V3i32((i32)face->white.uv.x0, (i32)face->white.uv.y0, (i32)face->white.w);
|
||||
Vec3_i32 dim = V3i32(white.dim.x, white.dim.y, 1);
|
||||
graphics_fill_texture(texture_kind, texture, p, dim, white.data);
|
||||
face->white.uv.x1 = (face->white.uv.x0 + face->white.uv.x1)/texture_dim.x;
|
||||
face->white.uv.y1 = (face->white.uv.y0 + face->white.uv.y1)/texture_dim.y;
|
||||
face->white.uv.x0 = face->white.uv.x0/texture_dim.x;
|
||||
face->white.uv.y0 = face->white.uv.y0/texture_dim.y;
|
||||
}
|
||||
|
||||
met->space_advance = font_get_glyph_advance(advance_map, met, ' ', 0);
|
||||
met->decimal_digit_advance =
|
||||
font_get_max_glyph_advance_range(advance_map, met, '0', '9', 0);
|
||||
met->hex_digit_advance =
|
||||
font_get_max_glyph_advance_range(advance_map, met, 'A', 'F', 0);
|
||||
met->hex_digit_advance =
|
||||
Max(met->hex_digit_advance, met->decimal_digit_advance);
|
||||
met->byte_sub_advances[0] =
|
||||
font_get_glyph_advance(advance_map, met, '\\', 0);
|
||||
met->byte_sub_advances[1] = met->hex_digit_advance;
|
||||
met->byte_sub_advances[2] = met->hex_digit_advance;
|
||||
met->byte_advance =
|
||||
met->byte_sub_advances[0] +
|
||||
met->byte_sub_advances[1] +
|
||||
met->byte_sub_advances[2];
|
||||
met->normal_lowercase_advance =
|
||||
font_get_average_glyph_advance_range(advance_map, met, 'a', 'z', 0);
|
||||
met->normal_uppercase_advance =
|
||||
font_get_average_glyph_advance_range(advance_map, met, 'A', 'Z', 0);
|
||||
met->normal_advance = (26*met->normal_lowercase_advance +
|
||||
26*met->normal_uppercase_advance +
|
||||
10*met->decimal_digit_advance)/62.f;
|
||||
for (u16 i = 0; i < index_count; i += 1){
|
||||
Vec3_i32 p = V3i32((i32)face->bounds[i].uv.x0, (i32)face->bounds[i].uv.y0, (i32)face->bounds[i].w);
|
||||
Vec3_i32 dim = V3i32(glyph_bitmaps[i].dim.x, glyph_bitmaps[i].dim.y, 1);
|
||||
graphics_fill_texture(texture_kind, texture, p, dim, glyph_bitmaps[i].data);
|
||||
face->bounds[i].uv.x1 = (face->bounds[i].uv.x0 + face->bounds[i].uv.x1)/texture_dim.x;
|
||||
face->bounds[i].uv.y1 = (face->bounds[i].uv.y0 + face->bounds[i].uv.y1)/texture_dim.y;
|
||||
face->bounds[i].uv.x0 = face->bounds[i].uv.x0/texture_dim.x;
|
||||
face->bounds[i].uv.y0 = face->bounds[i].uv.y0/texture_dim.y;
|
||||
}
|
||||
|
||||
{
|
||||
Face_Advance_Map *advance_map = &face->advance_map;
|
||||
|
||||
met->space_advance = font_get_glyph_advance(advance_map, met, ' ', 0);
|
||||
met->decimal_digit_advance =
|
||||
font_get_max_glyph_advance_range(advance_map, met, '0', '9', 0);
|
||||
met->hex_digit_advance =
|
||||
font_get_max_glyph_advance_range(advance_map, met, 'A', 'F', 0);
|
||||
met->hex_digit_advance =
|
||||
Max(met->hex_digit_advance, met->decimal_digit_advance);
|
||||
met->byte_sub_advances[0] =
|
||||
font_get_glyph_advance(advance_map, met, '\\', 0);
|
||||
met->byte_sub_advances[1] = met->hex_digit_advance;
|
||||
met->byte_sub_advances[2] = met->hex_digit_advance;
|
||||
met->byte_advance =
|
||||
met->byte_sub_advances[0] +
|
||||
met->byte_sub_advances[1] +
|
||||
met->byte_sub_advances[2];
|
||||
met->normal_lowercase_advance =
|
||||
font_get_average_glyph_advance_range(advance_map, met, 'a', 'z', 0);
|
||||
met->normal_uppercase_advance =
|
||||
font_get_average_glyph_advance_range(advance_map, met, 'A', 'Z', 0);
|
||||
met->normal_advance = (26*met->normal_lowercase_advance +
|
||||
26*met->normal_uppercase_advance +
|
||||
10*met->decimal_digit_advance)/62.f;
|
||||
}
|
||||
|
||||
} else {
|
||||
pop_array(arena, Face, 1);
|
||||
face = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -388,4 +399,3 @@ ft__font_make_face(Arena *arena, Face_Description *description, f32 scale_factor
|
|||
}
|
||||
|
||||
// BOTTOM
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
struct Metal_Buffer{
|
||||
Node node;
|
||||
|
||||
|
||||
id<MTLBuffer> buffer;
|
||||
u32 size;
|
||||
u64 last_reuse_time;
|
||||
|
@ -23,7 +23,7 @@ typedef id<MTLTexture> Metal_Texture;
|
|||
// NOTE(yuval): This is a locator used to describe where a specific slot is located.
|
||||
union Metal_Texture_Slot_Locator{
|
||||
u32 packed;
|
||||
|
||||
|
||||
struct{
|
||||
u16 bucket_index;
|
||||
u16 slot_index;
|
||||
|
@ -34,7 +34,7 @@ union Metal_Texture_Slot_Locator{
|
|||
struct Metal_Texture_Slot{
|
||||
// NOTE(yuval): This is a pointer to the next texture in the free texture slots list
|
||||
Metal_Texture_Slot *next;
|
||||
|
||||
|
||||
Metal_Texture texture;
|
||||
Metal_Texture_Slot_Locator locator;
|
||||
};
|
||||
|
@ -52,12 +52,12 @@ struct Metal_Texture_Slot_List{
|
|||
Metal_Texture_Slot_Bucket *first_bucket;
|
||||
Metal_Texture_Slot_Bucket *last_bucket;
|
||||
u16 bucket_count;
|
||||
|
||||
|
||||
Metal_Texture_Slot *first_free_slot;
|
||||
Metal_Texture_Slot *last_free_slot;
|
||||
};
|
||||
|
||||
global_const u32 metal__invalid_texture_slot_locator = (u32)-1;
|
||||
global_const u32 metal__invalid_texture_slot_locator = 0;
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
|
@ -67,6 +67,7 @@ global_const u32 metal__invalid_texture_slot_locator = (u32)-1;
|
|||
- (u32)get_texture_of_dim:(Vec3_i32)dim kind:(Texture_Kind)kind;
|
||||
- (b32)fill_texture:(u32)texture kind:(Texture_Kind)kind pos:(Vec3_i32)p dim:(Vec3_i32)dim data:(void*)data;
|
||||
- (void)bind_texture:(u32)handle encoder:(id<MTLRenderCommandEncoder>)render_encoder;
|
||||
- (void)free_texture:(u32)handle;
|
||||
- (Metal_Texture_Slot*)get_texture_slot_at_locator:(Metal_Texture_Slot_Locator)locator;
|
||||
- (Metal_Texture_Slot*)get_texture_slot_at_handle:(u32)handle;
|
||||
|
||||
|
@ -175,15 +176,15 @@ return(out_color);
|
|||
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;
|
||||
result->buffer = [device newBufferWithLength:size options:options];
|
||||
result->size = size;
|
||||
|
||||
|
||||
// NOTE(yuval): Set the last_reuse_time to the current time
|
||||
result->last_reuse_time = system_now_time();
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -191,15 +192,15 @@ metal__make_buffer(u32 size, id<MTLDevice> device){
|
|||
|
||||
@implementation Metal_Renderer{
|
||||
Render_Target *_target;
|
||||
|
||||
|
||||
id<MTLDevice> _device;
|
||||
id<MTLRenderPipelineState> _pipeline_state;
|
||||
id<MTLCommandQueue> _command_queue;
|
||||
id<MTLCaptureScope> _capture_scope;
|
||||
|
||||
|
||||
Node _buffer_cache;
|
||||
u64 _last_buffer_cache_purge_time;
|
||||
|
||||
|
||||
Metal_Texture_Slot_List _texture_slots;
|
||||
}
|
||||
|
||||
|
@ -208,33 +209,33 @@ metal__make_buffer(u32 size, id<MTLDevice> device){
|
|||
if (self == nil){
|
||||
return(nil);
|
||||
}
|
||||
|
||||
|
||||
_target = target;
|
||||
|
||||
|
||||
NSError *error = nil;
|
||||
|
||||
|
||||
_device = mtk_view.device;
|
||||
|
||||
|
||||
// NOTE(yuval): Compile the shaders
|
||||
id<MTLFunction> vertex_function = nil;
|
||||
id<MTLFunction> fragment_function = nil;
|
||||
{
|
||||
NSString *shaders_source_str = [NSString stringWithUTF8String:metal__shaders_source];
|
||||
|
||||
|
||||
MTLCompileOptions *options = [[MTLCompileOptions alloc] init];
|
||||
options.fastMathEnabled = YES;
|
||||
|
||||
|
||||
id<MTLLibrary> shader_library = [_device newLibraryWithSource:shaders_source_str
|
||||
options:options error:&error];
|
||||
vertex_function = [shader_library newFunctionWithName:@"vertex_shader"];
|
||||
fragment_function = [shader_library newFunctionWithName:@"fragment_shader"];
|
||||
|
||||
|
||||
[options release];
|
||||
}
|
||||
|
||||
|
||||
Assert(error == nil);
|
||||
Assert((vertex_function != nil) && (fragment_function != nil));
|
||||
|
||||
|
||||
// NOTE(yuval): Configure the pipeline descriptor
|
||||
{
|
||||
MTLVertexDescriptor *vertexDescriptor = [MTLVertexDescriptor vertexDescriptor];
|
||||
|
@ -253,7 +254,7 @@ metal__make_buffer(u32 size, id<MTLDevice> device){
|
|||
vertexDescriptor.layouts[0].stepRate = 1;
|
||||
vertexDescriptor.layouts[0].stepFunction = MTLVertexStepFunctionPerVertex;
|
||||
vertexDescriptor.layouts[0].stride = sizeof(Render_Vertex);
|
||||
|
||||
|
||||
MTLRenderPipelineDescriptor *pipeline_state_descriptor = [[MTLRenderPipelineDescriptor alloc] init];
|
||||
pipeline_state_descriptor.label = @"4coder Metal Renderer Pipeline";
|
||||
pipeline_state_descriptor.vertexFunction = vertex_function;
|
||||
|
@ -267,23 +268,28 @@ metal__make_buffer(u32 size, id<MTLDevice> device){
|
|||
pipeline_state_descriptor.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
|
||||
pipeline_state_descriptor.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorOne;
|
||||
pipeline_state_descriptor.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
|
||||
|
||||
|
||||
_pipeline_state = [_device newRenderPipelineStateWithDescriptor:pipeline_state_descriptor
|
||||
error:&error];
|
||||
}
|
||||
|
||||
|
||||
Assert(error == nil);
|
||||
|
||||
|
||||
// NOTE(yuval): Create the command queue
|
||||
_command_queue = [_device newCommandQueue];
|
||||
|
||||
|
||||
// NOTE(yuval): Initialize buffer caching
|
||||
dll_init_sentinel(&_buffer_cache);
|
||||
_last_buffer_cache_purge_time = system_now_time();
|
||||
|
||||
|
||||
// NOTE(yuval): Initialize the texture slot list
|
||||
block_zero_struct(&_texture_slots);
|
||||
|
||||
|
||||
// NOTE(simon): Other platforms use 0 as invalid handle, so we allocate the first texture here (it should be 0),
|
||||
// and never use it so we can use 0 as the invalid handle.
|
||||
u32 reserved_texture_slot_do_not_use = [self get_texture_of_dim:V3i32(2, 2, 1) kind:TextureKind_Mono];
|
||||
Assert( reserved_texture_slot_do_not_use == 0 );
|
||||
|
||||
// NOTE(yuval): Create the fallback texture
|
||||
_target->fallback_texture_id = [self get_texture_of_dim:V3i32(2, 2, 1)
|
||||
kind:TextureKind_Mono];
|
||||
|
@ -293,12 +299,12 @@ metal__make_buffer(u32 size, id<MTLDevice> device){
|
|||
pos:V3i32(0, 0, 0)
|
||||
dim:V3i32(2, 2, 1)
|
||||
data:white_block];
|
||||
|
||||
|
||||
// NOTE(yuval): Create a capture scope for gpu frame capture
|
||||
_capture_scope = [[MTLCaptureManager sharedCaptureManager]
|
||||
newCaptureScopeWithDevice:_device];
|
||||
_capture_scope.label = @"4coder Metal Capture Scope";
|
||||
|
||||
|
||||
return(self);
|
||||
}
|
||||
|
||||
|
@ -310,14 +316,14 @@ metal__make_buffer(u32 size, id<MTLDevice> device){
|
|||
#if FRED_INTERNAL
|
||||
[_capture_scope beginScope];
|
||||
#endif
|
||||
|
||||
|
||||
// HACK(yuval): This is the best way I found to force valid width and height without drawing on the next draw cycle (1 frame delay).
|
||||
CGSize drawable_size = [view drawableSize];
|
||||
i32 width = (i32)Min(_target->width, drawable_size.width);
|
||||
i32 height = (i32)Min(_target->height, drawable_size.height);
|
||||
|
||||
|
||||
Font_Set *font_set = (Font_Set*)_target->font_set;
|
||||
|
||||
|
||||
// NOTE(yuval): Free any textures in the target's texture free list
|
||||
for (Render_Free_Texture *free_texture = _target->free_texture_first;
|
||||
free_texture;
|
||||
|
@ -329,27 +335,27 @@ metal__make_buffer(u32 size, id<MTLDevice> device){
|
|||
}
|
||||
_target->free_texture_first = 0;
|
||||
_target->free_texture_last = 0;
|
||||
|
||||
|
||||
// NOTE(yuval): Create the command buffer
|
||||
id<MTLCommandBuffer> command_buffer = [_command_queue commandBuffer];
|
||||
command_buffer.label = @"4coder Metal Render Command";
|
||||
|
||||
|
||||
// NOTE(yuval): Obtain the render pass descriptor from the renderer's view
|
||||
MTLRenderPassDescriptor *render_pass_descriptor = view.currentRenderPassDescriptor;
|
||||
if (render_pass_descriptor != nil){
|
||||
render_pass_descriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
|
||||
|
||||
// NOTE(yuval): Create the render command encoder
|
||||
id<MTLRenderCommandEncoder> render_encoder =
|
||||
[command_buffer renderCommandEncoderWithDescriptor:render_pass_descriptor];
|
||||
render_encoder.label = @"4coder Render Encoder";
|
||||
|
||||
|
||||
// NOTE(yuval): Set the region of the drawable to draw into
|
||||
[render_encoder setViewport:(MTLViewport){0.0, 0.0, (double)width, (double)height, 0.0, 1.0}];
|
||||
|
||||
|
||||
// NOTE(yuval): Set the render pipeline to use for drawing
|
||||
[render_encoder setRenderPipelineState:_pipeline_state];
|
||||
|
||||
|
||||
// NOTE(yuval): Calculate the projection matrix
|
||||
float left = 0, right = (float)width;
|
||||
float bottom = (float)height, top = 0;
|
||||
|
@ -361,7 +367,7 @@ metal__make_buffer(u32 size, id<MTLDevice> device){
|
|||
-((right + left) / (right - left)), -((top + bottom) / (top - bottom)),
|
||||
-(near_depth / (far_depth - near_depth)), 1.0f
|
||||
};
|
||||
|
||||
|
||||
// NOTE(yuval): Calculate required vertex buffer size
|
||||
i32 all_vertex_count = 0;
|
||||
for (Render_Group *group = _target->group_first;
|
||||
|
@ -369,22 +375,22 @@ metal__make_buffer(u32 size, id<MTLDevice> device){
|
|||
group = group->next){
|
||||
all_vertex_count += group->vertex_list.vertex_count;
|
||||
}
|
||||
|
||||
|
||||
u32 vertex_buffer_size = (all_vertex_count * sizeof(Render_Vertex));
|
||||
|
||||
|
||||
// NOTE(yuval): Find & Get a vertex buffer matching the required size
|
||||
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
|
||||
offset:0
|
||||
atIndex:0];
|
||||
|
||||
|
||||
// NOTE(yuval): Pass the projection matrix to the vertex shader
|
||||
[render_encoder setVertexBytes:&proj
|
||||
length:sizeof(proj)
|
||||
atIndex:1];
|
||||
|
||||
|
||||
u32 buffer_offset = 0;
|
||||
for (Render_Group *group = _target->group_first;
|
||||
group;
|
||||
|
@ -392,21 +398,21 @@ metal__make_buffer(u32 size, id<MTLDevice> device){
|
|||
// NOTE(yuval): Set scissor rect
|
||||
{
|
||||
Rect_i32 box = Ri32(group->clip_box);
|
||||
|
||||
|
||||
NSUInteger x0 = (NSUInteger)Min(Max(0, box.x0), width - 1);
|
||||
NSUInteger x1 = (NSUInteger)Min(Max(0, box.x1), width);
|
||||
NSUInteger y0 = (NSUInteger)Min(Max(0, box.y0), height - 1);
|
||||
NSUInteger y1 = (NSUInteger)Min(Max(0, box.y1), height);
|
||||
|
||||
|
||||
MTLScissorRect scissor_rect;
|
||||
scissor_rect.x = x0;
|
||||
scissor_rect.y = y0;
|
||||
scissor_rect.width = (x1 - x0);
|
||||
scissor_rect.height = (y1 - y0);
|
||||
|
||||
|
||||
[render_encoder setScissorRect:scissor_rect];
|
||||
}
|
||||
|
||||
|
||||
i32 vertex_count = group->vertex_list.vertex_count;
|
||||
if (vertex_count > 0){
|
||||
// NOTE(yuval): Bind a texture
|
||||
|
@ -422,10 +428,10 @@ metal__make_buffer(u32 size, id<MTLDevice> device){
|
|||
encoder:render_encoder];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// NOTE(yuval): Copy the vertex data to the vertex buffer
|
||||
{
|
||||
|
||||
|
||||
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;
|
||||
|
@ -435,39 +441,39 @@ metal__make_buffer(u32 size, id<MTLDevice> device){
|
|||
memcpy(cursor, node->vertices, size);
|
||||
cursor += size;
|
||||
}
|
||||
|
||||
|
||||
NSUInteger data_size = (NSUInteger)(cursor - group_buffer_contents);
|
||||
NSRange modify_range = NSMakeRange(buffer_offset, data_size);
|
||||
[buffer->buffer didModifyRange:modify_range];
|
||||
}
|
||||
|
||||
|
||||
// NOTE(yuval): Set the vertex buffer offset to the beginning of the group's vertices
|
||||
[render_encoder setVertexBufferOffset:buffer_offset atIndex:0];
|
||||
|
||||
|
||||
// NOTE(yuval): Draw the vertices
|
||||
[render_encoder drawPrimitives:MTLPrimitiveTypeTriangle
|
||||
vertexStart:0
|
||||
vertexCount:vertex_count];
|
||||
|
||||
|
||||
buffer_offset += (vertex_count * sizeof(Render_Vertex));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[render_encoder endEncoding];
|
||||
|
||||
|
||||
// NOTE(yuval): Schedule a present once the framebuffer is complete using the current drawable
|
||||
[command_buffer presentDrawable:view.currentDrawable];
|
||||
|
||||
|
||||
[command_buffer addCompletedHandler:^(id<MTLCommandBuffer>){
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self add_reusable_buffer:buffer];
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
// NOTE(yuval): Finalize rendering here and push the command buffer to the GPU
|
||||
[command_buffer commit];
|
||||
|
||||
|
||||
#if FRED_INTERNAL
|
||||
[_capture_scope endScope];
|
||||
#endif
|
||||
|
@ -475,14 +481,14 @@ metal__make_buffer(u32 size, id<MTLDevice> device){
|
|||
|
||||
- (u32)get_texture_of_dim:(Vec3_i32)dim kind:(Texture_Kind)kind{
|
||||
u32 handle = metal__invalid_texture_slot_locator;
|
||||
|
||||
|
||||
// NOTE(yuval): Check for a free texture slot and allocate another slot bucket if no free slot has been found
|
||||
if (!_texture_slots.first_free_slot){
|
||||
// NOTE(yuval): Assert that the next bucket's index can fit in a u16
|
||||
Assert(_texture_slots.bucket_count < ((u16)-1));
|
||||
|
||||
|
||||
Metal_Texture_Slot_Bucket *bucket = (Metal_Texture_Slot_Bucket*)system_memory_allocate(sizeof(Metal_Texture_Slot_Bucket), file_name_line_number_lit_u8);
|
||||
|
||||
|
||||
for (u16 slot_index = 0;
|
||||
slot_index < ArrayCount(bucket->slots);
|
||||
++slot_index){
|
||||
|
@ -490,20 +496,20 @@ metal__make_buffer(u32 size, id<MTLDevice> device){
|
|||
block_zero_struct(slot);
|
||||
slot->locator.bucket_index = _texture_slots.bucket_count;
|
||||
slot->locator.slot_index = slot_index;
|
||||
|
||||
|
||||
sll_queue_push(_texture_slots.first_free_slot, _texture_slots.last_free_slot, slot);
|
||||
}
|
||||
|
||||
|
||||
sll_queue_push(_texture_slots.first_bucket, _texture_slots.last_bucket, bucket);
|
||||
_texture_slots.bucket_count += 1;
|
||||
}
|
||||
|
||||
|
||||
// NOTE(yuval): Get the first free texture slot and remove it from the free list (a slot is guarenteed to exist because we assert that above).
|
||||
if (_texture_slots.first_free_slot){
|
||||
Metal_Texture_Slot *texture_slot = _texture_slots.first_free_slot;
|
||||
sll_queue_pop(_texture_slots.first_free_slot, _texture_slots.last_free_slot);
|
||||
texture_slot->next = 0;
|
||||
|
||||
|
||||
// NOTE(yuval): Create a texture descriptor.
|
||||
MTLTextureDescriptor *texture_descriptor = [[MTLTextureDescriptor alloc] init];
|
||||
texture_descriptor.textureType = MTLTextureType2DArray;
|
||||
|
@ -515,21 +521,21 @@ metal__make_buffer(u32 size, id<MTLDevice> device){
|
|||
// NOTE(yuval): Create the texture from the device using the descriptor and add it to the textures array.
|
||||
Metal_Texture texture = [_device newTextureWithDescriptor:texture_descriptor];
|
||||
texture_slot->texture = texture;
|
||||
|
||||
|
||||
handle = texture_slot->locator.packed;
|
||||
}
|
||||
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
- (b32)fill_texture:(u32)handle kind:(Texture_Kind)kind pos:(Vec3_i32)p dim:(Vec3_i32)dim data:(void*)data{
|
||||
b32 result = false;
|
||||
|
||||
|
||||
if (data){
|
||||
Metal_Texture_Slot *texture_slot = [self get_texture_slot_at_handle:handle];
|
||||
if (texture_slot){
|
||||
Metal_Texture texture = texture_slot->texture;
|
||||
|
||||
|
||||
if (texture != 0){
|
||||
// https://developer.apple.com/documentation/metal/mtlregion
|
||||
// for 2d texture origin.z is 0, and depth is 1
|
||||
|
@ -537,7 +543,7 @@ metal__make_buffer(u32 size, id<MTLDevice> device){
|
|||
{(NSUInteger)p.x, (NSUInteger)p.y, 0},
|
||||
{(NSUInteger)dim.x, (NSUInteger)dim.y, 1}
|
||||
};
|
||||
|
||||
|
||||
// NOTE(yuval): Fill the texture with data
|
||||
[texture replaceRegion:replace_region
|
||||
mipmapLevel:0
|
||||
|
@ -550,7 +556,7 @@ metal__make_buffer(u32 size, id<MTLDevice> device){
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
|
@ -565,41 +571,49 @@ metal__make_buffer(u32 size, id<MTLDevice> device){
|
|||
}
|
||||
}
|
||||
|
||||
- (void)free_texture:(u32)handle{
|
||||
Metal_Texture_Slot *texture_slot = [self get_texture_slot_at_handle:handle];
|
||||
if (texture_slot){
|
||||
sll_queue_push(_texture_slots.first_free_slot, _texture_slots.last_free_slot, texture_slot);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (Metal_Texture_Slot*)get_texture_slot_at_locator:(Metal_Texture_Slot_Locator)locator{
|
||||
Metal_Texture_Slot *result = 0;
|
||||
|
||||
|
||||
if (locator.packed != metal__invalid_texture_slot_locator){
|
||||
Metal_Texture_Slot_Bucket *bucket = _texture_slots.first_bucket;
|
||||
for (u16 bucket_index = 0;
|
||||
(bucket_index < locator.bucket_index) && bucket;
|
||||
++bucket_index, bucket = bucket->next);
|
||||
|
||||
|
||||
if (bucket && (locator.slot_index < metal__texture_slots_per_bucket)){
|
||||
result = &bucket->slots[locator.slot_index];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
- (Metal_Texture_Slot*)get_texture_slot_at_handle:(u32)handle{
|
||||
Metal_Texture_Slot_Locator locator;
|
||||
locator.packed = handle;
|
||||
|
||||
|
||||
Metal_Texture_Slot *result = [self get_texture_slot_at_locator:locator];
|
||||
return(result);
|
||||
}
|
||||
|
||||
- (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
|
||||
|
||||
|
||||
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) > 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){
|
||||
|
@ -608,10 +622,10 @@ metal__make_buffer(u32 size, id<MTLDevice> device){
|
|||
dll_insert(&_buffer_cache, node);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_last_buffer_cache_purge_time = now;
|
||||
}
|
||||
|
||||
|
||||
// NOTE(yuval): See if we have a buffer we can reuse
|
||||
Metal_Buffer *best_candidate = 0;
|
||||
for (Node *node = _buffer_cache.next;
|
||||
|
@ -622,7 +636,7 @@ metal__make_buffer(u32 size, id<MTLDevice> device){
|
|||
best_candidate = candidate;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Metal_Buffer *result;
|
||||
if (best_candidate){
|
||||
// NOTE(yuval): A best candidate has been found! Remove it from the buffer list and set its last reuse time.
|
||||
|
@ -633,13 +647,13 @@ metal__make_buffer(u32 size, id<MTLDevice> device){
|
|||
// NOTE(yuval): No luck; make a new buffer.
|
||||
result = metal__make_buffer(size, _device);
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
dll_insert(&_buffer_cache, &buffer->node);
|
||||
}
|
||||
@end
|
||||
@end
|
Loading…
Reference in New Issue