Compare commits
	
		
			3 Commits 
		
	
	
		
			2942efe6e2
			...
			fb60bd5f32
		
	
	| Author | SHA1 | Date | 
|---|---|---|
|  | fb60bd5f32 | |
|  | 89f0a15ade | |
|  | 85df8265b0 | 
							
								
								
									
										2
									
								
								justfile
								
								
								
								
							
							
						
						
									
										2
									
								
								justfile
								
								
								
								
							|  | @ -4,7 +4,7 @@ alias r := run | |||
| c_flags := if os() == "macos" { | ||||
|     "$(curl-config --libs) -framework Cocoa -framework QuartzCore -framework CoreImage -framework Metal -framework MetalKit -ObjC" | ||||
| } else if os_family() == "unix" { | ||||
|     "$(curl-config --libs) -lEGL -lGLESv2 -lGL -lm -lwayland-client -lwayland-egl -lX11 -lXi -lXcursor" | ||||
|     "$(curl-config --libs) -lEGL -lGLESv2 -lGL -lm -lwayland-client -lwayland-egl -lX11 -lXi -lXcursor -Wno-implicit-function-declaration" | ||||
| } else { "" } | ||||
| 
 | ||||
| [macos] | ||||
|  |  | |||
|  | @ -0,0 +1,18 @@ | |||
| #version 440 core | ||||
| 
 | ||||
| struct VertexOutput { | ||||
|     vec4 position; | ||||
|     vec2 tex_coord; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| layout(location = 0) in VertexOutput out_vertex; | ||||
| layout(location = 1) uniform highp sampler2D atlas_texture; | ||||
| 
 | ||||
| out vec4 color; | ||||
| 
 | ||||
| void main() { | ||||
|     float text_color = texture(atlas_texture, out_vertex.tex_coord).r; | ||||
| 
 | ||||
|     color = vec4(text_color * vec3(1,1,1), text_color); | ||||
| } | ||||
|  | @ -0,0 +1,55 @@ | |||
| #version 440 core | ||||
| 
 | ||||
| struct Vertex { | ||||
|     vec2 position; | ||||
|     vec2 tex_coord; | ||||
| }; | ||||
| 
 | ||||
| struct VertexOutput { | ||||
|     vec4 position; | ||||
|     vec2 tex_coord; | ||||
| }; | ||||
| 
 | ||||
| struct Glyph { | ||||
|     vec2 atlas_position; | ||||
|     vec2 size; | ||||
|     vec2 target_position; | ||||
|     float y_offset; | ||||
|     float _haha_alignment; | ||||
| }; | ||||
| 
 | ||||
| layout(std430, binding = 0) readonly buffer VertexBlock { | ||||
|     Vertex vertices[]; | ||||
| }; | ||||
| 
 | ||||
| layout(std430, binding = 1) readonly buffer GlyphBlock { | ||||
|     Glyph glyphs[]; | ||||
| }; | ||||
| 
 | ||||
| layout(std430, binding = 2) readonly buffer ParamsBlock { | ||||
|     vec2 screen_size; | ||||
| }; | ||||
| 
 | ||||
| vec4 to_device_position(vec2 position, vec2 size) { | ||||
|     return vec4(((position / size) * 2.0) - vec2(1.0), 1.0, 1.0); | ||||
| } | ||||
| 
 | ||||
| layout(location = 0) out VertexOutput out_vertex; | ||||
| 
 | ||||
| void main() { | ||||
|     Glyph glyph = glyphs[gl_InstanceID]; | ||||
| 
 | ||||
|     vec2 scaled_size = ((vertices[gl_VertexID].position + 1.0) / 2.0) * (glyph.size/2.0); | ||||
|     vec2 scaled_size_2 = ((vertices[gl_VertexID].position + 1.0) / 2.0) * (glyph.size); | ||||
|     vec2 glyph_pos = scaled_size + glyph.target_position + vec2(0, glyph.y_offset/2.0+24); | ||||
| 
 | ||||
|     vec4 device_position = to_device_position(glyph_pos, screen_size); | ||||
|     vec2 atlas_position = (scaled_size_2 + glyph.atlas_position) / 512.0; | ||||
| 
 | ||||
|     device_position.y = -device_position.y; | ||||
| 
 | ||||
|     out_vertex.position = device_position; | ||||
|     out_vertex.tex_coord = atlas_position; | ||||
| 
 | ||||
|     gl_Position = device_position; | ||||
| } | ||||
|  | @ -0,0 +1,45 @@ | |||
| #version 440 core | ||||
| 
 | ||||
| struct UiRectFragment { | ||||
|     highp vec4 device_position; | ||||
|     highp vec2 position; | ||||
|     highp vec2 size; | ||||
|     highp vec2 border_size; | ||||
|     highp vec2 screen_size; | ||||
|     highp vec2 tex_coord; | ||||
|     highp vec4 color; | ||||
| }; | ||||
| 
 | ||||
| in UiRectFragment out_rect; | ||||
| 
 | ||||
| layout(location = 0) out highp vec4 color; | ||||
| 
 | ||||
| highp float rect_sdf( | ||||
|     highp vec2 absolute_pixel_position, | ||||
|     highp vec2 origin, | ||||
|     highp vec2 size, | ||||
|     highp float corner_radius | ||||
| ) { | ||||
|     highp vec2 half_size = size / 2.0; | ||||
|     highp vec2 rect_center = origin + half_size; | ||||
| 
 | ||||
|     highp vec2 pixel_position = abs(absolute_pixel_position - rect_center); | ||||
|     highp vec2 shrunk_corner_position = half_size - corner_radius; | ||||
| 
 | ||||
|     highp vec2 pixel_to_shrunk_corner = max(vec2(0), pixel_position - shrunk_corner_position); | ||||
|     highp float distance_to_shrunk_corner = length(pixel_to_shrunk_corner); | ||||
|     highp float distance = distance_to_shrunk_corner - corner_radius; | ||||
| 
 | ||||
|     return distance; | ||||
| } | ||||
| 
 | ||||
| void main() { | ||||
|     highp vec2 pixel_pos = out_rect.tex_coord.xy * out_rect.screen_size; | ||||
| 
 | ||||
|     highp float distance = rect_sdf(pixel_pos, out_rect.position, out_rect.size, out_rect.border_size.x); | ||||
|     if (distance <= 0.0) { | ||||
|         color = out_rect.color; | ||||
|     } else { | ||||
|         color = vec4(0); | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,50 @@ | |||
| #version 440 core | ||||
| 
 | ||||
| struct Vertex { | ||||
|     vec2 position; | ||||
|     vec2 tex_coord; | ||||
| }; | ||||
| 
 | ||||
| struct UiRect { | ||||
|     vec4 position; | ||||
|     vec4 size; | ||||
|     vec4 border_size; | ||||
|     vec4 color; | ||||
| }; | ||||
| 
 | ||||
| struct UiRectFragment { | ||||
|     highp vec4 device_position; | ||||
|     highp vec2 position; | ||||
|     highp vec2 size; | ||||
|     highp vec2 border_size; | ||||
|     highp vec2 screen_size; | ||||
|     highp vec2 tex_coord; | ||||
|     highp vec4 color; | ||||
| }; | ||||
| 
 | ||||
| layout(std430, binding = 0) readonly buffer VertexBlock { | ||||
|     Vertex vertices[]; | ||||
| }; | ||||
| 
 | ||||
| layout(std430, binding = 1) readonly buffer RectBlock { | ||||
|     UiRect rects[]; | ||||
| }; | ||||
| layout(std430, binding = 2) readonly buffer ParamsBlock { | ||||
|     vec2 screen_size; | ||||
| }; | ||||
| 
 | ||||
| out UiRectFragment out_rect; | ||||
| 
 | ||||
| void main() { | ||||
|     UiRect rect = rects[gl_InstanceID]; | ||||
| 
 | ||||
|     out_rect.device_position = vec4(vertices[gl_VertexID].position, 1, 1); | ||||
|     out_rect.position = rect.position.xy; | ||||
|     out_rect.size = rect.size.xy; | ||||
|     out_rect.border_size = rect.border_size.xy; | ||||
|     out_rect.screen_size = screen_size; | ||||
|     out_rect.tex_coord = vertices[gl_VertexID].tex_coord; | ||||
|     out_rect.color = rect.color; | ||||
| 
 | ||||
|     gl_Position = vec4(vertices[gl_VertexID].position, 1, 1); | ||||
| } | ||||
							
								
								
									
										137
									
								
								src/ed_array.h
								
								
								
								
							
							
						
						
									
										137
									
								
								src/ed_array.h
								
								
								
								
							|  | @ -1,80 +1,85 @@ | |||
| #ifndef ED_ARRAY_INCLUDED | ||||
| #define ED_ARRAY_INCLUDED | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <assert.h> | ||||
| #include <memory.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| 
 | ||||
| #define array(T) struct T ## _Array | ||||
| #define arrayTemplate(T) array(T) {\ | ||||
|     size_t size, capacity;\ | ||||
|     T *data;\ | ||||
| };\ | ||||
| array(T) T ## _ArrayConstruct(size_t size) {\ | ||||
|     array(T) this;\ | ||||
|     this.size = 0;\ | ||||
|     this.capacity = size;\ | ||||
|     this.data = malloc(size * sizeof(T));\ | ||||
|     if (!this.data) {\ | ||||
|         assert("failed to allocate memory for array");\ | ||||
|     }\ | ||||
|     return this;\ | ||||
| };\ | ||||
| void T ## _PushArray(array(T) *arr, T value) {\ | ||||
|     if (arr->size+1 <= arr->capacity) {\ | ||||
|         arr->data[arr->size] = value;\ | ||||
|         arr->size += 1;\ | ||||
|     } else {\ | ||||
|         fprintf(stderr, "failed to push to u8 array, size+num > capacity\n");\ | ||||
|     }\ | ||||
| };\ | ||||
| void T ## _PushArrayMulti(array(T) *arr, T *values, size_t len) {\ | ||||
|     for (size_t i = 0; i < len; ++i) {\ | ||||
|         T ## _PushArray(arr, values[i]);\ | ||||
|     }\ | ||||
| };\ | ||||
| void T ## _InsertArrayAt(array(T) *arr, size_t loc, T value) {\ | ||||
|     if (arr->size == arr->capacity) {\ | ||||
|         arr->capacity *= 2;\ | ||||
|         void *new_data = realloc(arr->data, arr->capacity);\ | ||||
|         if (new_data == NULL) {\ | ||||
|             fprintf(stderr, "out of memory when reallocating array\n");\ | ||||
|         }\ | ||||
|         arr->data = new_data;\ | ||||
|     }\ | ||||
|     memcpy(&arr->data[loc+1], &arr->data[loc], arr->size * sizeof(T));\ | ||||
|     arr->data[loc] = value;\ | ||||
|     arr->size += 1;\ | ||||
| }; | ||||
| #define array(T) struct T##_Array | ||||
| #define arrayTemplate(T)                                                       \ | ||||
|     array(T) {                                                                 \ | ||||
|         size_t size, capacity;                                                 \ | ||||
|         T *data;                                                               \ | ||||
|     };                                                                         \ | ||||
|     array(T) T##_ArrayConstruct(size_t size) {                                 \ | ||||
|         array(T) this;                                                         \ | ||||
|         this.size = 0;                                                         \ | ||||
|         this.capacity = size;                                                  \ | ||||
|         this.data = malloc(size * sizeof(T));                                  \ | ||||
|         if (!this.data) {                                                      \ | ||||
|             assert("failed to allocate memory for array");                     \ | ||||
|         }                                                                      \ | ||||
|         return this;                                                           \ | ||||
|     };                                                                         \ | ||||
|     void T##_PushArray(array(T) * arr, T value) {                              \ | ||||
|         if (arr->size + 1 <= arr->capacity) {                                  \ | ||||
|             arr->data[arr->size] = value;                                      \ | ||||
|             arr->size += 1;                                                    \ | ||||
|         } else {                                                               \ | ||||
|             fprintf(stderr,                                                    \ | ||||
|                     "failed to push to u8 array, size+num > capacity\n");      \ | ||||
|         }                                                                      \ | ||||
|     };                                                                         \ | ||||
|     void T##_PushArrayMulti(array(T) * arr, T * values, size_t len) {          \ | ||||
|         for (size_t i = 0; i < len; ++i) {                                     \ | ||||
|             T##_PushArray(arr, values[i]);                                     \ | ||||
|         }                                                                      \ | ||||
|     };                                                                         \ | ||||
|     void T##_InsertArrayAt(array(T) * arr, size_t loc, T value) {              \ | ||||
|         if (arr->size == arr->capacity) {                                      \ | ||||
|             arr->capacity *= 2;                                                \ | ||||
|             void *new_data = realloc(arr->data, arr->capacity);                \ | ||||
|             if (new_data == NULL) {                                            \ | ||||
|                 fprintf(stderr, "out of memory when reallocating array\n");    \ | ||||
|             }                                                                  \ | ||||
|             arr->data = new_data;                                              \ | ||||
|         }                                                                      \ | ||||
|         memcpy(&arr->data[loc + 1], &arr->data[loc], arr->size * sizeof(T));   \ | ||||
|         arr->data[loc] = value;                                                \ | ||||
|         arr->size += 1;                                                        \ | ||||
|     }; | ||||
| 
 | ||||
| #define slice(T) struct T ## _Slice | ||||
| #define sliceTemplate(T) slice(T) {\ | ||||
|     size_t len;\ | ||||
|     T *data;\ | ||||
| };\ | ||||
| array(T) T ## _FromSlice(slice(T) s) {\ | ||||
|     array(T) arr = T ## _ArrayConstruct(s.len);\ | ||||
|     memcpy(arr.data, s.data, sizeof(T) * s.len);\ | ||||
|     arr.size = s.len;\ | ||||
|     return arr;\ | ||||
| }\ | ||||
| slice(T) T ## _SliceConstruct(void *data, size_t len) {\ | ||||
|     slice(T) this;\ | ||||
|     this.len = len;\ | ||||
|     this.data = data;\ | ||||
|     return this;\ | ||||
| } | ||||
| #define slice(T) struct T##_Slice | ||||
| #define sliceTemplate(T)                                                       \ | ||||
|     slice(T) {                                                                 \ | ||||
|         size_t len;                                                            \ | ||||
|         T *data;                                                               \ | ||||
|     };                                                                         \ | ||||
|     array(T) T##_FromSlice(slice(T) s) {                                       \ | ||||
|         array(T) arr = T##_ArrayConstruct(s.len);                              \ | ||||
|         memcpy(arr.data, s.data, sizeof(T) * s.len);                           \ | ||||
|         arr.size = s.len;                                                      \ | ||||
|         return arr;                                                            \ | ||||
|     }                                                                          \ | ||||
|     slice(T) T##_SliceConstruct(void *data, size_t len) {                      \ | ||||
|         slice(T) this;                                                         \ | ||||
|         this.len = len;                                                        \ | ||||
|         this.data = data;                                                      \ | ||||
|         return this;                                                           \ | ||||
|     } | ||||
| 
 | ||||
| #define newArray(T, size) T ## _ArrayConstruct(size) | ||||
| #define newArrayFromSlice(T, s) T ## _FromSlice(s) | ||||
| #define pushArray(T, arr, value) T ## _PushArray(arr, (value)) | ||||
| #define pushArrayMulti(T, arr, values, len) T ## _PushArrayMulti(arr, values, len) | ||||
| #define newArray(T, size) T##_ArrayConstruct(size) | ||||
| #define newArrayFromSlice(T, s) T##_FromSlice(s) | ||||
| #define pushArray(T, arr, value) T##_PushArray(arr, (value)) | ||||
| #define pushArrayMulti(T, arr, values, len) T##_PushArrayMulti(arr, values, len) | ||||
| 
 | ||||
| #define insertArrayAt(T, arr, loc, value) T ## _InsertArrayAt(arr, loc, (value)) | ||||
| #define insertArrayAt(T, arr, loc, value) T##_InsertArrayAt(arr, loc, (value)) | ||||
| 
 | ||||
| #define newSlice(T, data, len) T ## _SliceConstruct(data, len) | ||||
| #define newSlice(T, data, len) T##_SliceConstruct(data, len) | ||||
| 
 | ||||
| arrayTemplate(uint8_t); | ||||
| sliceTemplate(uint8_t); | ||||
| arrayTemplate(uint32_t); | ||||
| sliceTemplate(uint32_t); | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
							
								
								
									
										229
									
								
								src/gfx.h
								
								
								
								
							
							
						
						
									
										229
									
								
								src/gfx.h
								
								
								
								
							|  | @ -85,7 +85,10 @@ typedef struct { | |||
| #include <wayland-client.h> | ||||
| #include <wayland-egl.h> | ||||
| 
 | ||||
| #include "file_io.h" | ||||
| 
 | ||||
| // And I thought MacOS needed a lot of state to create a window
 | ||||
| arrayTemplate(GLuint); | ||||
| typedef struct { | ||||
|     struct wl_display *display; | ||||
|     struct wl_registry *registry; | ||||
|  | @ -99,6 +102,7 @@ typedef struct { | |||
|     struct xdg_toplevel *xdg_toplevel; | ||||
|     struct wl_seat *seat; | ||||
|     struct wl_pointer *pointer; | ||||
|     uint8_t *pixels; | ||||
| 
 | ||||
|     EGLDisplay egl_display; | ||||
|     EGLConfig egl_config; | ||||
|  | @ -108,8 +112,20 @@ typedef struct { | |||
| 
 | ||||
|     int mouse_x, mouse_y; | ||||
|     int mouse_left_down, mouse_right_down; | ||||
| 
 | ||||
|     GLuint ui_rect_vertex_shader; | ||||
|     GLuint ui_rect_fragment_shader; | ||||
|     GLuint text_atlas_vertex_shader; | ||||
|     GLuint text_atlas_fragment_shader; | ||||
|     GLuint ui_rect_shader_program; | ||||
|     GLuint text_atlas_shader_program; | ||||
| 
 | ||||
|     array(GLuint) buffers; | ||||
|     array(GLuint) textures; | ||||
| } _opengl_gfx_context_wayland; | ||||
| 
 | ||||
| static void _opengl_gfx_present_wayland(_opengl_gfx_context_wayland *cx); | ||||
| 
 | ||||
| typedef struct { | ||||
|     Display *display; | ||||
|     Window window; | ||||
|  | @ -734,6 +750,71 @@ static const struct wl_registry_listener registry_listener = { | |||
|     _wayland_registry_handle_global_remove, | ||||
| }; | ||||
| 
 | ||||
| static void _opengl_gfx_message_callback(GLenum source, GLenum type, GLenum id, | ||||
|                                          GLenum severity, GLsizei length, | ||||
|                                          const GLchar *message, | ||||
|                                          const void *user_param) { | ||||
|     fprintf(stderr, | ||||
|             "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n", | ||||
|             type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "", type, severity, | ||||
|             message); | ||||
| } | ||||
| 
 | ||||
| static void _opengl_gfx_check_shader_error(string msg, GLuint shader, | ||||
|                                            GLuint status) { | ||||
|     GLint good = 0; | ||||
|     glGetShaderiv(shader, status, &good); | ||||
|     if (good == GL_FALSE) { | ||||
|         GLint max_length = 0; | ||||
|         glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &max_length); | ||||
| 
 | ||||
|         uint8_t *log_buffer = malloc(max_length + 1); | ||||
|         glGetShaderInfoLog(shader, max_length, &max_length, log_buffer); | ||||
|         glDeleteShader(shader); | ||||
| 
 | ||||
|         fprintf(stderr, "%.*s: %.*s\n", msg.len, msg.data, max_length, | ||||
|                 log_buffer); | ||||
|         exit(1); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void _opengl_gfx_check_shader_program_error(string msg, | ||||
|                                                    GLuint shader_program, | ||||
|                                                    GLuint status) { | ||||
|     GLint good = 0; | ||||
|     glGetProgramiv(shader_program, status, &good); | ||||
|     if (good == GL_FALSE) { | ||||
|         GLint max_length = 0; | ||||
|         glGetProgramiv(shader_program, GL_INFO_LOG_LENGTH, &max_length); | ||||
| 
 | ||||
|         uint8_t *log_buffer = malloc(max_length + 1); | ||||
|         glGetProgramInfoLog(shader_program, max_length, &max_length, | ||||
|                             log_buffer); | ||||
|         glDeleteProgram(shader_program); | ||||
| 
 | ||||
|         fprintf(stderr, "%.*s: %.*s\n", msg.len, msg.data, max_length, | ||||
|                 log_buffer); | ||||
|         exit(1); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static GLuint _opengl_gfx_compile_shader(string file_path, GLuint shader_type) { | ||||
|     GLuint shader = glCreateShader(shader_type); | ||||
|     size_t shader_file_size = get_file_size(file_path); | ||||
|     uint8_t *shader_file_data = malloc(shader_file_size + 1); | ||||
|     load_file(file_path, shader_file_size, shader_file_data); | ||||
|     shader_file_data[shader_file_size] = 0; | ||||
|     // fprintf(stderr, "%s\n", shader_file_data);
 | ||||
| 
 | ||||
|     glShaderSource(shader, 1, &shader_file_data, NULL); | ||||
|     glCompileShader(shader); | ||||
| 
 | ||||
|     _opengl_gfx_check_shader_error(_String("failed to compile shader"), shader, | ||||
|                                    GL_COMPILE_STATUS); | ||||
| 
 | ||||
|     return shader; | ||||
| } | ||||
| 
 | ||||
| static _opengl_gfx_context_wayland | ||||
| _opengl_gfx_init_context_wayland(uint32_t width, uint32_t height) { | ||||
|     _opengl_gfx_context_wayland cx = {0}; | ||||
|  | @ -773,10 +854,10 @@ _opengl_gfx_init_context_wayland(uint32_t width, uint32_t height) { | |||
|     int fd = syscall(SYS_memfd_create, "buffer", 0); | ||||
|     ftruncate(fd, buffer_size); | ||||
| 
 | ||||
|     uint8_t *data = | ||||
|     cx.pixels = | ||||
|         mmap(NULL, buffer_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); | ||||
|     for (int i = 0; i < buffer_size; ++i) { | ||||
|         data[i] = 255; | ||||
|     for (int i = 0; i < buffer_size; i++) { | ||||
|         cx.pixels[i] = 0; | ||||
|     } | ||||
| 
 | ||||
|     cx.shared_memory_pool = | ||||
|  | @ -805,7 +886,7 @@ _opengl_gfx_init_context_wayland(uint32_t width, uint32_t height) { | |||
|         8, | ||||
|         //
 | ||||
|         EGL_RENDERABLE_TYPE, | ||||
|         EGL_OPENGL_ES2_BIT, | ||||
|         EGL_OPENGL_BIT, | ||||
|         //
 | ||||
|         EGL_NONE, | ||||
|     }; | ||||
|  | @ -813,7 +894,7 @@ _opengl_gfx_init_context_wayland(uint32_t width, uint32_t height) { | |||
|     static const EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, | ||||
|                                              EGL_NONE}; | ||||
| 
 | ||||
|     cx.egl_display = eglGetDisplay((EGLNativeDisplayType)cx.display); | ||||
|     cx.egl_display = eglGetDisplay(cx.display); | ||||
|     if (cx.egl_display == EGL_NO_DISPLAY) { | ||||
|         fprintf(stderr, "Failed to create EGL display\n"); | ||||
|         exit(1); | ||||
|  | @ -842,6 +923,7 @@ _opengl_gfx_init_context_wayland(uint32_t width, uint32_t height) { | |||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     eglBindAPI(EGL_OPENGL_API); | ||||
|     cx.egl_context = eglCreateContext(cx.egl_display, cx.egl_config, | ||||
|                                       EGL_NO_CONTEXT, context_attribs); | ||||
| 
 | ||||
|  | @ -859,13 +941,39 @@ _opengl_gfx_init_context_wayland(uint32_t width, uint32_t height) { | |||
|         fprintf(stderr, "eglMakeCurrent() failed\n"); | ||||
|     } | ||||
| 
 | ||||
|     glClearColor(1.0f, 0.0f, 1.0f, 1.0f); | ||||
|     glClear(GL_COLOR_BUFFER_BIT); | ||||
|     glFlush(); | ||||
|     glEnable(GL_DEBUG_OUTPUT); | ||||
|     glDebugMessageCallback(_opengl_gfx_message_callback, NULL); | ||||
| 
 | ||||
|     if (eglSwapBuffers(cx.egl_display, cx.egl_surface) != EGL_TRUE) { | ||||
|         fprintf(stderr, "eglSwapBuffers() failed\n"); | ||||
|     } | ||||
|     glEnable(GL_BLEND); | ||||
|     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | ||||
|     glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | ||||
| 
 | ||||
|     cx.ui_rect_vertex_shader = _opengl_gfx_compile_shader( | ||||
|         _String("shaders/ui_rect_vertex.glsl"), GL_VERTEX_SHADER); | ||||
|     cx.ui_rect_fragment_shader = _opengl_gfx_compile_shader( | ||||
|         _String("shaders/ui_rect_fragment.glsl"), GL_FRAGMENT_SHADER); | ||||
|     cx.ui_rect_shader_program = glCreateProgram(); | ||||
|     glAttachShader(cx.ui_rect_shader_program, cx.ui_rect_vertex_shader); | ||||
|     glAttachShader(cx.ui_rect_shader_program, cx.ui_rect_fragment_shader); | ||||
|     glLinkProgram(cx.ui_rect_shader_program); | ||||
|     _opengl_gfx_check_shader_program_error( | ||||
|         _String("failed to link ui_rect shader program"), | ||||
|         cx.ui_rect_shader_program, GL_LINK_STATUS); | ||||
| 
 | ||||
|     cx.text_atlas_vertex_shader = _opengl_gfx_compile_shader( | ||||
|         _String("shaders/text_atlas_vertex.glsl"), GL_VERTEX_SHADER); | ||||
|     cx.text_atlas_fragment_shader = _opengl_gfx_compile_shader( | ||||
|         _String("shaders/text_atlas_fragment.glsl"), GL_FRAGMENT_SHADER); | ||||
|     cx.text_atlas_shader_program = glCreateProgram(); | ||||
|     glAttachShader(cx.text_atlas_shader_program, cx.text_atlas_vertex_shader); | ||||
|     glAttachShader(cx.text_atlas_shader_program, cx.text_atlas_fragment_shader); | ||||
|     glLinkProgram(cx.text_atlas_shader_program); | ||||
|     _opengl_gfx_check_shader_program_error( | ||||
|         _String("failed to link text_atlas shader program"), | ||||
|         cx.text_atlas_shader_program, GL_LINK_STATUS); | ||||
| 
 | ||||
|     cx.buffers = newArray(GLuint, 8); | ||||
|     cx.textures = newArray(GLuint, 8); | ||||
|     /* ******** */ | ||||
| 
 | ||||
|     return cx; | ||||
|  | @ -898,11 +1006,8 @@ static _opengl_gfx_context_x11 _opengl_gfx_init_context_11(uint32_t width, | |||
| static void _opengl_gfx_send_events_wayland(_opengl_gfx_context_wayland *cx) { | ||||
|     wl_display_dispatch(cx->display); | ||||
| 
 | ||||
|     // TODO: move this to present()
 | ||||
|     _gfx_context.gpu_glyphs.size = 0; | ||||
|     _gfx_context.gpu_ui_rects.size = 0; | ||||
|     _gfx_context.frame_func(cx->mouse_x, cx->mouse_y, cx->mouse_left_down, | ||||
|                             cx->mouse_right_down); | ||||
|     // TODO: don't just render like crazy, limit framerate
 | ||||
|     _opengl_gfx_present_wayland(cx); | ||||
| } | ||||
| 
 | ||||
| static void _opengl_gfx_send_events_x11(_opengl_gfx_context_x11 *cx) { | ||||
|  | @ -921,16 +1026,82 @@ static void _opengl_gfx_send_events_x11(_opengl_gfx_context_x11 *cx) { | |||
| } | ||||
| 
 | ||||
| static void _opengl_gfx_present_wayland(_opengl_gfx_context_wayland *cx) { | ||||
|     // TODO
 | ||||
|     _gfx_context.gpu_glyphs.size = 0; | ||||
|     _gfx_context.gpu_ui_rects.size = 0; | ||||
|     _gfx_context.frame_func(cx->mouse_x, cx->mouse_y, cx->mouse_left_down, | ||||
|                             cx->mouse_right_down); | ||||
| 
 | ||||
|     if (_gfx_context.gpu_glyphs.size > 0) { | ||||
|         gfx_update_buffer(&_gfx_context, 2, _gfx_context.gpu_glyphs.data, | ||||
|                           _gfx_context.gpu_glyphs.size * sizeof(GpuGlyph)); | ||||
|     } | ||||
|     if (_gfx_context.gpu_ui_rects.size > 0) { | ||||
|         gfx_update_buffer(&_gfx_context, 4, _gfx_context.gpu_ui_rects.data, | ||||
|                           _gfx_context.gpu_ui_rects.size * sizeof(GpuUiRect)); | ||||
|     } | ||||
| 
 | ||||
|     GpuUniformParams gpu_uniform_params = { | ||||
|         .screen_size = | ||||
|             { | ||||
|                 (float)_gfx_context.frame_width, | ||||
|                 (float)_gfx_context.frame_height, | ||||
|             }, | ||||
|     }; | ||||
| 
 | ||||
|     gfx_update_buffer(&_gfx_context, 3, &gpu_uniform_params, | ||||
|                       sizeof(GpuUniformParams)); | ||||
|     glClearColor(0.0f, 0.0f, 0.0f, 1.0f); | ||||
|     glClear(GL_COLOR_BUFFER_BIT); | ||||
|     glViewport(0, 0, _gfx_context.frame_width, _gfx_context.frame_height); | ||||
| 
 | ||||
|     if (_gfx_context.gpu_ui_rects.size > 0) { | ||||
|         glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, cx->buffers.data[0]); | ||||
|         glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, cx->buffers.data[4]); | ||||
|         glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, cx->buffers.data[3]); | ||||
|         glUseProgram(cx->ui_rect_shader_program); | ||||
| 
 | ||||
|         const uint16_t indices[] = {0, 1, 2, 0, 2, 3}; | ||||
|         glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices, | ||||
|                                 _gfx_context.gpu_ui_rects.size); | ||||
|     } | ||||
| 
 | ||||
|     if (_gfx_context.gpu_glyphs.size > 0) { | ||||
|         glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, cx->buffers.data[0]); | ||||
|         glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, cx->buffers.data[2]); | ||||
|         glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, cx->buffers.data[3]); | ||||
|         glUseProgram(cx->text_atlas_shader_program); | ||||
| 
 | ||||
|         const uint16_t indices[] = {0, 1, 2, 0, 2, 3}; | ||||
|         glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices, | ||||
|                                 _gfx_context.gpu_glyphs.size); | ||||
|     } | ||||
| 
 | ||||
|     glFlush(); | ||||
| 
 | ||||
|     if (eglSwapBuffers(cx->egl_display, cx->egl_surface) != EGL_TRUE) { | ||||
|         fprintf(stderr, "eglSwapBuffers() failed\n"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static size_t | ||||
| _opengl_gfx_push_texture_buffer_wayland(_opengl_gfx_context_wayland *cx, | ||||
|                                         uint32_t width, uint32_t height, | ||||
|                                         const void *data, size_t len) { | ||||
|     pushArray(GLuint, &cx->textures, 0); | ||||
|     glCreateTextures(GL_TEXTURE_2D, 1, | ||||
|                      &cx->textures.data[cx->textures.size - 1]); | ||||
|     glTextureParameteri(cx->textures.data[cx->textures.size - 1], | ||||
|                         GL_TEXTURE_MIN_FILTER, GL_NEAREST); | ||||
|     glTextureParameteri(cx->textures.data[cx->textures.size - 1], | ||||
|                         GL_TEXTURE_MAG_FILTER, GL_NEAREST); | ||||
|     glTextureStorage2D(cx->textures.data[cx->textures.size - 1], 1, GL_RGBA8, | ||||
|                        width, height); | ||||
|     glTextureSubImage2D(cx->textures.data[cx->textures.size - 1], 0, 0, 0, | ||||
|                         width, height, GL_RED, GL_UNSIGNED_BYTE, data); | ||||
|     glBindTextureUnit(cx->textures.size - 1, | ||||
|                       cx->textures.data[cx->textures.size - 1]); | ||||
| 
 | ||||
|     // TODO
 | ||||
|     return 0; | ||||
|     return cx->textures.size - 1; | ||||
| } | ||||
| 
 | ||||
| static void | ||||
|  | @ -938,24 +1109,32 @@ _opengl_gfx_resize_texture_buffer_wayland(_opengl_gfx_context_wayland *cx, | |||
|                                           uint32_t width, size_t texture_index, | ||||
|                                           uint32_t height) { | ||||
|     // TODO
 | ||||
|     assert(false && "_opengl_gfx_resize_texture_buffer_wayland unimplemented"); | ||||
| } | ||||
| 
 | ||||
| size_t _opengl_gfx_push_vertex_buffer_wayland(_opengl_gfx_context_wayland *cx, | ||||
|                                               const void *data, size_t len) { | ||||
|     pushArray(GLuint, &cx->buffers, 0); | ||||
|     glCreateBuffers(1, &cx->buffers.data[cx->buffers.size - 1]); | ||||
|     glNamedBufferStorage(cx->buffers.data[cx->buffers.size - 1], len, data, | ||||
|                          GL_DYNAMIC_STORAGE_BIT); | ||||
| 
 | ||||
|     // TODO
 | ||||
|     return 0; | ||||
|     return cx->buffers.size - 1; | ||||
| } | ||||
| static size_t | ||||
| _opengl_gfx_allocate_vertex_buffer_wayland(_opengl_gfx_context_wayland *cx, | ||||
|                                            size_t len) { | ||||
|     // TODO
 | ||||
|     return 0; | ||||
|     pushArray(GLuint, &cx->buffers, 0); | ||||
|     glCreateBuffers(1, &cx->buffers.data[cx->buffers.size - 1]); | ||||
|     glNamedBufferStorage(cx->buffers.data[cx->buffers.size - 1], len, (void *)0, | ||||
|                          GL_DYNAMIC_STORAGE_BIT); | ||||
| 
 | ||||
|     return cx->buffers.size - 1; | ||||
| } | ||||
| static void _opengl_gfx_update_buffer_wayland(_opengl_gfx_context_wayland *cx, | ||||
|                                               size_t buffer_index, | ||||
|                                               const void *data, size_t len) { | ||||
|     // TODO
 | ||||
|     glNamedBufferSubData(cx->buffers.data[buffer_index], 0, len, data); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
|  | @ -1072,7 +1251,7 @@ void *gfx_init_context(_gfx_frame_func frame_func, uint32_t width, | |||
|     _gfx_context.gpu_glyphs = newArray(GpuGlyph, 8192 * 32); | ||||
|     _gfx_context.glyph_cache = newArray(GpuGlyph, 97); | ||||
| 
 | ||||
|     float vertices[] = { | ||||
|     const float vertices[] = { | ||||
|         // positions       texture coords
 | ||||
|         -1.0f, 1.0f,  0.0f, 0.0f, 1.0f,  1.0f,  1.0f, 0.0f, | ||||
|         1.0f,  -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 0.0f, 1.0f, | ||||
|  |  | |||
|  | @ -42,7 +42,7 @@ static struct { | |||
|     gfx_context_t *gfx_cx; | ||||
| } state; | ||||
| 
 | ||||
| void ed_init(_gfx_frame_func frame_func) { | ||||
| void init(_gfx_frame_func frame_func) { | ||||
|     state.gfx_cx = gfx_init_context(frame_func, 640, 480); | ||||
|     state.ui_cx = ui_init_context(); | ||||
| 
 | ||||
|  | @ -211,7 +211,7 @@ int main(int argc, char *argv[]) { | |||
|     return 0; | ||||
|     */ | ||||
| 
 | ||||
|     ed_init(ed_frame); | ||||
|     init(ed_frame); | ||||
| 
 | ||||
|     while (keep_running) { | ||||
|         gfx_run_events(state.gfx_cx); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue