Compare commits
2 Commits
6b94f0dbde
...
e941db978c
Author | SHA1 | Date |
---|---|---|
|
e941db978c | |
|
6e0d5d58d2 |
|
@ -44,6 +44,7 @@ vs_main(
|
|||
float2 scaled_size = ((vertices[vertex_id].position + 1.0) / 2.0) * (glyph.size/2.0);
|
||||
float2 scaled_size_2 = ((vertices[vertex_id].position + 1.0) / 2.0) * (glyph.size);
|
||||
float2 glyph_pos = scaled_size + glyph.target_position + float2(0, glyph.y_offset/2.0+32);
|
||||
|
||||
float4 device_position = to_device_position(glyph_pos, params.screen_size);
|
||||
float2 atlas_position = (scaled_size_2 + glyph.atlas_position) / 1024.0;
|
||||
|
||||
|
@ -64,3 +65,69 @@ fragment float4 fs_main(VertexOutput in [[stage_in]],
|
|||
return float4(text_color * float3(1,1,1), text_color);
|
||||
}
|
||||
|
||||
struct UiRect {
|
||||
float2 position;
|
||||
float2 size;
|
||||
float2 border_size;
|
||||
};
|
||||
|
||||
struct UiRectFragment {
|
||||
float4 device_position [[position]];
|
||||
float2 position;
|
||||
float2 size;
|
||||
float2 border_size;
|
||||
float2 screen_size;
|
||||
float2 tex_coord;
|
||||
};
|
||||
|
||||
vertex UiRectFragment
|
||||
ui_rect_vs(
|
||||
uint vertex_id [[vertex_id]],
|
||||
uint rect_id [[instance_id]],
|
||||
constant VertexInput *vertices [[buffer(0)]],
|
||||
constant UiRect *rects [[buffer(1)]],
|
||||
constant UniformParams ¶ms [[buffer(2)]]
|
||||
)
|
||||
{
|
||||
UiRect rect = rects[rect_id];
|
||||
|
||||
return UiRectFragment {
|
||||
float4(vertices[vertex_id].position, 1, 1),
|
||||
rect.position,
|
||||
rect.size,
|
||||
rect.border_size,
|
||||
params.screen_size,
|
||||
vertices[vertex_id].tex_coord,
|
||||
};
|
||||
}
|
||||
|
||||
float rect_sdf(
|
||||
float2 absolute_pixel_position,
|
||||
float2 origin,
|
||||
float2 size,
|
||||
float corner_radius
|
||||
) {
|
||||
float2 half_size = size / 2;
|
||||
float2 rect_center = origin + half_size;
|
||||
|
||||
float2 pixel_position = abs(absolute_pixel_position - rect_center);
|
||||
float2 shrunk_corner_position = half_size - corner_radius;
|
||||
|
||||
float2 pixel_to_shrunk_corner = max(float2(0), pixel_position - shrunk_corner_position);
|
||||
float distance_to_shrunk_corner = length(pixel_to_shrunk_corner);
|
||||
float distance = distance_to_shrunk_corner - corner_radius;
|
||||
|
||||
return distance;
|
||||
}
|
||||
|
||||
fragment float4 ui_rect_fs(UiRectFragment in [[stage_in]])
|
||||
{
|
||||
float2 pixel_pos = in.tex_coord.xy * in.screen_size;
|
||||
|
||||
float distance = rect_sdf(pixel_pos, in.position, in.size, in.border_size.x);
|
||||
if (distance <= 0.0) {
|
||||
return float4(1,1,1,0.5);
|
||||
} else {
|
||||
return float4(0);
|
||||
}
|
||||
}
|
||||
|
|
56
src/gfx.h
56
src/gfx.h
|
@ -92,7 +92,9 @@ static void _metal_gfx_send_events(_metal_gfx_context *cx);
|
|||
|
||||
_gfx_context.frame_width = _gfx_context.backend.window.contentView.frame.size.width;
|
||||
_gfx_context.frame_height = _gfx_context.backend.window.contentView.frame.size.height;
|
||||
[_gfx_context.backend.metal_layer setDrawableSize:CGSizeMake(_gfx_context.frame_width, _gfx_context.frame_height)];
|
||||
|
||||
CGFloat scale = _gfx_context.backend.metal_layer.contentsScale;
|
||||
[_gfx_context.backend.metal_layer setDrawableSize:CGSizeMake(_gfx_context.frame_width * scale, _gfx_context.frame_height * scale)];
|
||||
|
||||
_metal_gfx_present(&_gfx_context.backend);
|
||||
}
|
||||
|
@ -161,11 +163,13 @@ static _metal_gfx_context _metal_gfx_init_context(uint32_t width, uint32_t heigh
|
|||
metal_layer.needsDisplayOnBoundsChange = YES;
|
||||
metal_layer.presentsWithTransaction = YES;
|
||||
metal_layer.autoresizingMask = kCALayerWidthSizable|kCALayerHeightSizable;
|
||||
|
||||
// TODO: set this to the display dpi scale
|
||||
metal_layer.contentsScale = 2.0;
|
||||
view.wantsLayer = YES;
|
||||
[view.layer addSublayer:metal_layer];
|
||||
view.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay;
|
||||
|
||||
|
||||
NSError *libraryError = NULL;
|
||||
NSURL *libraryURL = [[NSBundle mainBundle] URLForResource:@"./shaders" withExtension:@"metallib"];
|
||||
if (libraryURL == NULL) {
|
||||
|
@ -186,15 +190,42 @@ static _metal_gfx_context _metal_gfx_init_context(uint32_t width, uint32_t heigh
|
|||
id<MTLCommandQueue> command_queue = [device newCommandQueue];
|
||||
id<MTLFunction> vertex_func = [library newFunctionWithName:@"vs_main"];
|
||||
id<MTLFunction> fragment_func = [library newFunctionWithName:@"fs_main"];
|
||||
id<MTLFunction> ui_rect_vertex_func = [library newFunctionWithName:@"ui_rect_vs"];
|
||||
id<MTLFunction> ui_rect_fragment_func = [library newFunctionWithName:@"ui_rect_fs"];
|
||||
|
||||
MTLRenderPipelineDescriptor *pipeline_descriptor = [[MTLRenderPipelineDescriptor alloc] init];
|
||||
[pipeline_descriptor setVertexFunction:vertex_func];
|
||||
[pipeline_descriptor setFragmentFunction:fragment_func];
|
||||
pipeline_descriptor.colorAttachments[0].pixelFormat = MTLPixelFormatRGBA8Unorm;
|
||||
pipeline_descriptor.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd;
|
||||
pipeline_descriptor.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha;
|
||||
pipeline_descriptor.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
|
||||
pipeline_descriptor.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
|
||||
pipeline_descriptor.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
|
||||
pipeline_descriptor.colorAttachments[0].blendingEnabled = true;
|
||||
|
||||
MTLRenderPipelineDescriptor *ui_rect_pipeline_descriptor = [[MTLRenderPipelineDescriptor alloc] init];
|
||||
[ui_rect_pipeline_descriptor setVertexFunction:ui_rect_vertex_func];
|
||||
[ui_rect_pipeline_descriptor setFragmentFunction:ui_rect_fragment_func];
|
||||
ui_rect_pipeline_descriptor.colorAttachments[0].pixelFormat = MTLPixelFormatRGBA8Unorm;
|
||||
ui_rect_pipeline_descriptor.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd;
|
||||
ui_rect_pipeline_descriptor.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha;
|
||||
ui_rect_pipeline_descriptor.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
|
||||
ui_rect_pipeline_descriptor.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
|
||||
ui_rect_pipeline_descriptor.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
|
||||
ui_rect_pipeline_descriptor.colorAttachments[0].blendingEnabled = true;
|
||||
|
||||
NSError *pipeline_error = NULL;
|
||||
array(_MTLRenderPipelineState) pipelines = newArray(_MTLRenderPipelineState, 2);
|
||||
pushArray(_MTLRenderPipelineState, &pipelines, [device newRenderPipelineStateWithDescriptor:pipeline_descriptor error:&pipeline_error]);
|
||||
if (pipeline_error != NULL) {
|
||||
if (pipeline_error.description != NULL) {
|
||||
NSLog(@"Error description: %@\n", pipeline_error.description);
|
||||
}
|
||||
|
||||
exit(1);
|
||||
}
|
||||
pushArray(_MTLRenderPipelineState, &pipelines, [device newRenderPipelineStateWithDescriptor:ui_rect_pipeline_descriptor error:&pipeline_error]);
|
||||
|
||||
array(_MTLBuffer) buffers = newArray(_MTLBuffer, 8);
|
||||
array(_MTLTexture) textures = newArray(_MTLTexture, 8);
|
||||
|
@ -244,16 +275,27 @@ void _metal_gfx_present(_metal_gfx_context *cx) {
|
|||
render_pass_desc.colorAttachments[0].storeAction = MTLStoreActionStore;
|
||||
|
||||
id<MTLRenderCommandEncoder> encoder = [command_buffer renderCommandEncoderWithDescriptor:render_pass_desc];
|
||||
|
||||
// UI Text
|
||||
[encoder setRenderPipelineState:cx->pipelines.data[0]];
|
||||
|
||||
// FIXME: allow these to be described by the user instead of hardcoded
|
||||
[encoder setVertexBuffer:cx->buffers.data[0] offset:0 atIndex:0];
|
||||
[encoder setVertexBuffer:cx->buffers.data[2] offset:0 atIndex:1];
|
||||
[encoder setVertexBuffer:cx->buffers.data[3] offset:0 atIndex:2];
|
||||
[encoder setVertexBuffer:cx->buffers.data[0] offset:0 atIndex:0]; // vertices
|
||||
[encoder setVertexBuffer:cx->buffers.data[2] offset:0 atIndex:1]; // glyph data
|
||||
[encoder setVertexBuffer:cx->buffers.data[3] offset:0 atIndex:2]; // uniforms
|
||||
[encoder setFragmentTexture:cx->textures.data[0] atIndex:0];
|
||||
// TODO: get instance count properly
|
||||
[encoder drawIndexedPrimitives:MTLPrimitiveTypeTriangle indexCount:6 indexType:MTLIndexTypeUInt16 indexBuffer:cx->buffers.data[1] indexBufferOffset:0 instanceCount:36];
|
||||
[encoder endEncoding];
|
||||
|
||||
// UI Rects
|
||||
[encoder setRenderPipelineState:cx->pipelines.data[1]];
|
||||
// FIXME: allow these to be described by the user instead of hardcoded
|
||||
[encoder setVertexBuffer:cx->buffers.data[0] offset:0 atIndex:0]; // vertices
|
||||
[encoder setVertexBuffer:cx->buffers.data[4] offset:0 atIndex:1]; // ui rects
|
||||
[encoder setVertexBuffer:cx->buffers.data[3] offset:0 atIndex:2]; // uniforms
|
||||
// TODO: get instance count properly
|
||||
[encoder drawIndexedPrimitives:MTLPrimitiveTypeTriangle indexCount:6 indexType:MTLIndexTypeUInt16 indexBuffer:cx->buffers.data[1] indexBufferOffset:0 instanceCount:3];
|
||||
|
||||
[encoder endEncoding];
|
||||
[command_buffer presentDrawable:drawable];
|
||||
[command_buffer commit];
|
||||
|
||||
|
|
23
src/main.c
23
src/main.c
|
@ -73,6 +73,16 @@ void queue_text(string text, float position[2]) {
|
|||
}
|
||||
}
|
||||
|
||||
void queue_ui_rect(uint32_t position[2], uint32_t size[2], uint32_t border_size) {
|
||||
GpuUiRect rect = (GpuUiRect) {
|
||||
.position = { (float)position[0], (float)position[1] },
|
||||
.size = { (float)size[0], (float)size[1] },
|
||||
.border_size = { (float)border_size, (float)border_size },
|
||||
};
|
||||
|
||||
pushArray(GpuUiRect, &state.gpu_ui_rects, rect);
|
||||
}
|
||||
|
||||
void ed_init(_gfx_frame_func frame_func) {
|
||||
state.gfx_cx = gfx_init_context(frame_func, 640, 480);
|
||||
state.ui_cx = ui_init_context();
|
||||
|
@ -96,6 +106,7 @@ void ed_init(_gfx_frame_func frame_func) {
|
|||
|
||||
gfx_allocate_vertex_buffer(state.gfx_cx, state.gpu_glyphs.capacity * sizeof(GpuGlyph));
|
||||
gfx_allocate_vertex_buffer(state.gfx_cx, sizeof(GpuUniformParams));
|
||||
gfx_allocate_vertex_buffer(state.gfx_cx, state.gpu_ui_rects.capacity * sizeof(GpuUiRect));
|
||||
|
||||
uint8_t ttf_buffer[1<<20];
|
||||
// TODO: grab default font from the system
|
||||
|
@ -126,7 +137,7 @@ void ed_init(_gfx_frame_func frame_func) {
|
|||
// manually add glyph for SPACE
|
||||
pushArray(GpuGlyph, &state.glyph_cache, ((GpuGlyph){
|
||||
.atlas_position = { 0 },
|
||||
.size = { rasterized_font_height/4, rasterized_font_height },
|
||||
.size = { rasterized_font_height/4, 1 },
|
||||
.position = { 0 },
|
||||
.y_offset = -rasterized_font_height,
|
||||
}));
|
||||
|
@ -178,11 +189,15 @@ void ed_frame() {
|
|||
ui_compute_layout(&state.ui_cx, 0);
|
||||
|
||||
state.gpu_glyphs.size = 0;
|
||||
for (size_t i = 0; i < state.ui_cx.frame_elements.size; ++i) {
|
||||
state.gpu_ui_rects.size = 0;
|
||||
for (size_t i = 1; i < state.ui_cx.frame_elements.size; ++i) {
|
||||
string text = state.ui_cx.frame_elements.data[i].key;
|
||||
ui_element_frame_data *elm = &state.ui_cx.frame_elements.data[i];
|
||||
|
||||
queue_text(text, (float[]){ (float)elm->size.computed_pos[0], (float)elm->size.computed_pos[1] });
|
||||
|
||||
fprintf(stderr, "size[0]: %d, size[1]: %d\n", elm->size.computed_size[0], elm->size.computed_size[1]);
|
||||
queue_ui_rect(elm->size.computed_pos, elm->size.computed_size, 16);
|
||||
}
|
||||
|
||||
ui_update_cache(&state.ui_cx, 0);
|
||||
|
@ -193,6 +208,10 @@ void ed_frame() {
|
|||
fprintf(stderr, "updated glyph buffer: %zu\n", state.gpu_glyphs.size);
|
||||
}
|
||||
|
||||
if (state.gpu_ui_rects.size > 0) {
|
||||
gfx_update_buffer(state.gfx_cx, 4, state.gpu_ui_rects.data, state.gpu_ui_rects.size * sizeof(GpuUiRect));
|
||||
}
|
||||
|
||||
GpuUniformParams gpu_uniform_params = {
|
||||
.screen_size = {
|
||||
(float)state.gfx_cx->frame_width,
|
||||
|
|
27
src/ui.h
27
src/ui.h
|
@ -155,7 +155,7 @@ size_t ui_element(ui_context *cx, string label) {
|
|||
.prev = cx->frame_elements.data[cx->current_parent].last,
|
||||
.parent = cx->current_parent,
|
||||
.size.semantic_size[0].type = UI_SEMANTIC_SIZE_FILL,
|
||||
.size.semantic_size[1].type = UI_SEMANTIC_SIZE_FILL,
|
||||
.size.semantic_size[1].type = UI_SEMANTIC_SIZE_FIT_TEXT,
|
||||
};
|
||||
|
||||
// Get cached element data
|
||||
|
@ -210,7 +210,7 @@ static void _ui_compute_simple_layout(ui_context *cx, ui_element_frame_data *elm
|
|||
if (axis == UI_AXIS_HORIZONTAL) {
|
||||
elm->size.computed_size[axis] = elm->label.len * _FONT_WIDTH;
|
||||
} else if (axis == UI_AXIS_VERTICAL) {
|
||||
elm->size.computed_size[axis] = _FONT_WIDTH;
|
||||
elm->size.computed_size[axis] = _FONT_HEIGHT;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -236,13 +236,13 @@ static void _ui_compute_simple_layout(ui_context *cx, ui_element_frame_data *elm
|
|||
}
|
||||
|
||||
static void _ui_compute_children_layout(ui_context *cx, ui_element_frame_data *elm) {
|
||||
uint32_t child_size[2] = { 0 };
|
||||
uint32_t child_size[2] = { 0, 0 };
|
||||
|
||||
// NOTE: the number of fills for the opposite axis of this box needs to be 1
|
||||
// because it will never get incremented in the loop below and cause a divide by zero
|
||||
// and the number of fills for the axis of the box needs to start at zero or else it will
|
||||
// be n+1 causing incorrect sizes
|
||||
uint32_t num_fills[2] = { 1 };
|
||||
uint32_t num_fills[2] = { 1, 1 };
|
||||
num_fills[elm->size.axis] = 0;
|
||||
|
||||
// TODO: maybe just use the actual data instead of copying?
|
||||
|
@ -302,7 +302,9 @@ void ui_compute_layout(ui_context *cx, size_t element_index) {
|
|||
bool post_compute[2] = { false, false };
|
||||
// only compute layout for children of root
|
||||
if (elm->index > 0) {
|
||||
_ui_compute_simple_layout(cx, elm, axis, post_compute);
|
||||
for (int i=0; i<2; ++i) {
|
||||
_ui_compute_simple_layout(cx, elm, i, post_compute);
|
||||
}
|
||||
}
|
||||
_ui_compute_children_layout(cx, elm);
|
||||
|
||||
|
@ -382,14 +384,6 @@ void ui_update_cache(ui_context *cx, size_t element_index) {
|
|||
ui_update_cache(cx, child_index);
|
||||
} while ((child_index = _next(child_index)) < SIZE_MAX);
|
||||
}
|
||||
|
||||
cx->frame_elements.size = 1;
|
||||
cx->frame_elements.data[0].first = SIZE_MAX;
|
||||
cx->frame_elements.data[0].prev = SIZE_MAX;
|
||||
cx->frame_elements.data[0].next = SIZE_MAX;
|
||||
cx->frame_elements.data[0].last = SIZE_MAX;
|
||||
cx->frame_elements.data[0].parent = SIZE_MAX;
|
||||
cx->current_parent = 0;
|
||||
}
|
||||
|
||||
void ui_prune(ui_context *cx) {
|
||||
|
@ -408,6 +402,13 @@ void ui_prune(ui_context *cx) {
|
|||
}
|
||||
|
||||
cx->frame_index += 1;
|
||||
cx->frame_elements.size = 1;
|
||||
cx->frame_elements.data[0].first = SIZE_MAX;
|
||||
cx->frame_elements.data[0].prev = SIZE_MAX;
|
||||
cx->frame_elements.data[0].next = SIZE_MAX;
|
||||
cx->frame_elements.data[0].last = SIZE_MAX;
|
||||
cx->frame_elements.data[0].parent = SIZE_MAX;
|
||||
cx->current_parent = 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue