From 19b08636a9a6d66e666a5730b05793a1f8417bd3 Mon Sep 17 00:00:00 2001 From: Andreev Gregory Date: Thu, 28 Aug 2025 03:07:43 +0300 Subject: [PATCH] Added Jane to r0. Splitted xlib and wayland margaret code (because I am switching to libwayland). Made some minor enhancements in r1. Wrote r2a program - doublebuffered wayland app that plays instruments. Started writing r3 - wayland client that uses vulkan instance --- CMakeLists.txt | 6 +- Makefile | 23 +- src/l2/codegen/codegen.c | 9 +- src/l2/margaret/vulkan.h | 135 ++- src/l2/marie/shape_geom.h | 6 - src/l2/tests/r0/r0.c | 102 +- src/l2/tests/r1/r1.c | 128 +-- src/l2/tests/r2/liza.h | 65 ++ src/l2/tests/r2/liza_collection.h | 285 ++++++ src/l2/tests/r2/r2a.c | 869 ++++++++++++++---- src/l2/tests/r2/r2b.c | 7 +- src/l2/tests/r3/r3.c | 514 +++++++++++ src/l2/tests/{r1 => r3}/shader_compile.sh | 0 src/l2/tests/{r1 => r3}/shaders/glsl/0/0.frag | 0 src/l2/tests/{r1 => r3}/shaders/glsl/0/0.vert | 0 15 files changed, 1791 insertions(+), 358 deletions(-) create mode 100644 src/l2/tests/r2/liza.h create mode 100644 src/l2/tests/r2/liza_collection.h create mode 100644 src/l2/tests/r3/r3.c rename src/l2/tests/{r1 => r3}/shader_compile.sh (100%) rename src/l2/tests/{r1 => r3}/shaders/glsl/0/0.frag (100%) rename src/l2/tests/{r1 => r3}/shaders/glsl/0/0.vert (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index ae1700b..958789f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,12 +43,14 @@ target_link_libraries(0_render_test_tex_init_prep -lm) add_executable(1_render_test src/l2/tests/r1/r1.c gen/l_wl_protocols/xdg-shell-private.c) target_link_libraries(1_render_test -lwayland-client -lrt -lm -lxkbcommon) -add_executable(2a_render_test src/l2/tests/r2/r2a.c) -target_link_libraries(2a_render_test ${LIBPIPEWIRE_LIBS} -lm) +add_executable(2a_render_test src/l2/tests/r2/r2a.c gen/l_wl_protocols/xdg-shell-private.c) +target_link_libraries(2a_render_test ${LIBPIPEWIRE_LIBS} -lwayland-client -lrt -lm -lxkbcommon) add_executable(2b_render_test src/l2/tests/r2/r2b.c) target_link_libraries(2b_render_test ${LIBPIPEWIRE_LIBS} -lm) +add_executable(3_render_test src/l2/tests/r3/r3.c gen/l_wl_protocols/xdg-shell-private.c) +target_link_libraries(3_render_test -lwayland-client -lm -lvulkan -lxkbcommon) #add_executable(0_play_test src/l3/tests/p0.c) #target_link_libraries(0_play_test -lncurses) diff --git a/Makefile b/Makefile index 3563c9e..5d3a75e 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,7 @@ cflags := -Wall -Wextra -Werror=implicit-function-declaration -Werror=return-typ cc := gcc wl_protocols := $(shell pkg-config --variable=pkgdatadir wayland-protocols) +libpipewire_flags := $(shell pkg-config --cflags --libs libpipewire-0.3) out/l1/codegen: src/l1/codegen/codegen.c $(HEADERS_src_l1) mkdir -p out/l1 @@ -50,21 +51,31 @@ gen/l_wl_protocols/xdg-shell-private.c: $(wl_protocols)/stable/xdg-shell/xdg-she mkdir -p gen/l_wl_protocols wayland-scanner private-code $< $@ - +l_wl_protocols := gen/l_wl_protocols/xdg-shell-client.h gen/l_wl_protocols/xdg-shell-private.c out/l2/r0: src/l2/tests/r0/r0.c $(HEADERS_gen_l2) $(HEADERS_src_l2) $(HEADERS_gen_l1) $(HEADERS_src_l1) mkdir -p out/l2 $(cc) $(cflags) -o $@ $< -lvulkan -lX11 -lm -#out/l2/r0: src/l2/tests/r0/r0_tex_init_prep.c $(HEADERS_gen_l2) $(HEADERS_src_l2) $(HEADERS_gen_l1) $(HEADERS_src_l1) -# mkdir -p out/l2 -# $(cc) $(cflags) -o $@ $< -lm -# out/l2/r1: src/l2/tests/r1/r1.c $(HEADERS_gen_l2) $(HEADERS_src_l2) $(HEADERS_gen_l1) $(HEADERS_src_l1) \ - gen/l_wl_protocols/xdg-shell-client.h gen/l_wl_protocols/xdg-shell-private.c + $(l_wl_protocols) mkdir -p out/l2 $(cc) $(cflags) -o $@ $< gen/l_wl_protocols/xdg-shell-private.c -lwayland-client -lrt -lxkbcommon -lm +out/l2/r2a: src/l2/tests/r2/r2a.c $(HEADERS_gen_l2) $(HEADERS_src_l2) $(HEADERS_gen_l1) $(HEADERS_src_l1) \ + $(l_wl_protocols) + mkdir -p out/l2 + $(cc) $(cflags) -o $@ $< gen/l_wl_protocols/xdg-shell-private.c -lwayland-client -lrt -lxkbcommon -lm $(libpipewire_flags) + +out/l2/r2b: src/l2/tests/r2/r2b.c $(HEADERS_gen_l2) $(HEADERS_src_l2) $(HEADERS_gen_l1) $(HEADERS_src_l1) + mkdir -p out/l2 + $(cc) $(cflags) -o $@ $< -lwayland-client -lrt -lxkbcommon -lm $(libpipewire_flags) + +out/l2/r3: src/l2/tests/r3/r3.c $(HEADERS_gen_l2) $(HEADERS_src_l2) $(HEADERS_gen_l1) $(HEADERS_src_l1) \ + $(l_wl_protocols) + mkdir -p out/l2 + $(cc) $(cflags) -o $@ $< gen/l_wl_protocols/xdg-shell-private.c -lwayland-client -lrt -lxkbcommon -lm -lvulkan + clean: rm -rf gen out diff --git a/src/l2/codegen/codegen.c b/src/l2/codegen/codegen.c index 6fa3705..613cdc7 100644 --- a/src/l2/codegen/codegen.c +++ b/src/l2/codegen/codegen.c @@ -25,9 +25,10 @@ void eve_of_l2() { /* Needed in r0_scene.h */ generate_eve_span_garden_for_primitive(cstr("l2"), cstr("r0"), cstr("ModelOnScene"), true, false); generate_eve_span_garden_for_primitive(cstr("l2"), cstr("r0"), cstr("UsedModelOnScene"), true, false); - /* Needed in margaret.h */ + /* Needed in margaret/vulkan.h */ + generate_eve_span_garden_for_primitive(cstr("l2"), cstr(""), cstr("Xlib_Event"), true, false); generate_eve_span_garden_for_primitive(cstr("l2"), cstr(""), cstr("CSTR"), true, false); - generate_eve_span_garden_for_primitive(cstr("l2"), cstr(""), cstr("MargaretChosenQueueFamilies"), true, false); + // generate_eve_span_garden_for_primitive(cstr("l2"), cstr(""), cstr("MargaretChosenQueueFamilies"), true, false); generate_eve_span_garden_for_primitive(cstr("l2"), cstr(""), cstr("VkQueueFamilyProperties"), true, false); generate_eve_span_garden_for_primitive(cstr("l2"), cstr(""), cstr("VkExtensionProperties"), true, false); generate_eve_header(cstr("l2"), cstr(""), cstr("VkSurfaceFormatKHR"), @@ -61,6 +62,10 @@ void eve_of_l2() { (util_templates_instantiation_options){ .vec = true}); generate_eve_header(cstr("l2"), cstr("r2"), cstr("PlayingSound"), (util_templates_instantiation_options){.vec_extended = true}); + generate_eve_header(cstr("l2"), cstr("r2"), cstr("BoxLizaInstrument"), + (util_templates_instantiation_options){ .vec = true}); + generate_eve_header(cstr("l2"), cstr("r2"), cstr("MyInstrument"), + (util_templates_instantiation_options){ .vec = true}); } void generate_Vec_cvec_header() { diff --git a/src/l2/margaret/vulkan.h b/src/l2/margaret/vulkan.h index 7a49d9b..be92390 100644 --- a/src/l2/margaret/vulkan.h +++ b/src/l2/margaret/vulkan.h @@ -8,9 +8,11 @@ #include #include #include +#include #include "../core/stringop.h" #include "../../l1/system/fileio.h" #include "time.h" +#include // todo: rewrite margaret to use libwayland-client, and get rid of this fucking crap typedef XEvent Xlib_Event; @@ -48,6 +50,7 @@ void margaret_win_init_set_properties(Xlib_Display* dpy, Xlib_Window win) { abortf("XChangeProperty"); } +// todo: delete this crap typedef struct { Xlib_Atom A_WM_windel; Xlib_Atom A_WM_protocols; @@ -57,6 +60,7 @@ typedef struct { Xlib_Window win; } Margaret_WEP; +// todo: delete this crap Margaret_WEP Margaret_WEP_new(Xlib_Display* dpy, Xlib_Window win) { return (Margaret_WEP){ .A_WM_protocols = XInternAtom(dpy, "WM_PROTOCOLS", False), @@ -66,6 +70,7 @@ Margaret_WEP Margaret_WEP_new(Xlib_Display* dpy, Xlib_Window win) { }; } +// todo: delete this crap void Margaret_WEP_update_with_new_event(Margaret_WEP* self, const Xlib_Event* ev) { if (ev->xany.window != self->win) return; @@ -129,7 +134,8 @@ typedef struct { VkDebugUtilsMessengerEXT debug_messenger; } MargaretInstanceAndItsDebug; -MargaretInstanceAndItsDebug MargaretInstanceAndItsDebug_new(bool enable_validation_layers) { +// todo: delete this crap +MargaretInstanceAndItsDebug MargaretInstanceAndItsDebug_new_xlib_flavour(bool enable_validation_layers) { // InstanceAndDebugHands res{}; VkApplicationInfo app_info = { .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, @@ -195,6 +201,72 @@ MargaretInstanceAndItsDebug MargaretInstanceAndItsDebug_new(bool enable_validati return (MargaretInstanceAndItsDebug){.instance = instance, .debug_messenger = debug_messenger}; } +NODISCARD MargaretInstanceAndItsDebug MargaretInstanceAndItsDebug_new(bool enable_validation_layers) { + VkApplicationInfo app_info = { + .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, + .pApplicationName = "Kto prochital tot zdohnet", + .applicationVersion = VK_MAKE_VERSION(1, 0, 0), + .pEngineName = "Margaret", + .engineVersion = VK_MAKE_VERSION(1, 0, 0), + .apiVersion = VK_API_VERSION_1_2, + }; + + VecCSTR needed_extensions = VecCSTR_new(); + VecCSTR_append(&needed_extensions, "VK_KHR_wayland_surface"); + VecCSTR_append(&needed_extensions, "VK_KHR_surface"); + VecCSTR needed_layers = VecCSTR_new(); + if (enable_validation_layers) { + VecCSTR_append(&needed_extensions, "VK_EXT_debug_utils"); + VecCSTR_append(&needed_layers, "VK_LAYER_KHRONOS_validation"); + } + + VkInstanceCreateInfo instance_crinfo = { + .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + // .pNext may be set to `for-instance-creation-only` Debug Messanger crinfo later + .pApplicationInfo = &app_info, + .enabledLayerCount = needed_layers.len, + .ppEnabledLayerNames = needed_layers.buf, + .enabledExtensionCount = needed_extensions.len, + .ppEnabledExtensionNames = needed_extensions.buf, + }; + + if (enable_validation_layers) { + VkDebugUtilsMessengerCreateInfoEXT debug_messenger_2_crinfo = { + .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, + .messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, + .messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT, + .pfnUserCallback = margaret_static_debug_callback, + .pUserData = NULL, + }; + instance_crinfo.pNext = &debug_messenger_2_crinfo; + } + VkInstance instance; + if (vkCreateInstance(&instance_crinfo, NULL, &instance) != VK_SUCCESS) + abortf("Failed to create Vulkan instance"); + VkDebugUtilsMessengerEXT debug_messenger = NULL; + if (enable_validation_layers) { + VkDebugUtilsMessengerCreateInfoEXT debug_messenger_crinfo = { + .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, + .messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, + .messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT, + .pfnUserCallback = margaret_static_debug_callback, + .pUserData = NULL, + }; + margaret_create_debug_utils_messenger_EXT(instance, &debug_messenger_crinfo, NULL, &debug_messenger); + } + + return (MargaretInstanceAndItsDebug){.instance = instance, .debug_messenger = debug_messenger}; +} + + void MargaretInstanceAndItsDebug_drop(MargaretInstanceAndItsDebug instance) { if (instance.debug_messenger) { margaret_destroy_debug_utils_messenger_EXT(instance.instance, instance.debug_messenger, NULL); @@ -207,7 +279,6 @@ typedef struct { U32 for_presentation; } MargaretChosenQueueFamilies; -#include "../../../gen/l2/eve/VecMargaretChosenQueueFamilies.h" #include "../../../gen/l2/eve/VecVkQueueFamilyProperties.h" /* MargaretChosenQueueFamilies or a static string, describing which part could not be found @@ -241,7 +312,6 @@ ResultMargaretChosenQueueFamiliesOrSpanU8 margaret_choose_good_queue_families(Vk index_for_presentation = Some_U32(i); } VecVkQueueFamilyProperties_drop(queue_families); - // todo: method _is_none will soon be gone if (index_for_graphics.variant == Option_None) return (ResultMargaretChosenQueueFamiliesOrSpanU8){ .variant = Result_Err, .err = cstr("No graphics queue family") }; if (index_for_presentation.variant == Option_None) @@ -704,19 +774,11 @@ VkFence margaret_create_fence(VkDevice device, bool create_signaled) { return res; } -//todo: strip synchronization lines out of here, pls PLS DO IT ADGASDHH HDJHFHFDKKF DFKDKDK THIS IS SO STUPID -// todo: kill myself typedef struct { VkSwapchainKHR swapchain; VecVkImageView image_views; VecVkFramebuffer framebuffers; VkExtent2D extent; - - VkSemaphore in_frame_transfer_complete; - VkSemaphore image_available_semaphore; - VkSemaphore rendered_to_IT1_semaphore; - VkSemaphore render_finished_semaphore; - VkFence in_flight_fence; } MargaretSwapchainBundle; MargaretSwapchainBundle MargaretSwapchainBundle_new( @@ -728,11 +790,6 @@ MargaretSwapchainBundle MargaretSwapchainBundle_new( VecVkFramebuffer framebuffers = margaret_create_swapchain_framebuffers(device, &image_views, render_pass, swapchain_details.image_extent); return (MargaretSwapchainBundle){ .swapchain = swapchain, .image_views = image_views, .framebuffers = framebuffers, .extent = swapchain_details.image_extent, - .in_frame_transfer_complete = margaret_create_semaphore(device), - .image_available_semaphore = margaret_create_semaphore(device), - .rendered_to_IT1_semaphore = margaret_create_semaphore(device), - .render_finished_semaphore = margaret_create_semaphore(device), - .in_flight_fence = margaret_create_fence(device, true), }; } @@ -743,11 +800,6 @@ VkSwapchainKHR MargaretSwapchainBundle_pop_swapchain_drop_rest(VkDevice device, for (size_t i = 0; i < swfb.image_views.len; i++) { vkDestroyImageView(device, *VecVkImageView_at(&swfb.image_views, i), NULL); } - vkDestroyFence(device, swfb.in_flight_fence, NULL); - vkDestroySemaphore(device, swfb.render_finished_semaphore, NULL); - vkDestroySemaphore(device, swfb.rendered_to_IT1_semaphore, NULL); - vkDestroySemaphore(device, swfb.image_available_semaphore, NULL); - vkDestroySemaphore(device, swfb.in_frame_transfer_complete, NULL); // Old swapchain bundle is 83% dropped return swfb.swapchain; } @@ -815,12 +867,14 @@ VkCommandBuffer margaret_allocate_command_buffer(VkDevice device, VkCommandPool return res; } +// todo: remove this dumb crap typedef struct { Xlib_Display* dpy; Xlib_Window win; -} MargaretSingleWindowSetup; +} MargaretSingleWindowSetup_XlibFlavour; -MargaretSingleWindowSetup MargaretSingleWindowSetup_new() { +// todo: delte this garbage +MargaretSingleWindowSetup_XlibFlavour MargaretSingleWindowSetup_XlibFlavour_new() { Display *dpy = XOpenDisplay(NULL); if (!dpy) abortf("Unable to open X display"); @@ -841,15 +895,17 @@ MargaretSingleWindowSetup MargaretSingleWindowSetup_new() { /* 3) Select for ConfigureNotify and Expose events */ XSelectInput(dpy, win, StructureNotifyMask | ExposureMask | PointerMotionMask | KeyPressMask | KeyReleaseMask); - return (MargaretSingleWindowSetup){ .dpy = dpy, .win = win }; + return (MargaretSingleWindowSetup_XlibFlavour){ .dpy = dpy, .win = win }; } -void MargaretSingleWindowSetup_drop(MargaretSingleWindowSetup x) { +// todo: ahfadlhja kjdelete this crap +void MargaretSingleWindowSetup_XlibFlavour_drop(MargaretSingleWindowSetup_XlibFlavour x) { XDestroyWindow(x.dpy, x.win); XCloseDisplay(x.dpy); } -VkSurfaceKHR margaret_create_surface(VkInstance instance, const MargaretSingleWindowSetup* x) { +// todo: delete this crap. We are gonna use libwayland +VkSurfaceKHR margaret_create_surface_x_dunk(VkInstance instance, const MargaretSingleWindowSetup_XlibFlavour* x) { VkXlibSurfaceCreateInfoKHR surface_crinfo = { .sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, .dpy = x->dpy, .window = x->win, }; @@ -859,6 +915,17 @@ VkSurfaceKHR margaret_create_surface(VkInstance instance, const MargaretSingleWi return surface; } +VkSurfaceKHR margaret_create_surface(VkInstance instance, struct wl_display* wl_display, struct wl_surface* wl_surface) { + VkWaylandSurfaceCreateInfoKHR crinfo = { + .sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR, + .display = wl_display, .surface = wl_surface + }; + VkSurfaceKHR surface; + if (vkCreateWaylandSurfaceKHR(instance, &crinfo, NULL, &surface) != VK_SUCCESS) + abortf("vkCreateWaylandSurfaceKHR\n"); + return surface; +} + // type_filter is a set of memory types (bit set) and we return one of its elements. // Result must satisfy `properties` // Bit index in `type_filter` is an index in VkPhysicalDeviceMemoryProperties::memoryTypes for that physical_device @@ -1098,6 +1165,7 @@ void margaret_copy_buffer_imm ( margaret_end_and_submit_and_free_command_buffer(device, command_pool, graphics_queue, cmd_buffer); } +// todo: get rid of this crap. I can do better // For application initialization purposes only void transition_image_layout ( VkDevice device, VkCommandPool command_pool, VkQueue graphics_queue, @@ -1163,6 +1231,7 @@ void margaret_copy_buffer_to_trans_dst_optimal_image ( margaret_end_and_submit_and_free_command_buffer(device, command_pool, graphics_queue, cmd_buffer); } +// todo: AHFHDF EW WHAT IS THAT??? // For application initialization purposes only void margaret_copy_buffer_to_texture_for_frag_shader_imm( VkDevice device, VkCommandPool command_pool, VkQueue graphics_queue, @@ -1187,6 +1256,7 @@ void margaret_copy_buffer_to_texture_for_frag_shader_imm( ); } +// todo: cjafhs WHAT IS THIS?? I need to remove this. I can do better than this // For texture VkImageView margaret_create_view_for_image ( VkDevice device, const MargaretImageInMemoryInfo* image, VkImageAspectFlags aspect_flags @@ -1270,19 +1340,6 @@ VkDescriptorPool margaret_create_descriptor_set_pool(VkDevice device, return descriptor_pool; } -/* Won't actually use this function, it's too underpowered */ -// void margaret_record_buf_copying_command_buf(VkCommandBuffer command_buffer, -// VkBuffer dest_buffer, VkBuffer src_buffer, VkDeviceSize buffer_size -// ) { -// VkCommandBufferBeginInfo beginfo = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, }; -// if (vkBeginCommandBuffer(command_buffer, &beginfo) != VK_SUCCESS) -// abortf("vkBeginCommandBuffer"); -// VkBufferCopy regions_to_copy[1] = {(VkBufferCopy){.srcOffset = 0, .dstOffset = 0, .size = buffer_size}}; -// vkCmdCopyBuffer(command_buffer, src_buffer, dest_buffer, ARRAY_SIZE(regions_to_copy), regions_to_copy); -// if (vkEndCommandBuffer(command_buffer) != VK_SUCCESS) -// abortf("vkEndCommandBuffer"); -// } - VkDescriptorSet margaret_allocate_descriptor_set(VkDevice device, VkDescriptorPool descriptor_pool, VkDescriptorSetLayout layout) { VkDescriptorSetAllocateInfo alloc_info = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, diff --git a/src/l2/marie/shape_geom.h b/src/l2/marie/shape_geom.h index 1dd46ec..459487d 100644 --- a/src/l2/marie/shape_geom.h +++ b/src/l2/marie/shape_geom.h @@ -19,10 +19,4 @@ void marie_clip_triang_with_triang_append_to_Vec(MarieTriangle C, MarieTriangle marie_clip_ccw_triang_with_ccw_triang_append_to_Vec(C, T, pile); } -// /* Better allocate 2n elements in pile */ -// void marie_closed_path_to_polygon_outline_tangy_append_to_Vec(ConstSpanvec2 path, float thickness, VecMarieTriangle* pile) { -// size_t n = path.len; -// // for (size_t ) -// } - #endif diff --git a/src/l2/tests/r0/r0.c b/src/l2/tests/r0/r0.c index 8362275..c4f2132 100644 --- a/src/l2/tests/r0/r0.c +++ b/src/l2/tests/r0/r0.c @@ -595,31 +595,6 @@ void reset_and_record_command_buffer_0( } vkCmdEndRenderPass(command_buffer); - - // VkImageMemoryBarrier barrier = { - // .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - // .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - // .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, - // .oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - // .newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - // .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - // .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - // .image = IT1_image, - // .subresourceRange = (VkImageSubresourceRange){ - // .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - // .baseMipLevel = 0, - // .levelCount = 1, - // .baseArrayLayer = 0, - // .layerCount = 1, - // }, - // }; - // vkCmdPipelineBarrier(command_buffer, - // VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - // VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - // 0, - // 0, NULL, - // 0, NULL, - // 1, &barrier); if (vkEndCommandBuffer(command_buffer) != VK_SUCCESS) abortf("vkEndCommandBuffer"); } @@ -755,13 +730,40 @@ void copy_scene_info_to_buffer_and_rerecord_full_copy_command_buffer( abortf("vkEndCommandBuffer"); } +// todo: generate this structure in l2 +typedef struct { + VkSemaphore in_frame_transfer_complete; + VkSemaphore image_available_semaphore; + VkSemaphore rendered_to_IT1_semaphore; + VkSemaphore render_finished_semaphore; + VkFence in_flight_fence; +} Jane; + +NODISCARD Jane Jane_create(VkDevice device) { + return (Jane){ + .in_frame_transfer_complete = margaret_create_semaphore(device), + .image_available_semaphore = margaret_create_semaphore(device), + .rendered_to_IT1_semaphore = margaret_create_semaphore(device), + .render_finished_semaphore = margaret_create_semaphore(device), + .in_flight_fence = margaret_create_fence(device, true) + }; +} + +void Jane_destroy(VkDevice device, Jane jane) { + vkDestroyFence(device, jane.in_flight_fence, NULL); + vkDestroySemaphore(device, jane.render_finished_semaphore, NULL); + vkDestroySemaphore(device, jane.rendered_to_IT1_semaphore, NULL); + vkDestroySemaphore(device, jane.image_available_semaphore, NULL); + vkDestroySemaphore(device, jane.in_frame_transfer_complete, NULL); +} -// todo: add here deletion and recreation of several synchronization primitives void recreate_swapchain( VkPhysicalDevice physical_device, VkDevice device, MargaretChosenQueueFamilies queue_fam, VkSurfaceKHR surface, - VkRenderPass render_pass, MargaretSwapchainBundle* swfb) { + VkRenderPass render_pass, MargaretSwapchainBundle* swfb, Jane* jane) { // We are about stop program and rebuild our sem+sem+fence synchronization mechanism vkDeviceWaitIdle(device); + Jane_destroy(device, *jane); + *jane = Jane_create(device); VkSwapchainKHR old_swapchain = MargaretSwapchainBundle_pop_swapchain_drop_rest(device, *swfb); // old swfb is 83% dropped ResultMargaretChosenSwapchainDetailsOrSpanU8 swapchain_details_res = margaret_choose_swapchain_details(physical_device, surface); @@ -800,17 +802,17 @@ int main() { const U32 MAX_WIN_WIDTH = 1920; const U32 MAX_WIN_HEIGHT = 1080; - MargaretSingleWindowSetup x = MargaretSingleWindowSetup_new(); + MargaretSingleWindowSetup_XlibFlavour x = MargaretSingleWindowSetup_XlibFlavour_new(); Margaret_WEP wep = Margaret_WEP_new(x.dpy, x.win); XMapWindow(x.dpy, x.win); - MargaretInstanceAndItsDebug inst_hands = MargaretInstanceAndItsDebug_new(ENABLE_VALIDATION_LAYERS); + MargaretInstanceAndItsDebug inst_hands = MargaretInstanceAndItsDebug_new_xlib_flavour(ENABLE_VALIDATION_LAYERS); VkInstance instance = inst_hands.instance; // print_instance_available_extensions(instance); // print_instance_available_layers(instance); - VkSurfaceKHR surface = margaret_create_surface(instance, &x); + VkSurfaceKHR surface = margaret_create_surface_x_dunk(instance, &x); VkPhysicalDevice physical_device = margaret_select_one_physical_device(instance, surface, GPU, bugged_GPU); @@ -831,7 +833,6 @@ int main() { ResultMargaretChosenSwapchainDetailsOrSpanU8 swapchain_details_res = margaret_choose_swapchain_details(physical_device, surface); if (swapchain_details_res.variant != Result_Ok) abortf("swapchain_details_res.variant != Result_Ok"); - MargaretChosenSwapchainDetails swapchain_details = swapchain_details_res.ok; // We hope that the image format won't be changed even when window gets resized // (swapchain_details.surface_format.format) @@ -845,10 +846,10 @@ int main() { VkRenderPass render_pass_0 = create_render_pass_0(device, IT1_format.some, zbuffer_format.some); PipelineHands pipeline_hands_0 = create_graphics_pipeline_0(device, render_pass_0, 0); - VkRenderPass render_pass_1 = create_render_pass_1(device, swapchain_details.surface_format.format); + VkRenderPass render_pass_1 = create_render_pass_1(device, swapchain_details_res.ok.surface_format.format); PipelineHands pipeline_hands_1 = create_graphics_pipeline_1(device, render_pass_1, 0); - MargaretSwapchainBundle swfb = MargaretSwapchainBundle_new(device, queue_fam, swapchain_details, surface, render_pass_1, NULL); + MargaretSwapchainBundle swfb = MargaretSwapchainBundle_new(device, queue_fam, swapchain_details_res.ok, surface, render_pass_1, NULL); SceneTemplate scene_template = {.models = VecModelInSceneTemplate_new(), .point_lights_max_count = pipeline_0_ubo_point_light_max_count, @@ -1081,6 +1082,8 @@ int main() { CamControlInfo my_cam_control_info = CamControlInfo_new(); vec3 Buba_control_info = {0}; + Jane jane = Jane_create(device); + // Mainloop margaret_ns_time start = margaret_clock_gettime_monotonic_raw(); margaret_ns_time prev_key_frame_time = start; @@ -1171,25 +1174,25 @@ int main() { // Rendering - vkWaitForFences(device, 1, &swfb.in_flight_fence, VK_TRUE, UINT64_MAX); + vkWaitForFences(device, 1, &jane.in_flight_fence, VK_TRUE, UINT64_MAX); uint32_t ij; VkResult aq_ret = vkAcquireNextImageKHR( device, swfb.swapchain, - UINT64_MAX, swfb.image_available_semaphore, VK_NULL_HANDLE, &ij + UINT64_MAX, jane.image_available_semaphore, VK_NULL_HANDLE, &ij ); if (aq_ret == VK_ERROR_OUT_OF_DATE_KHR) { fprintf(stderr, "vkAcquireNextImageKHR: VK_ERROR_OUT_OF_DATE_KHR\n"); - recreate_swapchain(physical_device, device, queue_fam, surface, render_pass_1, &swfb); + recreate_swapchain(physical_device, device, queue_fam, surface, render_pass_1, &swfb, &jane); continue; } else if (aq_ret == VK_SUBOPTIMAL_KHR) { fprintf(stderr, "vkAcquireNextImageKHR: VK_SUBOPTIMAL_KHR\n"); - recreate_swapchain(physical_device, device, queue_fam, surface, render_pass_1, &swfb); + recreate_swapchain(physical_device, device, queue_fam, surface, render_pass_1, &swfb, &jane); continue; } else if (aq_ret != VK_SUCCESS) { abortf("vkAcquireNextImageKHR"); } - vkResetFences(device, 1, &swfb.in_flight_fence); + vkResetFences(device, 1, &jane.in_flight_fence); float ae = margaret_ns_time_sec_diff(start, frame_A0); scene.anim_time = ae; @@ -1207,7 +1210,7 @@ int main() { transfer_command_buffer, host_mem_buffer.buffer, host_mem_buffer_mem, &scene, device_lighting_ubo.buffer, device_instance_attrs_for_all_generic_meshes.buffer); VkCommandBuffer command_buffers[1] = { transfer_command_buffer }; - VkSemaphore signaling_semaphores[1] = { swfb.in_frame_transfer_complete }; + VkSemaphore signaling_semaphores[1] = { jane.in_frame_transfer_complete }; VkSubmitInfo submit_info = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .commandBufferCount = ARRAY_SIZE(command_buffers), @@ -1230,7 +1233,7 @@ int main() { { VkSemaphore waiting_for_semaphores_if_dt_transfer_required[1] = { - swfb.in_frame_transfer_complete + jane.in_frame_transfer_complete }; VkPipelineStageFlags waiting_stages_if_dt_transfer_required[1] = { VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT @@ -1238,7 +1241,7 @@ int main() { assert(ARRAY_SIZE(waiting_for_semaphores_if_dt_transfer_required) == ARRAY_SIZE(waiting_stages_if_dt_transfer_required)); VkCommandBuffer command_buffers[1] = {rendering_command_buffer_0}; - VkSemaphore signaling_semaphores[1] = { swfb.rendered_to_IT1_semaphore }; + VkSemaphore signaling_semaphores[1] = { jane.rendered_to_IT1_semaphore }; VkSubmitInfo submit_info = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, @@ -1262,8 +1265,8 @@ int main() { } { VkSemaphore waiting_for_semaphores[2] = { - swfb.image_available_semaphore, - swfb.rendered_to_IT1_semaphore }; + jane.image_available_semaphore, + jane.rendered_to_IT1_semaphore }; VkPipelineStageFlags waiting_stages[2] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, @@ -1271,7 +1274,7 @@ int main() { }; assert(ARRAY_SIZE(waiting_for_semaphores) == ARRAY_SIZE(waiting_stages)); VkCommandBuffer command_buffers[1] = { rendering_command_buffer_1 }; - VkSemaphore signaling_semaphores[1] = { swfb.render_finished_semaphore }; + VkSemaphore signaling_semaphores[1] = { jane.render_finished_semaphore }; VkSubmitInfo cmd_submit_info = { .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, @@ -1286,12 +1289,12 @@ int main() { .pSignalSemaphores = signaling_semaphores, }; - if (vkQueueSubmit(graphics_queue, 1, &cmd_submit_info, swfb.in_flight_fence) != VK_SUCCESS) + if (vkQueueSubmit(graphics_queue, 1, &cmd_submit_info, jane.in_flight_fence) != VK_SUCCESS) abortf("vkQueueSubmit"); } { - VkSemaphore waiting_for_semaphores[] = { swfb.render_finished_semaphore }; + VkSemaphore waiting_for_semaphores[] = { jane.render_finished_semaphore }; VkSwapchainKHR swapchains[] = { swfb.swapchain }; uint32_t image_indices[] = { ij }; assert( ARRAY_SIZE(swapchains) == ARRAY_SIZE(image_indices) ); @@ -1311,11 +1314,11 @@ int main() { // todo: ponder more over this if (pres_ret == VK_ERROR_OUT_OF_DATE_KHR) { fprintf(stderr, "vkQueuePresentKHR: VK_ERROR_OUT_OF_DATE_KHR\n"); - recreate_swapchain(physical_device, device, queue_fam, surface, render_pass_1, &swfb); + recreate_swapchain(physical_device, device, queue_fam, surface, render_pass_1, &swfb, &jane); continue; } else if (pres_ret == VK_SUBOPTIMAL_KHR) { fprintf(stderr, "vkQueuePresentKHR: VK_SUBOPTIMAL_KHR\n"); - recreate_swapchain(physical_device, device, queue_fam, surface, render_pass_1, &swfb); + recreate_swapchain(physical_device, device, queue_fam, surface, render_pass_1, &swfb, &jane); continue; } else if (pres_ret != VK_SUCCESS) { abortf("vkQueuePresentKHR"); @@ -1363,6 +1366,7 @@ int main() { vkFreeMemory(device, host_mem, NULL); vkDestroyCommandPool(device, command_pool, NULL); MargaretSwapchainBundle_drop_with_device(device, swfb); + Jane_destroy(device, jane); destroy_graphics_pipeline_hands(device, pipeline_hands_1); vkDestroyRenderPass(device, render_pass_1, NULL); destroy_graphics_pipeline_hands(device, pipeline_hands_0); @@ -1370,5 +1374,5 @@ int main() { vkDestroyDevice(device, NULL); vkDestroySurfaceKHR(instance, surface, NULL); MargaretInstanceAndItsDebug_drop(inst_hands); - MargaretSingleWindowSetup_drop(x); + MargaretSingleWindowSetup_XlibFlavour_drop(x); } diff --git a/src/l2/tests/r1/r1.c b/src/l2/tests/r1/r1.c index 6d9c0a5..688d6e6 100644 --- a/src/l2/tests/r1/r1.c +++ b/src/l2/tests/r1/r1.c @@ -12,50 +12,37 @@ #include "../../marie/graphics_geom.h" #include "../../marie/rasterization.h" +#include + #define MAX_BUFFER_WIDTH 1000 #define MAX_BUFFER_HEIGHT 800 #define SWAPCHAIN_SLOTS 2 _Static_assert(INT32_MAX / MAX_BUFFER_WIDTH / MAX_BUFFER_HEIGHT / 4 / SWAPCHAIN_SLOTS > 1, "Swapchain is too big"); -// todo: write something normal here -/* Shared memory support code */ -static void randname(char *buf) { - struct timespec ts; - clock_gettime(CLOCK_REALTIME, &ts); - long r = ts.tv_nsec; - for (int i = 0; i < 6; ++i) { - buf[i] = 'A'+(r&15)+(r&16)*2; - r >>= 5; +static int create_shm_file(){ + char name[] = "/prototype1-XXXXXXXXXXXX"; + U64 v = ((U64)rand() << 32) + (U64)rand(); + for (int i = 0; i < 12; i++) { + name[sizeof(name) - 1 - 12 + i] = 'A' + (v&15) + (v&16)*2; + v >>= 5; } -} - -static int create_shm_file(void){ - int retries = 100; - do { - char name[] = "/wl_shm-XXXXXX"; - randname(name + sizeof(name) - 7); - --retries; - int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); - if (fd >= 0) { - shm_unlink(name); - return fd; - } - } while (retries > 0 && errno == EEXIST); - return -1; + int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); + if (fd < 0) + abortf("shm_open(\"%s\")", name); + if (shm_unlink(name) < 0) + abortf("shm_unlink(\"%s\")", name); + return fd; } static int allocate_shm_file(size_t size) { int fd = create_shm_file(); - if (fd < 0) - return -1; int ret; do { ret = ftruncate(fd, size); } while (ret < 0 && errno == EINTR); if (ret < 0) { - close(fd); - return -1; + abortf("ftruncate(%ld)", size); } return fd; } @@ -104,7 +91,7 @@ typedef struct { int32_t width; int32_t height; bool closed; -} audio_thread_state_t; +} state_t; typedef struct { uint32_t* data; @@ -123,7 +110,7 @@ void draw_frame_h_rasterizer_cb(void* ug, S32 x, S32 y, vec4 attr) { g->data[y * g->width + x] = color_vec4_to_color_u32(attr); } -void draw_frame(audio_thread_state_t* state, uint32_t* data, int32_t width, int32_t height) { +void draw_frame(state_t* state, uint32_t* data, int32_t width, int32_t height) { for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { data[y * width + x] = 0xFF00FFFF; @@ -170,7 +157,7 @@ void durackaya_tochka_update(durackaya_tochka* dot, float dur, float width, floa } } -void update_state(audio_thread_state_t* state, uint32_t fl) { +void update_state(state_t* state, uint32_t fl) { const float width = (float)(state->width < MAX_BUFFER_WIDTH ? state->width : MAX_BUFFER_WIDTH); const float height = (float)(state->height < MAX_BUFFER_HEIGHT ? state->height : MAX_BUFFER_HEIGHT); float dur = (float)fl / 1000; @@ -180,7 +167,7 @@ void update_state(audio_thread_state_t* state, uint32_t fl) { } static void main_h_wl_buffer_release(void *data, struct wl_buffer *buffer) { - audio_thread_state_t* state = data; + state_t* state = data; for (int ij = 0; ij < SWAPCHAIN_SLOTS; ij++) { if (state->swapchain.used_buffers[ij] == buffer) { // printf("Buffer %p was released and destroyed\n", buffer); @@ -196,7 +183,7 @@ static const struct wl_buffer_listener main_h_wl_buffer_listener = { .release = main_h_wl_buffer_release, }; -int swapchain_take_slot(audio_thread_state_t *state) { +int swapchain_take_slot(state_t *state) { for (int ij = 0; ij < SWAPCHAIN_SLOTS; ij++) { if (!state->swapchain.used_buffers[ij]) { const int32_t width = state->width < MAX_BUFFER_WIDTH ? state->width : MAX_BUFFER_WIDTH; @@ -215,7 +202,7 @@ int swapchain_take_slot(audio_thread_state_t *state) { return -1; } -void try_drawing_frame(audio_thread_state_t *state){ +void try_drawing_frame(state_t *state){ if (!state->swapchain.want_to_draw) return; int ij = swapchain_take_slot(state); @@ -236,7 +223,7 @@ void try_drawing_frame(audio_thread_state_t *state){ } static void main_h_xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial){ - audio_thread_state_t *state = data; + state_t *state = data; printf("XDG surface configured!\n"); xdg_surface_ack_configure(xdg_surface, serial); // todo: synchronize with frame event @@ -247,7 +234,7 @@ static void main_h_xdg_surface_configure(void *data, struct xdg_surface *xdg_sur static void main_h_xdg_toplevel_configure( void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height, struct wl_array *states ){ - audio_thread_state_t *state = data; + state_t *state = data; printf("XDG toplevel configured to (%d %d)\n", width, height); if (width <= 0 || height < 0) return; @@ -256,7 +243,7 @@ static void main_h_xdg_toplevel_configure( } static void main_h_xdg_toplevel_close(void *data, struct xdg_toplevel *toplevel){ - audio_thread_state_t *state = data; + state_t *state = data; state->closed = true; } @@ -283,11 +270,12 @@ static const struct xdg_wm_base_listener main_h_xdg_wm_base_listener = { static void main_h_wl_keyboard_keymap( void *data, struct wl_keyboard *wl_keyboard, uint32_t format, int32_t fd, uint32_t size ) { - audio_thread_state_t* state = data; - // todo: replace asserts with abortf - assert(format == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1); + state_t* state = data; + if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) + abortf("O_o"); char *map_shm = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); - assert(map_shm != MAP_FAILED); + if (map_shm == MAP_FAILED) + abortf("Couldn't mmap new keymap ha-ha\n"); xkb_keymap_unref(state->xkb_keymap); xkb_state_unref(state->xkb_state); @@ -303,7 +291,7 @@ static void main_h_wl_keyboard_keymap( static void main_h_wl_keyboard_enter( void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys ) { - audio_thread_state_t* state = data; + state_t* state = data; printf("keyboard enter; keys pressed are:\n"); uint32_t *key; wl_array_for_each(key, keys) { @@ -322,14 +310,14 @@ static void main_h_wl_keyboard_enter( static void main_h_wl_keyboard_leave( void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface ) { - audio_thread_state_t* state = data; + state_t* state = data; memset(&state->first_0x80_keys, 0, sizeof(state->first_0x80_keys)); } static void main_h_wl_keyboard_key( void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t key_action ) { - audio_thread_state_t* state = data; + state_t* state = data; char buf1[128]; char buf2[128]; uint32_t actual_key = key + 8; @@ -350,7 +338,7 @@ static void main_h_wl_keyboard_modifiers( void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group ) { - audio_thread_state_t* state = data; + state_t* state = data; xkb_state_update_mask(state->xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group); } @@ -372,27 +360,27 @@ static void main_h_wl_pointer_enter( void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y ) { - audio_thread_state_t *state = data; + state_t *state = data; state->pointer_pos = (vec2){(float)surface_x / 256.f, (float)surface_y / 256.f}; } static void main_h_wl_pointer_leave( void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface ) { - audio_thread_state_t *state = data; + state_t *state = data; } static void main_h_wl_pointer_motion( void *data,struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y ) { - audio_thread_state_t *state = data; + state_t *state = data; state->pointer_pos = (vec2){(float)surface_x / 256.f, (float)surface_y / 256.f}; } static void main_h_wl_pointer_button( void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t btn_action ) { - audio_thread_state_t *state = data; + state_t *state = data; if (btn_action == WL_POINTER_BUTTON_STATE_PRESSED) { printf("button = %u\n", button); if (button == 0x110) { @@ -404,11 +392,11 @@ static void main_h_wl_pointer_button( static void main_h_wl_pointer_axis( void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value ) { - audio_thread_state_t *state = data; + state_t *state = data; } static void main_h_wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) { - audio_thread_state_t *state = data; + state_t *state = data; } const struct wl_pointer_listener main_h_wl_pointer_listener = { @@ -421,7 +409,7 @@ const struct wl_pointer_listener main_h_wl_pointer_listener = { }; static void main_h_wl_seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capabilities) { - audio_thread_state_t* state = data; + state_t* state = data; if (capabilities & WL_SEAT_CAPABILITY_POINTER) { state->pointer = wl_seat_get_pointer(wl_seat); if (!state->pointer) @@ -448,7 +436,7 @@ static const struct wl_seat_listener main_h_wl_seat_listener = { static void main_h_wl_registry_global( void *data, struct wl_registry *wl_registry, uint32_t name, const char *interface, uint32_t version ) { - audio_thread_state_t *state = data; + state_t *state = data; if (strcmp(interface, wl_shm_interface.name) == 0) { state->wl_shm = wl_registry_bind(wl_registry, name, &wl_shm_interface, 1); if (!state->wl_shm) @@ -486,7 +474,7 @@ static const struct wl_registry_listener main_h_wl_registry_listener = { static const struct wl_callback_listener main_h_wl_surface_frame_listener; static void main_h_wl_surface_frame_done(void *data, struct wl_callback *cb, uint32_t time){ - audio_thread_state_t *state = data; + state_t *state = data; wl_callback_destroy(cb); // todo: when I add multiple surfaces, gonna need to think of something smarter state->wl_callback = wl_surface_frame(state->wl_surface); @@ -510,7 +498,7 @@ static const struct wl_callback_listener main_h_wl_surface_frame_listener = { int main() { - audio_thread_state_t state = { .width = 800, .height = 480 }; + state_t state = { .width = 800, .height = 480 }; state.v0 = (durackaya_tochka){.pos = {10, 10}, .speed = {100, 100}}; state.v1 = (durackaya_tochka){.pos = {100, 10}, .speed = {100, -50}}; state.v2 = (durackaya_tochka){.pos = {5, 330}, .speed = {-20, 170}}; @@ -570,9 +558,33 @@ int main() { abortf("wl_surface_frame\n"); wl_callback_add_listener(state.wl_callback, &main_h_wl_surface_frame_listener, &state); - while (wl_display_dispatch(state.wl_display)) { - if (state.closed) - break; + + int display_fd = wl_display_get_fd(state.wl_display); + struct pollfd pollfds[1] = {{.fd = display_fd, .events = POLLIN | POLLERR}}; + while (!state.closed) { + wl_display_prepare_read(state.wl_display); + if (wl_display_flush(state.wl_display) < 0) { + if (errno == EAGAIN) { + printf("Not enough space for a flush!\n"); + pollfds[0].events |= POLLOUT; + } + else { + abortf("wl_display_flush\n"); + } + } else { + pollfds[0].events &= ~POLLOUT; + } + if (poll(pollfds, ARRAY_SIZE(pollfds), -1) < 0) + abortf("poll\n"); + if (pollfds[0].revents & (POLLIN | POLLERR)) { + if (wl_display_read_events(state.wl_display)) + abortf("wl_display_read_events\n"); + int ret; + if ((ret =wl_display_dispatch_pending(state.wl_display) )< 0) + abortf("wl_display_dispatch_pending\n"); + } else { + wl_display_cancel_read(state.wl_display); + } } if (state.wl_callback) wl_callback_destroy(state.wl_callback); diff --git a/src/l2/tests/r2/liza.h b/src/l2/tests/r2/liza.h new file mode 100644 index 0000000..4aa972a --- /dev/null +++ b/src/l2/tests/r2/liza.h @@ -0,0 +1,65 @@ +#ifndef PROTOTYPE1_SRC_L2_TESTS_R2_LIZA_H +#define PROTOTYPE1_SRC_L2_TESTS_R2_LIZA_H + +typedef struct { + /* self (takes ownership) */ + void (*drop)(void*); + /* self, returns: pcm value */ + double (*next)(void*); + /* self, returns: duration in frames */ + size_t (*get_duration)(const void*); +} LizaSound_Table; + +typedef struct { + const void* r; + const LizaSound_Table* t; +} RefLizaSound; + +typedef struct { + void* r; + const LizaSound_Table* t; +} MutRefLizaSound; + +/* Existence of Box type implies that _drop method is virtual according to this trait */ +typedef struct { + void* m; /* Owns memory block r and object in it */ + const LizaSound_Table* t; +} BoxLizaSound; + +void BoxLizaSound_drop(BoxLizaSound self) { + self.t->drop(self.m); + free(self.m); +} + +#include "../../../../gen/l2/eve/r2/VecBoxLizaSound.h" + +typedef struct { + /* self (takes ownership) */ + void (*drop)(void*); + /* self, frequency, time, returns: new sound box */ + BoxLizaSound (*ding)(const void*, double, double); + // todo: request options for instrument +} LizaInstrument_Table; + +typedef struct { + const void* r; + const LizaInstrument_Table* t; +} RefLizaInstrument; + +typedef struct { + void* r; + const LizaInstrument_Table* t; +} MutLizaInstrument; + +typedef struct { + void* m; + const LizaInstrument_Table* t; +} BoxLizaInstrument; + +void BoxLizaInstrument_drop(BoxLizaInstrument self) { + self.t->drop(self.m); +} + +#include "../../../../gen/l2/eve/r2/VecBoxLizaInstrument.h" + +#endif \ No newline at end of file diff --git a/src/l2/tests/r2/liza_collection.h b/src/l2/tests/r2/liza_collection.h new file mode 100644 index 0000000..58f4a51 --- /dev/null +++ b/src/l2/tests/r2/liza_collection.h @@ -0,0 +1,285 @@ +#ifndef PROTOTYPE1_SRC_L2_TESTS_R2_LIZA_COLLECTION_H +#define PROTOTYPE1_SRC_L2_TESTS_R2_LIZA_COLLECTION_H + +#include "liza.h" + +// todo: dfasjshfdasfkjld +// double get_decay_factor(double planned_decay_time, double passes_per_second) { +// return pow(0.01, 1 / (planned_decay_time * passes_per_second)); +// } + +/* WeirdGuitar that produces StringTwitchSound */ + +// todo: remove decay_factor, replace with decay base +// todo: replace ding randomness with something better +typedef struct { + double rate; + double ta; + double decay_base; + double planned_time; + + double time; + double* buffer; + int buffer_size; + int i; + +} StringTwitchSound; + +StringTwitchSound StringTwitchSound_new(double frequency, double time, double rate) { + int buffer_size = (int)(rate / frequency); + double* buffer = safe_calloc(buffer_size, sizeof(double)); + for (int i = 0; i < buffer_size; i++) { + /* random noise in [-1, 1] */ + buffer[i] = ((double)rand() / RAND_MAX) * 2.0 - 1.0; + } + return (StringTwitchSound){ + .rate = rate, .ta = 0.01, .decay_base = pow(0.01, 1/time), .planned_time = 0.01 + time, + .buffer = buffer, .buffer_size = buffer_size, .i = 0, + }; +} + +void StringTwitchSound_drop(StringTwitchSound self) { + free(self.buffer); +} + +double StringTwitchSound_next(StringTwitchSound* self) { + double volume_c = (self->time > self->ta) ? pow(self->decay_base, self->time) : self->time / self->ta; + int j = (self->i + 1) % self->buffer_size; + double A = self->buffer[self->i]; + double B = self->buffer[j]; + self->buffer[self->i] = 0.5 * (A + B); + self->i = j; + self->time += 1. / self->rate; + return A * volume_c; +} + +size_t StringTwitchSound_get_duration(const StringTwitchSound* self) { + return (size_t)ceil(self->planned_time * self->rate); +} + +void LizaSound_Table_StringTwitchSound_drop(void* g) { + StringTwitchSound_drop(*(StringTwitchSound*)g); +} +double LizaSound_Table_StringTwitchSound_next(void* g) { + return StringTwitchSound_next(g); +} +size_t LizaSound_Table_StringTwitchSound_get_duration(const void* g) { + return StringTwitchSound_get_duration(g); +} +const LizaSound_Table LizaSound_Table_StringTwitchSound = { + .drop = LizaSound_Table_StringTwitchSound_drop, + .next = LizaSound_Table_StringTwitchSound_next, + .get_duration = LizaSound_Table_StringTwitchSound_get_duration +}; + +BoxLizaSound BoxLizaSound_from_StringTwitchSound(StringTwitchSound obj) { + void* mem = safe_malloc(sizeof(StringTwitchSound)); + memcpy(mem, &obj, sizeof(StringTwitchSound)); + return (BoxLizaSound){.m = mem, .t = &LizaSound_Table_StringTwitchSound}; +} + + +typedef struct { + double rate; +} WeirdGuitar; + +WeirdGuitar WeirdGuitar_new(double rate) { + return (WeirdGuitar){.rate = rate}; +} + +StringTwitchSound WeirdGuitar_ding(const WeirdGuitar* self, double frequency, double time) { + return StringTwitchSound_new(frequency, time, self->rate); +} + +void LizaInstrument_Table_WeirdGuitar_drop(void* self) { + +} +BoxLizaSound LizaInstrument_Table_WeirdGuitar_ding(const void* self, double frequency, double time) { + return BoxLizaSound_from_StringTwitchSound(WeirdGuitar_ding(self, frequency, time)); +} +const LizaInstrument_Table LizaInstrument_Table_WeirdGuitar = { + .drop = LizaInstrument_Table_WeirdGuitar_drop, + .ding = LizaInstrument_Table_WeirdGuitar_ding +}; + +BoxLizaInstrument BoxLizaInstrument_from_WeirdGuitar(WeirdGuitar obj) { + void* mem = safe_malloc(sizeof(WeirdGuitar)); + memcpy(mem, &obj, sizeof(WeirdGuitar)); + return (BoxLizaInstrument){.m = mem, .t = &LizaInstrument_Table_WeirdGuitar}; +} + +/* ElectroBlaster which produces AmplitudeModulationSound */ + +typedef struct { + double rate; + double mod_frequency; + double second_frequency; + double low_amplitude; + double ta; + double decay_base; + double planned_time; + + double ph1; + double ph2; + double time; +} AmplitudeModulationSound; + +double AmplitudeModulationSound_next(AmplitudeModulationSound* self) { + double volume_c = (self->time > self->ta) ? pow(self->decay_base, self->time) : self->time / self->ta; + double res = volume_c * (self->low_amplitude + (sin(self->ph2) + 1) * (1 - self->low_amplitude) / 2) * sin(self->ph1); + self->ph1 += 2 * M_PI * self->mod_frequency / self->rate; + if (self->ph1 > 2 * M_PI) + self->ph1 -= 2 * M_PI; + self->ph2 += 2 * M_PI * self->second_frequency / self->rate; + if (self->ph2 > 2 * M_PI) + self->ph2 -= 2 * M_PI; + self->time += 1. / self->rate; + return res; +} + +size_t AmplitudeModulationSound_get_duration(const AmplitudeModulationSound* self) { + return (size_t)ceil(self->planned_time * self->rate); +} + +void LizaSound_Table_AmplitudeModulationSound_drop(void* self) { + +} +double LizaSound_Table_AmplitudeModulationSound_next(void* self) { + return AmplitudeModulationSound_next(self); +} +size_t LizaSound_Table_AmplitudeModulationSound_get_duration(const void* self) { + return AmplitudeModulationSound_get_duration(self); +} +const LizaSound_Table LizaSound_Table_AmplitudeModulationSound = { + .drop = LizaSound_Table_AmplitudeModulationSound_drop, + .next = LizaSound_Table_AmplitudeModulationSound_next, + .get_duration = LizaSound_Table_AmplitudeModulationSound_get_duration, +}; + +BoxLizaSound BoxLizaSound_from_AmplitudeModulationSound(AmplitudeModulationSound obj) { + void* mem = safe_malloc(sizeof(AmplitudeModulationSound)); + memcpy(mem, &obj, sizeof(AmplitudeModulationSound)); + return (BoxLizaSound){.m = mem, .t = &LizaSound_Table_AmplitudeModulationSound}; +} + + +typedef struct { + double rate; +} AMKeys; + +AMKeys AMKeys_new(double rate) { + return (AMKeys){rate}; +} + +AmplitudeModulationSound AMKeys_ding(const AMKeys* self, double frequency, double time) { + return (AmplitudeModulationSound){ + .rate = self->rate, .mod_frequency = frequency, .second_frequency = 600, + .low_amplitude = 0.1, .ta = 0.01, .decay_base = pow(0.01, 1/time), .planned_time = 0.01 + time, + }; +} + +void LizaInstrument_Table_AMKeys_drop(void* self) { + +} +BoxLizaSound LizaInstrument_Table_AMKeys_ding(const void* self, double freq, double time) { + return BoxLizaSound_from_AmplitudeModulationSound(AMKeys_ding(self, freq, time)); +} +const LizaInstrument_Table LizaInstrument_Table_AMKeys = { + .drop = LizaInstrument_Table_AMKeys_drop, + .ding = LizaInstrument_Table_AMKeys_ding +}; + +BoxLizaInstrument BoxLizaInstrument_from_AMKeys(AMKeys obj) { + void* mem = safe_malloc(sizeof(AMKeys)); + memcpy(mem, &obj, sizeof(AMKeys)); + return (BoxLizaInstrument){.m = mem, .t = &LizaInstrument_Table_AMKeys}; +} + +/* FMKeys which produces FrequencyModulationSound */ + +typedef struct { + double rate; + double freq_1; + double freq_2; + double fmc; + double ta; + double decay_base; + double planned_time; + + double ph1; + double ph2; + double time; +} FrequencyModulationSound; + +double FrequencyModulationSound_next(FrequencyModulationSound* self) { + double volume_c = (self->time > self->ta) ? pow(self->decay_base, self->time) : self->time / self->ta; + double res = volume_c * sin(self->ph1); + self->ph1 += 2 * M_PI / self->rate * pow(self->freq_1, self->fmc * sin(self->ph2)); + if (self->ph1 > 2 * M_PI) + self->ph1 -= 2 * M_PI; + self->ph2 += 2 * M_PI * self->freq_2 / self->rate; + if (self->ph2 > 2 * M_PI) + self->ph2 -= 2 * M_PI; + self->time += 1. / self->rate; + return res; +} + +size_t FrequencyModulationSound_get_duration(const FrequencyModulationSound* self) { + return (size_t)ceil(self->planned_time * self->rate); +} + +void LizaSound_Table_FrequencyModulationSound_drop(void* self) { + +} +double LizaSound_Table_FrequencyModulationSound_next(void* self) { + return FrequencyModulationSound_next(self); +} +size_t LizaSound_Table_FrequencyModulationSound_get_duration(const void* self) { + return FrequencyModulationSound_get_duration(self); +} +const LizaSound_Table LizaSound_Table_FrequencyModulationSound = { + .drop = LizaSound_Table_FrequencyModulationSound_drop, + .next = LizaSound_Table_FrequencyModulationSound_next, + .get_duration = LizaSound_Table_FrequencyModulationSound_get_duration +}; + +BoxLizaSound BoxLizaSound_from_FrequencyModulationSound(FrequencyModulationSound obj) { + void* mem = safe_malloc(sizeof(FrequencyModulationSound)); + memcpy(mem, &obj, sizeof(FrequencyModulationSound)); + return (BoxLizaSound){.m = mem, .t = &LizaSound_Table_FrequencyModulationSound}; +} + +typedef struct { + double rate; +} FMKeys; + +FMKeys FMKeys_new(double rate) { + return (FMKeys){rate}; +} + +FrequencyModulationSound FMKeys_ding(FMKeys* self, double frequency, double time) { + return (FrequencyModulationSound){ + .rate = self->rate, .freq_1 = frequency, .freq_2 = 600, + .fmc = 3, .ta = 0.01, .decay_base = pow(0.01, 1/time), .planned_time = 0.01 + time, + }; +} + +void LizaInstrument_Table_FMKeys_drop(void* self) { + +} +BoxLizaSound LizaInstrument_Table_FMKeys_ding(const void* self, double freq, double time) { + return BoxLizaSound_from_FrequencyModulationSound(FMKeys_ding(self, freq, time)); +} +const LizaInstrument_Table LizaInstrument_Table_FMKeys = { + .drop = LizaInstrument_Table_FMKeys_drop, + .ding = LizaInstrument_Table_FMKeys_ding +}; + +BoxLizaInstrument BoxLizaInstrument_from_FMKeys(FMKeys obj) { + void* mem = safe_malloc(sizeof(FMKeys)); + memcpy(mem, &obj, sizeof(FMKeys)); + return (BoxLizaInstrument){.m = mem, .t = &LizaInstrument_Table_FMKeys}; +} + + +#endif \ No newline at end of file diff --git a/src/l2/tests/r2/r2a.c b/src/l2/tests/r2/r2a.c index 80ea979..8fc2685 100644 --- a/src/l2/tests/r2/r2a.c +++ b/src/l2/tests/r2/r2a.c @@ -4,154 +4,63 @@ #include #include #include "../../../l1/system/pthread.h" +#include "../../margaret/time.h" +#include "../../marie/graphics_geom.h" +#include "../../marie/rasterization.h" + +#include +#include +#include +#include +#include "../../../../gen/l_wl_protocols/xdg-shell-client.h" +#include + +#include "liza_collection.h" #define DEFAULT_RATE 44100 #define DEFAULT_CHANNELS 2 #define DEFAULT_VOLUME 0.1 -/* [roundtrip] */ -typedef struct { - int pending; - struct pw_main_loop *loop; -} Roundtrip_H_GuestData; - -static void roundtrip_h_pw_core_done(void *data, uint32_t id, int seq) { - Roundtrip_H_GuestData *g = data; - - if (id == PW_ID_CORE && seq == g->pending) - pw_main_loop_quit(g->loop); -} - -static void roundtrip(struct pw_core *core, struct pw_main_loop *loop) { - int err; - static const struct pw_core_events core_events = { - PW_VERSION_CORE_EVENTS, - .done = roundtrip_h_pw_core_done, - }; - - Roundtrip_H_GuestData aboba = {.pending = pw_core_sync(core, PW_ID_CORE, 0), .loop = loop }; - struct spa_hook core_listener; - - pw_core_add_listener(core, &core_listener, &core_events, &aboba); - - if ((err = pw_main_loop_run(loop)) < 0) - printf("main_loop_run error:%d!\n", err); - - spa_hook_remove(&core_listener); -} -/* [roundtrip] */ - -static void main_h_pw_registry_global( - void *data, uint32_t id, uint32_t permissions, const char *type, uint32_t version, const struct spa_dict *props - ) { - printf("object: id:%u type:%s/%d\n", id, type, version); -} - -static const struct pw_registry_events main_h_pw_registry_listener = { - PW_VERSION_REGISTRY_EVENTS, - .global = main_h_pw_registry_global, -}; - -typedef struct { - double* buffer; - int buffer_size; - int i; - double decay; -} KaplusStrong; - -KaplusStrong KaplusStrong_new(double frequency) { - int buffer_size = (int)(DEFAULT_RATE / frequency); - double* buffer = safe_calloc(buffer_size, sizeof(double)); - return (KaplusStrong){buffer, buffer_size, 0, 0.999}; -} - -void KaplusStrong_ding(KaplusStrong* self) { - for (int i = 0; i < self->buffer_size; i++) { - /* random noise in [-1, 1] */ - self->buffer[i] = ((double)rand() / RAND_MAX) * 2.0 - 1.0; - } -} - -double KaplusStrong_next(KaplusStrong* self) { - int j = (self->i + 1) % self->buffer_size; - double A = self->buffer[self->i]; - double B = self->buffer[j]; - self->buffer[self->i] = self->decay * 0.5 * (A + B); - self->i = j; - return A; -} - -void KaplusStrong_drop(KaplusStrong self) { - free(self.buffer); -} - -typedef struct { - double freq; - int tones; - bool raise_first; - double* phase; - double* tone_c; - double c; -} Multitone; - -Multitone Multitone_new(double frequency) { - int tones = MAX_S32(1, (int)floor(DEFAULT_RATE / frequency) - 1); - double* phase = safe_calloc(tones, sizeof(double)); - double* tone_c = safe_calloc(tones, sizeof(double)); - double nc = 0; - for (int i = 0; i < tones; i++) - nc += exp(-(double)i / 16); - for (int i = 0; i < tones; i++) { - tone_c[i] = exp(-(double)i / 16) / nc; - } - // for (int i = 0; i < tones; i++) { - // tone_c[i] = 1. / (double)(i + 1); - // } - return (Multitone){.freq = frequency, .tones = tones, .phase = phase, .tone_c = tone_c}; -} - -void Multitone_ding(Multitone* self) { - self->raise_first = true; -} - -double Multitone_next(Multitone* self) { - if (self->raise_first) { - self->c += 0.01; - if (self->c >= 1) { - self->c = 1; - self->raise_first = false; - } - } else { - self->c *= 0.9999; - } - - double a = 0; - for (int t = 0; t < self->tones; t++) { - a += sin(self->phase[t]) * self->tone_c[t]; - self->phase[t] += M_PI * 2 * self->freq * (float)(t + 1) / DEFAULT_RATE; - if (self->phase[t] >= M_PI * 2) - self->phase[t] -= M_PI * 2; - } - return a * self->c; -} - -void Multitone_drop(Multitone self) { - free(self.phase); - free(self.tone_c); -} - typedef struct { bool stop; - bool note_ks; - bool note_mt; + VecBoxLizaSound new_sounds; } AudioThreadCommands; -/* Can't be copied, can't be cloned (contains mutex) */ +void AudioThreadCommands_drop(AudioThreadCommands self) { + VecBoxLizaSound_drop(self.new_sounds); +} + +/* Not movable and even so more it isn't trivially movable */ typedef struct { pthread_mutex_t mut; AudioThreadCommands cmd; } AudioThreadBridge; +/* non-copyable trivially movable (because of BoxLizaSound field) */ +typedef struct { + BoxLizaSound gen; + size_t remaining_frames; +} PlayingSound; + +void PlayingSound_drop(PlayingSound self) { + BoxLizaSound_drop(self.gen); +} + +#include "../../../../gen/l2/eve/r2/PlayingSound.h" + +double fix_loud_sound(double a) { + const double L = 0.5; + const double b = (1 - L) / exp(-L / (1 - L)); + const double d = 1 / (1 - L); + if (a < -L) { + return - (1 - d * exp(b * a)); + } else if (a < L) { + return a; + } else { + return 1 - d * exp(-b * a); + } +} + typedef struct { struct pw_main_loop *pw_main_loop; struct pw_context *pw_context; @@ -162,63 +71,74 @@ typedef struct { struct spa_hook stream_listener_hook; AudioThreadBridge* bridge; - KaplusStrong ks; - Multitone mt; + VecPlayingSound playing_sounds; double time_written; } audio_thread_state_t; -static void main_h_pw_stream_process(void *ug){ +static void r2a_app_h_pw_stream_process(void *ug){ audio_thread_state_t* state = ug; safe_pthread_mutex_lock(&state->bridge->mut); - AudioThreadCommands cmd = state->bridge->cmd; - state->bridge->cmd = (AudioThreadCommands){ 0}; - safe_pthread_mutex_unlock(&state->bridge->mut); - if (cmd.stop) { + AudioThreadCommands* incoming_cmd = &state->bridge->cmd; + if (incoming_cmd->stop) { pw_main_loop_quit(state->pw_main_loop); + safe_pthread_mutex_unlock(&state->bridge->mut); return; } - if (cmd.note_ks) - KaplusStrong_ding(&state->ks); - if (cmd.note_mt) - Multitone_ding(&state->mt); + for (size_t i = 0; i < incoming_cmd->new_sounds.len; i++) { + /* effectively moved out (will resize incoming_cmd->new_sounds to 0 soon) */ + BoxLizaSound snd = *VecBoxLizaSound_at(&incoming_cmd->new_sounds, i); + VecPlayingSound_append(&state->playing_sounds, (PlayingSound){ + .gen = snd, .remaining_frames = snd.t->get_duration(snd.m) + }); + } + incoming_cmd->new_sounds.len = 0; /* We moved everything out */ + safe_pthread_mutex_unlock(&state->bridge->mut); struct pw_buffer *b = pw_stream_dequeue_buffer(state->pw_stream); if (!b) { - pw_log_warn("out of buffers: %m"); + pw_log_warn("out of buffers"); return; } - struct spa_buffer *buf = b->buffer; - int16_t *dst = buf->datas[0].data; - if (dst == NULL) - return; - uint32_t stride = sizeof(int16_t) * DEFAULT_CHANNELS; uint32_t n_frames = buf->datas[0].maxsize / stride; if (b->requested) n_frames = SPA_MIN(b->requested, n_frames); - for (uint32_t i = 0; i < n_frames; i++) { + int16_t *dst = buf->datas[0].data; + if (dst == NULL) + return; + + for (uint32_t f = 0; f < n_frames; f++) { double a = 0; - a += KaplusStrong_next(&state->ks); - a += Multitone_next(&state->mt); - state->time_written += 1. / DEFAULT_RATE; - int16_t val = (int16_t)round(a * DEFAULT_VOLUME * 32767.0); + for (size_t i = 0; i < state->playing_sounds.len;) { + PlayingSound* snd = VecPlayingSound_mat(&state->playing_sounds, i); + if (snd->remaining_frames == 0) { + /* snd invalidated, but iteration continues */ + VecPlayingSound_unordered_pop_and_drop(&state->playing_sounds, i); + continue; + } + a += snd->gen.t->next(snd->gen.m); + snd->remaining_frames--; + i++; + } + int16_t val = (int16_t)round(fix_loud_sound(a * DEFAULT_VOLUME) * 32767.0); for (int c = 0; c < DEFAULT_CHANNELS; c++) *dst++ = val; } + state->time_written += (double)n_frames / DEFAULT_RATE; + buf->datas[0].chunk->offset = 0; buf->datas[0].chunk->stride = (int32_t)stride; buf->datas[0].chunk->size = n_frames * stride; pw_stream_queue_buffer(state->pw_stream, b); - printf("Time written = %lf\n", state->time_written); } -const static struct pw_stream_events main_h_pw_stream_listener = { +const static struct pw_stream_events r2a_app_h_pw_stream_listener = { PW_VERSION_STREAM_EVENTS, - .process = main_h_pw_stream_process + .process = r2a_app_h_pw_stream_process }; void AudioThreadBridge_init(AudioThreadBridge* place) { @@ -227,12 +147,13 @@ void AudioThreadBridge_init(AudioThreadBridge* place) { } void AudioThreadBridge_destroy(AudioThreadBridge* resid) { + AudioThreadCommands_drop(resid->cmd); pthread_mutex_destroy(&resid->mut); } /* Full of audio */ void* audio_thread(void* ug) { - audio_thread_state_t state = { .ks = KaplusStrong_new(440), .mt = Multitone_new(440), .bridge = ug }; + audio_thread_state_t state = { .playing_sounds = VecPlayingSound_new(), .bridge = ug }; state.pw_main_loop = pw_main_loop_new(NULL /* properties */); if (!state.pw_main_loop) @@ -247,16 +168,11 @@ void* audio_thread(void* ug) { if (!state.pw_registry) abortf("pw_core_get_registry\n"); - struct spa_hook registry_listener_hook; - pw_registry_add_listener(state.pw_registry, ®istry_listener_hook, &main_h_pw_registry_listener, NULL); - - roundtrip(state.pw_core, state.pw_main_loop); - state.pw_stream = pw_stream_new(state.pw_core, "audio-src", pw_properties_new( PW_KEY_MEDIA_TYPE, "Audio", PW_KEY_MEDIA_CATEGORY, "Playback", PW_KEY_MEDIA_ROLE, "Music", NULL)); struct spa_hook stream_listener_hook; - pw_stream_add_listener(state.pw_stream, &stream_listener_hook, &main_h_pw_stream_listener, &state); + pw_stream_add_listener(state.pw_stream, &stream_listener_hook, &r2a_app_h_pw_stream_listener, &state); const struct spa_pod *stream_params[1]; uint8_t pod_buffer[1024]; @@ -279,17 +195,603 @@ void* audio_thread(void* ug) { printf("Exited mainloop\n"); pw_stream_destroy(state.pw_stream); - KaplusStrong_drop(state.ks); - Multitone_drop(state.mt); pw_proxy_destroy((struct pw_proxy*)state.pw_registry); pw_core_disconnect(state.pw_core); pw_context_destroy(state.pw_context); pw_main_loop_destroy(state.pw_main_loop); - printf("Thread finished\n"); + + VecPlayingSound_drop(state.playing_sounds); return NULL; } -int main(int argc, char *argv[]) { +/* No we specify what happens in the main thread */ + +#define MAX_BUFFER_WIDTH 1000 +#define MAX_BUFFER_HEIGHT 800 +#define SWAPCHAIN_SLOTS 2 + +_Static_assert(INT32_MAX / MAX_BUFFER_WIDTH / MAX_BUFFER_HEIGHT / 4 / SWAPCHAIN_SLOTS > 1, "Swapchain is too big"); + +static int create_shm_file(){ + char name[] = "/prototype1-XXXXXXXXXXXX"; + U64 v = ((U64)rand() << 32) + (U64)rand(); + for (int i = 0; i < 12; i++) { + name[sizeof(name) - 1 - 12 + i] = 'A' + (v&15) + (v&16)*2; + v >>= 5; + } + int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); + if (fd < 0) + abortf("shm_open(\"%s\")", name); + if (shm_unlink(name) < 0) + abortf("shm_unlink(\"%s\")", name); + return fd; +} + +static int allocate_shm_file(size_t size) { + int fd = create_shm_file(); + int ret; + do { + ret = ftruncate(fd, size); + } while (ret < 0 && errno == EINTR); + if (ret < 0) { + abortf("ftruncate(%ld)", size); + } + return fd; +} + +typedef struct { + int fd; + uint32_t* data; + struct wl_shm_pool* pool; + bool want_to_draw; + struct wl_buffer* used_buffers[SWAPCHAIN_SLOTS]; +} swapchain_t; + +typedef struct { + BoxLizaInstrument liza; + float turn_progress; +} MyInstrument; + +MyInstrument MyInstrument_new(BoxLizaInstrument liza) { + return (MyInstrument){liza, 0}; +} + +void MyInstrument_drop(MyInstrument self) { + BoxLizaInstrument_drop(self.liza); +} + +#include "../../../../gen/l2/eve/r2/VecMyInstrument.h" + +typedef struct { + /* Globals */ + struct wl_display *wl_display; + struct wl_registry *wl_registry; + struct wl_shm *wl_shm; + struct wl_compositor *wl_compositor; + struct xdg_wm_base *xdg_wm_base; + struct wl_seat *wl_seat; + /* Objects */ + swapchain_t swapchain; + struct wl_surface *wl_surface; + struct wl_callback* wl_callback; + struct xdg_surface *xdg_surface; + struct xdg_toplevel *xdg_toplevel; + /* inputs */ + struct wl_pointer* pointer; + struct wl_keyboard* keyboard; + /* xkb */ + struct xkb_state* xkb_state; + struct xkb_context* xkb_context; + struct xkb_keymap* xkb_keymap; + /* app state */ + vec2 pointer_pos; + bool first_0x80_keys[0x80]; + uint32_t last_frame_time; + int32_t width; + int32_t height; + bool closed; + /* instruments */ + VecMyInstrument instruments; + size_t selected_instrument; + /* Bridge to audio thread */ + AudioThreadBridge* audio_thread_bridge; +} wl_app_state_t; + +typedef struct { + uint32_t* data; + int32_t width; + int32_t height; +} WhereToDrawTriangle; + +U32 color_vec4_to_color_u32(vec4 color) { + return (((U32)roundf(color.w * 255)) << 24) + (((U32)roundf(color.x * color.w * 255)) << 16) + + (((U32)roundf(color.y * color.w * 255)) << 8) + (((U32)roundf(color.z * color.w * 255)) << 0); +} + +void draw_frame_h_rasterizer_cb(void* ug, S32 x, S32 y, vec4 attr) { + WhereToDrawTriangle* g = ug; + if (0 <= x && x < g->width && 0 <= y && y < g->height) + g->data[y * g->width + x] = color_vec4_to_color_u32(attr); +} + +void draw_frame(wl_app_state_t* state, uint32_t* data, int32_t width, int32_t height) { + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) { + data[y * width + x] = 0xFF00FFFF; + } + } + + for (size_t i = 0; i < state->instruments.len; i++) { + MyInstrument* inst = VecMyInstrument_mat(&state->instruments, i); + vec2 pos = {60 + 80 * (float)i, 60}; + WhereToDrawTriangle aboba = {data, width, height}; + vec2 triangle[3] = {}; + for (int v = 0; v < 3; v++) { + triangle[v] = vec2_add_vec2(pos, vec2_mul_scal( + marie_trigonom_circle(3 * M_PIf / 2 + (float)v * 2 * M_PIf / 3 + M_PIf * inst->turn_progress), + 10 + 10 * inst->turn_progress)); + } + vec4 color = {1, 1 - inst->turn_progress, 1 - inst->turn_progress, 1}; + marie_rasterize_triangle_with_attr( + (MariePlaneVertAttr){.pos = triangle[0], .attr = color}, + (MariePlaneVertAttr){.pos = triangle[1], .attr = color}, + (MariePlaneVertAttr){.pos = triangle[2], .attr = color}, + (FnMarieRasterizerCallback){.fn = draw_frame_h_rasterizer_cb, .guest = &aboba}); + } +} + +void update_state(wl_app_state_t* state, uint32_t fl) { + const float width = (float)(state->width < MAX_BUFFER_WIDTH ? state->width : MAX_BUFFER_WIDTH); + const float height = (float)(state->height < MAX_BUFFER_HEIGHT ? state->height : MAX_BUFFER_HEIGHT); + float dur = (float)fl / 1000; + for (size_t i = 0; i < state->instruments.len; i++) { + MyInstrument* inst = VecMyInstrument_mat(&state->instruments, i); + if (i == state->selected_instrument) { + inst->turn_progress = MIN_float(1, inst->turn_progress + dur * 1); + } else { + inst->turn_progress = MAX_float(0, inst->turn_progress - dur * 1); + } + } +} + +static void r2a_app_h_wl_buffer_release(void *data, struct wl_buffer *buffer) { + wl_app_state_t* state = data; + for (int ij = 0; ij < SWAPCHAIN_SLOTS; ij++) { + if (state->swapchain.used_buffers[ij] == buffer) { + // printf("Buffer %p was released and destroyed\n", buffer); + wl_buffer_destroy(buffer); + state->swapchain.used_buffers[ij] = NULL; + return; + } + } + abortf("HUH??!!!\n"); +} + +static const struct wl_buffer_listener r2a_app_h_wl_buffer_listener = { + .release = r2a_app_h_wl_buffer_release, +}; + +int swapchain_take_slot(wl_app_state_t *state) { + for (int ij = 0; ij < SWAPCHAIN_SLOTS; ij++) { + if (!state->swapchain.used_buffers[ij]) { + const int32_t width = state->width < MAX_BUFFER_WIDTH ? state->width : MAX_BUFFER_WIDTH; + const int32_t height = state->height < MAX_BUFFER_HEIGHT ? state->height : MAX_BUFFER_HEIGHT; + struct wl_buffer* s = wl_shm_pool_create_buffer( + state->swapchain.pool, ij * 4 * MAX_BUFFER_WIDTH * MAX_BUFFER_HEIGHT, + width, height, 4 * width, WL_SHM_FORMAT_ARGB8888); + state->swapchain.used_buffers[ij] = s; + if (!s) + abortf("wl_shm_pool_create_buffer\n"); + wl_buffer_add_listener(s, &r2a_app_h_wl_buffer_listener, state); + + return ij; + } + } + return -1; +} + +void try_drawing_frame(wl_app_state_t *state){ + if (!state->swapchain.want_to_draw) + return; + int ij = swapchain_take_slot(state); + // printf("Got slot %d", ij); + if (ij < 0) + return; + assert(ij < SWAPCHAIN_SLOTS); + state->swapchain.want_to_draw = false; + const int32_t width = state->width < MAX_BUFFER_WIDTH ? state->width : MAX_BUFFER_WIDTH; + const int32_t height = state->height < MAX_BUFFER_HEIGHT ? state->height : MAX_BUFFER_HEIGHT; + + /* Draw checkerboxed background */ + uint32_t* data = state->swapchain.data + ij * MAX_BUFFER_WIDTH * MAX_BUFFER_HEIGHT; + draw_frame(state, data, width, height); + wl_surface_attach(state->wl_surface, state->swapchain.used_buffers[ij], 0, 0); + wl_surface_damage_buffer(state->wl_surface, 0, 0, INT32_MAX, INT32_MAX); + wl_surface_commit(state->wl_surface); +} + +static void r2a_app_h_xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial){ + wl_app_state_t *state = data; + printf("XDG surface configured!\n"); + xdg_surface_ack_configure(xdg_surface, serial); + // todo: synchronize with frame event + state->swapchain.want_to_draw = true; + try_drawing_frame(state); +} + +static void r2a_app_h_xdg_toplevel_configure( + void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height, struct wl_array *states + ){ + wl_app_state_t *state = data; + printf("XDG toplevel configured to (%d %d)\n", width, height); + if (width <= 0 || height < 0) + return; + state->width = width; + state->height = height; +} + +static void r2a_app_h_xdg_toplevel_close(void *data, struct xdg_toplevel *toplevel){ + wl_app_state_t *state = data; + state->closed = true; +} + +static const struct xdg_toplevel_listener r2a_app_h_xdg_toplevel_listener = { + .configure = r2a_app_h_xdg_toplevel_configure, + .close = r2a_app_h_xdg_toplevel_close, +}; + + +static const struct xdg_surface_listener xdg_surface_listener = { + .configure = r2a_app_h_xdg_surface_configure, +}; + +static void r2a_app_h_xdg_wm_base_ping(void *data, struct xdg_wm_base *xdg_wm_base, uint32_t serial){ + xdg_wm_base_pong(xdg_wm_base, serial); +} + +static const struct xdg_wm_base_listener r2a_app_h_xdg_wm_base_listener = { + .ping = r2a_app_h_xdg_wm_base_ping, +}; + + + +static void r2a_app_h_wl_keyboard_keymap( + void *data, struct wl_keyboard *wl_keyboard, uint32_t format, int32_t fd, uint32_t size + ) { + wl_app_state_t* state = data; + if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) + abortf("O_o"); + char *map_shm = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + if (map_shm == MAP_FAILED) + abortf("Couldn't mmap new keymap ha-ha\n"); + + xkb_keymap_unref(state->xkb_keymap); + xkb_state_unref(state->xkb_state); + state->xkb_keymap = xkb_keymap_new_from_string( + state->xkb_context, map_shm, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); + munmap(map_shm, size); + close(fd); + state->xkb_state = xkb_state_new(state->xkb_keymap); + printf("Keymap changed!\n"); + memset(&state->first_0x80_keys, 0, sizeof(state->first_0x80_keys)); +} + +static void r2a_app_h_wl_keyboard_enter( + void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys + ) { + wl_app_state_t* state = data; + // todo: fill first_0x80_keys array + // todo: move all of this into a separate framework +} + +static void r2a_app_h_wl_keyboard_leave( + void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface + ) { + wl_app_state_t* state = data; + memset(&state->first_0x80_keys, 0, sizeof(state->first_0x80_keys)); +} + +static void r2a_app_h_wl_keyboard_key( + void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t key_action + ) { + wl_app_state_t* state = data; + uint32_t actual_key = key + 8; + /* It actually could be nokey keysym. You never know without checking */ + xkb_keysym_t keysym = xkb_state_key_get_one_sym(state->xkb_state, actual_key); + if (keysym < 0x80 && key_action == WL_KEYBOARD_KEY_STATE_RELEASED) { + state->first_0x80_keys[keysym] = false; + } else if (keysym < 0x80 && key_action == WL_KEYBOARD_KEY_STATE_PRESSED) { + state->first_0x80_keys[keysym] = true; + } + if (keysym == XKB_KEY_0 && key_action == WL_KEYBOARD_KEY_STATE_PRESSED) { + state->selected_instrument = 10; + } + if (XKB_KEY_1 <= keysym && keysym <= XKB_KEY_9 && key_action == WL_KEYBOARD_KEY_STATE_PRESSED) { + state->selected_instrument = keysym - XKB_KEY_1; + } + const xkb_keysym_t octave[] = { + XKB_KEY_a, XKB_KEY_w, XKB_KEY_s, XKB_KEY_e, XKB_KEY_d, XKB_KEY_r, + XKB_KEY_f, XKB_KEY_t, XKB_KEY_g, XKB_KEY_y, XKB_KEY_h, XKB_KEY_u + }; + if (state->selected_instrument < state->instruments.len) { + // todo: add box to ref and box to mref methods + const BoxLizaInstrument* instrument = &VecMyInstrument_at(&state->instruments, state->selected_instrument)->liza; + for (int i = 0; i < ARRAY_SIZE(octave); i++) { + if (keysym == octave[i] && key_action == WL_KEYBOARD_KEY_STATE_PRESSED) { + safe_pthread_mutex_lock(&state->audio_thread_bridge->mut); + VecBoxLizaSound_append(&state->audio_thread_bridge->cmd.new_sounds, + instrument->t->ding(instrument->m, 440 * pow(2, (double)i / 12), 0.9)); + safe_pthread_mutex_unlock(&state->audio_thread_bridge->mut); + break; + } + } + } +} + +static void r2a_app_h_wl_keyboard_modifiers( + void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, + uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group + ) { + wl_app_state_t* state = data; + xkb_state_update_mask(state->xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group); +} + +static void r2a_app_h_wl_keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay){ + printf("Repeat timings changed: rate = %d, delay = %d", rate, delay); +} + +static const struct wl_keyboard_listener r2a_app_h_wl_keyboard_listener = { + .keymap = r2a_app_h_wl_keyboard_keymap, + .enter = r2a_app_h_wl_keyboard_enter, + .leave = r2a_app_h_wl_keyboard_leave, + .key = r2a_app_h_wl_keyboard_key, + .modifiers = r2a_app_h_wl_keyboard_modifiers, + .repeat_info = r2a_app_h_wl_keyboard_repeat_info, +}; + + +static void r2a_app_h_wl_pointer_enter( + void *data, struct wl_pointer *wl_pointer, uint32_t serial, + struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y + ) { + wl_app_state_t *state = data; + state->pointer_pos = (vec2){(float)surface_x / 256.f, (float)surface_y / 256.f}; +} + +static void r2a_app_h_wl_pointer_leave( + void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface + ) { + wl_app_state_t *state = data; +} + +static void r2a_app_h_wl_pointer_motion( + void *data,struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y + ) { + wl_app_state_t *state = data; + state->pointer_pos = (vec2){(float)surface_x / 256.f, (float)surface_y / 256.f}; +} + +static void r2a_app_h_wl_pointer_button( + void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t btn_action + ) { + wl_app_state_t *state = data; + if (btn_action == WL_POINTER_BUTTON_STATE_PRESSED) { + printf("button = %u\n", button); + if (button == 0x110) { + printf("Bup!\n"); + } + } +} + +static void r2a_app_h_wl_pointer_axis( + void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value + ) { + wl_app_state_t *state = data; +} + +static void r2a_app_h_wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) { + wl_app_state_t *state = data; +} + +const struct wl_pointer_listener r2a_app_h_wl_pointer_listener = { + .enter = r2a_app_h_wl_pointer_enter, + .leave = r2a_app_h_wl_pointer_leave, + .motion = r2a_app_h_wl_pointer_motion, + .button = r2a_app_h_wl_pointer_button, + .axis = r2a_app_h_wl_pointer_axis, + .frame = r2a_app_h_wl_pointer_frame +}; + +static void r2a_app_h_wl_seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capabilities) { + wl_app_state_t* state = data; + if (capabilities & WL_SEAT_CAPABILITY_POINTER) { + state->pointer = wl_seat_get_pointer(wl_seat); + if (!state->pointer) + abortf("wl_seat_get_pointer\n"); + wl_pointer_add_listener(state->pointer, &r2a_app_h_wl_pointer_listener, state); + } + if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) { + state->keyboard = wl_seat_get_keyboard(wl_seat); + if (!state->keyboard) + abortf("wl_seat_get_keyboard\n"); + wl_keyboard_add_listener(state->keyboard, &r2a_app_h_wl_keyboard_listener, state); + } +} + +static void r2a_app_h_wl_seat_name(void* data, struct wl_seat* wl_seat, const char* name) { + printf("Our seat name: %s\n", name); +} + +static const struct wl_seat_listener r2a_app_h_wl_seat_listener = { + .capabilities = r2a_app_h_wl_seat_capabilities, + .name = r2a_app_h_wl_seat_name, +}; + +static void r2a_app_h_wl_registry_global( + void *data, struct wl_registry *wl_registry, uint32_t name, const char *interface, uint32_t version + ) { + wl_app_state_t *state = data; + if (strcmp(interface, wl_shm_interface.name) == 0) { + state->wl_shm = wl_registry_bind(wl_registry, name, &wl_shm_interface, 1); + if (!state->wl_shm) + abortf("wl_registry_bind\n"); + } else if (strcmp(interface, wl_compositor_interface.name) == 0) { + state->wl_compositor = wl_registry_bind(wl_registry, name, &wl_compositor_interface, 4); + if (!state->wl_compositor) + abortf("wl_registry_bind\n"); + } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { + state->xdg_wm_base = wl_registry_bind(wl_registry, name, &xdg_wm_base_interface, 1); + if (!state->xdg_wm_base) + abortf("wl_registry_bind\n"); + xdg_wm_base_add_listener(state->xdg_wm_base, &r2a_app_h_xdg_wm_base_listener, state); + } else if (strcmp(interface, wl_seat_interface.name) == 0) { + if (state->wl_seat) { + printf("We got second seat, but we only need one\n"); + return; + } + state->wl_seat = wl_registry_bind(wl_registry, name, &wl_seat_interface, 4); + if (!state->wl_seat) + abortf("wl_registry_bind\n"); + wl_seat_add_listener(state->wl_seat, &r2a_app_h_wl_seat_listener, state); + } +} + +static void r2a_app_h_wl_registry_global_remove(void *data, struct wl_registry *wl_registry, uint32_t name){ + // todo: delete seats +} + +static const struct wl_registry_listener r2a_app_h_wl_registry_listener = { + .global = r2a_app_h_wl_registry_global, + .global_remove = r2a_app_h_wl_registry_global_remove, +}; + +static const struct wl_callback_listener r2a_app_h_wl_surface_frame_listener; + +static void r2a_app_h_wl_surface_frame_done(void *data, struct wl_callback *cb, uint32_t time){ + wl_app_state_t *state = data; + wl_callback_destroy(cb); + // todo: when I add multiple surfaces, gonna need to think of something smarter + state->wl_callback = wl_surface_frame(state->wl_surface); + if (!state->wl_callback) + abortf("wl_surface_frame\n"); + wl_callback_add_listener(state->wl_callback, &r2a_app_h_wl_surface_frame_listener, state); + + if (state->last_frame_time != 0) { + update_state(state, time - state->last_frame_time); + } + + state->swapchain.want_to_draw = true; + try_drawing_frame(state); + + state->last_frame_time = time; +} + +static const struct wl_callback_listener r2a_app_h_wl_surface_frame_listener = { + .done = r2a_app_h_wl_surface_frame_done, +}; + + +void r2a_app(AudioThreadBridge *audio_thread_bridge) { + wl_app_state_t state = { .width = 800, .height = 480, + .instruments = VecMyInstrument_new(), + .selected_instrument = UINT64_MAX, .audio_thread_bridge = audio_thread_bridge + }; + VecMyInstrument_append(&state.instruments, MyInstrument_new(BoxLizaInstrument_from_WeirdGuitar(WeirdGuitar_new(DEFAULT_RATE)))); + VecMyInstrument_append(&state.instruments, MyInstrument_new(BoxLizaInstrument_from_AMKeys(AMKeys_new(DEFAULT_RATE)))); + VecMyInstrument_append(&state.instruments, MyInstrument_new(BoxLizaInstrument_from_FMKeys(FMKeys_new(DEFAULT_RATE)))); + + state.wl_display = wl_display_connect(NULL); + if (!state.wl_display) + abortf("Could not connect"); + state.wl_registry = wl_display_get_registry(state.wl_display); + if (!state.wl_registry) + abortf("wl_display_get_registry"); + wl_registry_add_listener(state.wl_registry, &r2a_app_h_wl_registry_listener, &state); + wl_display_roundtrip(state.wl_display); + if (!state.wl_shm) + abortf("No wl_shm"); + if (!state.wl_compositor) + abortf("No wl_compositor"); + if (!state.xdg_wm_base) + abortf("No xdg_wm_base"); + { + size_t size = SWAPCHAIN_SLOTS * MAX_BUFFER_WIDTH * MAX_BUFFER_HEIGHT * 4; + assert(size < INT32_MAX); + state.swapchain.fd = allocate_shm_file(size); + if (state.swapchain.fd == -1) { + abortf("AAA"); + } + + state.swapchain.data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, state.swapchain.fd, 0); + if (state.swapchain.data == MAP_FAILED) { + close(state.swapchain.fd); + abortf("what the dog doing"); + } + + state.swapchain.pool = wl_shm_create_pool(state.wl_shm, state.swapchain.fd, size); + if (!state.swapchain.pool) + abortf("wl_shm_create_pool"); + close(state.swapchain.fd); + } + state.xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + if (!state.xkb_context) + abortf("xkb_context_new\n"); + state.wl_surface = wl_compositor_create_surface(state.wl_compositor); + if (!state.wl_surface) + abortf("wl_compositor_create_surface\n"); + state.xdg_surface = xdg_wm_base_get_xdg_surface( + state.xdg_wm_base, state.wl_surface); + if (!state.xdg_surface) + abortf("xdg_wm_base_get_xdg_surface\n"); + xdg_surface_add_listener(state.xdg_surface, &xdg_surface_listener, &state); + state.xdg_toplevel = xdg_surface_get_toplevel(state.xdg_surface); + if (!state.xdg_toplevel) + abortf("xdg_surface_get_toplevel\n"); + xdg_toplevel_add_listener(state.xdg_toplevel, &r2a_app_h_xdg_toplevel_listener, &state); + xdg_toplevel_set_title(state.xdg_toplevel, "r2a"); + wl_surface_commit(state.wl_surface); + + state.wl_callback = wl_surface_frame(state.wl_surface); + if (!state.wl_callback) + abortf("wl_surface_frame\n"); + wl_callback_add_listener(state.wl_callback, &r2a_app_h_wl_surface_frame_listener, &state); + + while (wl_display_dispatch(state.wl_display)) { + if (state.closed) + break; + } + if (state.wl_callback) + wl_callback_destroy(state.wl_callback); + xdg_toplevel_destroy(state.xdg_toplevel); + xdg_surface_destroy(state.xdg_surface); + xdg_wm_base_destroy(state.xdg_wm_base); + wl_surface_destroy(state.wl_surface); + wl_compositor_destroy(state.wl_compositor); + if (state.pointer) + wl_pointer_destroy(state.pointer); + xkb_context_unref(state.xkb_context); + xkb_keymap_unref(state.xkb_keymap); + xkb_state_unref(state.xkb_state); + if (state.keyboard) + wl_keyboard_destroy(state.keyboard); + if (state.wl_seat) + wl_seat_destroy(state.wl_seat); + munmap(state.swapchain.data, SWAPCHAIN_SLOTS * MAX_BUFFER_WIDTH * MAX_BUFFER_HEIGHT * 2); + for (size_t i = 0; i < SWAPCHAIN_SLOTS; i++) { + if (state.swapchain.used_buffers[i]) + wl_buffer_destroy(state.swapchain.used_buffers[i]); + } + wl_shm_pool_destroy(state.swapchain.pool); + wl_shm_destroy(state.wl_shm); + wl_registry_destroy(state.wl_registry); + wl_display_disconnect(state.wl_display); + + /* Cleaning Up my state */ + VecMyInstrument_drop(state.instruments); +} + + +int main() { pw_init(NULL, NULL); AudioThreadBridge bridge; @@ -298,30 +800,11 @@ int main(int argc, char *argv[]) { if (pthread_create(&th, NULL, audio_thread, &bridge)) abortf("pthread_create\n"); - while (true) { - char line[50]; - if (!fgets(line, sizeof(line), stdin)) - break; - line[strcspn(line, "\n")] = '\0'; - if (strcmp(line, "1") == 0) { - safe_pthread_mutex_lock(&bridge.mut); - bridge.cmd.note_ks = true; - safe_pthread_mutex_unlock(&bridge.mut); - } else if (strcmp(line, "2") == 0) { - safe_pthread_mutex_lock(&bridge.mut); - bridge.cmd.note_mt = true; - safe_pthread_mutex_unlock(&bridge.mut); - } else if (strcmp(line, "q") == 0) { - safe_pthread_mutex_lock(&bridge.mut); - bridge.cmd.stop = true; - safe_pthread_mutex_unlock(&bridge.mut); - break; - } else { - printf("?\n"); - } - - } + r2a_app(&bridge); + safe_pthread_mutex_lock(&bridge.mut); + bridge.cmd.stop = true; + safe_pthread_mutex_unlock(&bridge.mut); safe_pthread_join(th); printf("The End!\n"); AudioThreadBridge_destroy(&bridge); diff --git a/src/l2/tests/r2/r2b.c b/src/l2/tests/r2/r2b.c index b45a1f3..de3f655 100644 --- a/src/l2/tests/r2/r2b.c +++ b/src/l2/tests/r2/r2b.c @@ -129,10 +129,12 @@ void PlayingSound_drop(PlayingSound self) { #include "../../../../gen/l2/eve/r2/PlayingSound.h" double fix_loud_sound(double a) { + return a; const double L = 0.5; const double b = (1 - L) / exp(-L / (1 - L)); const double d = 1 / (1 - L); if (a < -L) { + printf("Whhohps: %lf\n", a); return - (1 - d * exp(b * a)); } else if (a < L) { return a; @@ -203,7 +205,7 @@ static void main_h_pw_stream_process(void *ug){ snd->remaining_frames--; i++; } - int16_t val = (int16_t)round(fix_loud_sound(a) * DEFAULT_VOLUME * 32767.0); + int16_t val = (int16_t)round(fix_loud_sound(a * DEFAULT_VOLUME) * 32767.0); for (int c = 0; c < DEFAULT_CHANNELS; c++) *dst++ = val; } @@ -286,8 +288,7 @@ void* audio_thread(void* ug) { return NULL; } -int main(int argc, char *argv[]) { - int ret; +int main() { pw_init(NULL, NULL); AudioThreadBridge bridge; diff --git a/src/l2/tests/r3/r3.c b/src/l2/tests/r3/r3.c new file mode 100644 index 0000000..155be77 --- /dev/null +++ b/src/l2/tests/r3/r3.c @@ -0,0 +1,514 @@ +#include +#include +#include +#include +#include +#include +#include +#include "../../../../gen/l_wl_protocols/xdg-shell-client.h" +#include +#include "../../../l1/core/util.h" +#include "../../margaret/vulkan.h" +#include + +#define MAX_BUFFER_WIDTH 1000 +#define MAX_BUFFER_HEIGHT 800 + + +// todo: generate this function in l2 +VkRenderPass create_render_pass_0(VkDevice logical_device, VkFormat colorbuffer_format) { + VkAttachmentDescription all_attachments[1] = { + { + .format = colorbuffer_format, + .samples = VK_SAMPLE_COUNT_1_BIT, + .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, + .storeOp = VK_ATTACHMENT_STORE_OP_STORE, + .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, + .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + } + }; + + // For our one single render subpass + VkAttachmentReference color_attachments_refs[1] = { + { + .attachment = 0, + .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + } }; + + VkSubpassDescription subpasses_descr[1] = { (VkSubpassDescription){ + .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, + .colorAttachmentCount = ARRAY_SIZE(color_attachments_refs), + .pColorAttachments = color_attachments_refs, + } }; + + VkSubpassDependency subpass_dependencies[1] = { + // subpass_0_external + (VkSubpassDependency) { + .srcSubpass = VK_SUBPASS_EXTERNAL, + .dstSubpass = 0, + .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, + .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, + .srcAccessMask = 0, + .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + }}; + + VkRenderPassCreateInfo render_pass_crinfo = { + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, + .attachmentCount = ARRAY_SIZE(all_attachments), + .pAttachments = all_attachments, + .subpassCount = ARRAY_SIZE(subpasses_descr), + .pSubpasses = subpasses_descr, + .dependencyCount = ARRAY_SIZE(subpass_dependencies), + .pDependencies = subpass_dependencies, + }; + VkRenderPass render_pass; + if (vkCreateRenderPass(logical_device, &render_pass_crinfo, NULL, &render_pass) != VK_SUCCESS) + abortf("vkCreateRenderPass"); + return render_pass; +} + +void reset_and_record_command_buffer_0( + VkCommandBuffer command_buffer, VkRenderPass render_pass_0, + VkFramebuffer swapchain_image_framebuffer, VkExtent2D image_extent, + float ht + ) { + if (vkResetCommandBuffer(command_buffer, 0) != VK_SUCCESS) + abortf("vkResetCommandBuffer"); + VkCommandBufferBeginInfo info_begin = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; + if (vkBeginCommandBuffer(command_buffer, &info_begin) != VK_SUCCESS) + abortf("vkBeginCommandBuffer"); + + VkClearValue clear_values[1] = {{.color = {.float32={0, fabsf(sinf(ht)), 0, 1}}},}; + VkRenderPassBeginInfo renderpass_begin = { + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + .renderPass = render_pass_0, + .framebuffer = swapchain_image_framebuffer, + .renderArea.offset = (VkOffset2D){0, 0}, + .renderArea.extent = image_extent, + .clearValueCount = ARRAY_SIZE(clear_values), + .pClearValues = clear_values, + }; + + vkCmdBeginRenderPass(command_buffer, &renderpass_begin, VK_SUBPASS_CONTENTS_INLINE); + vkCmdEndRenderPass(command_buffer); + if (vkEndCommandBuffer(command_buffer) != VK_SUCCESS) + abortf("vkEndCommandBuffer"); +} + +typedef struct { + VkSemaphore image_available_semaphore; + VkSemaphore render_finished_semaphore; + VkFence in_flight_fence; +} Jane_r3; + +NODISCARD Jane_r3 Jane_r3_create(VkDevice device) { + return (Jane_r3){ + .image_available_semaphore = margaret_create_semaphore(device), + .render_finished_semaphore = margaret_create_semaphore(device), + .in_flight_fence = margaret_create_fence(device, true) + }; +} + +void Jane_r3_destroy(VkDevice device, Jane_r3 jane) { + vkDestroyFence(device, jane.in_flight_fence, NULL); + vkDestroySemaphore(device, jane.render_finished_semaphore, NULL); + vkDestroySemaphore(device, jane.image_available_semaphore, NULL); +} + + +typedef struct { + /* Globals */ + struct wl_display *wl_display; + struct wl_registry *wl_registry; + struct wl_compositor *wl_compositor; + struct xdg_wm_base *xdg_wm_base; + struct wl_seat *wl_seat; + /* Objects */ + struct wl_surface *wl_surface; + struct wl_callback* wl_callback; + struct xdg_surface *xdg_surface; + struct xdg_toplevel *xdg_toplevel; + /* vulkan objects */ + /* inputs */ + struct wl_pointer* pointer; + struct wl_keyboard* keyboard; + /* xkb */ + struct xkb_state* xkb_state; + struct xkb_context* xkb_context; + struct xkb_keymap* xkb_keymap; + /* app state */ + float ht; + uint32_t last_frame_time; + int32_t width; + int32_t height; + bool closed; + bool first_0x80_keys[0x80]; +} state_t; + +void draw_frame(state_t* state, uint32_t* data, int32_t width, int32_t height) { +} + +void update_state(state_t* state, uint32_t fl) { + const float width = (float)(state->width < MAX_BUFFER_WIDTH ? state->width : MAX_BUFFER_WIDTH); + const float height = (float)(state->height < MAX_BUFFER_HEIGHT ? state->height : MAX_BUFFER_HEIGHT); + float dur = (float)fl / 1000; + state->ht += dur; +} + +// void try_drawing_frame(state_t *state){ + // if (!state->swapchain.want_to_draw) + // return; + // const int32_t width = state->width < MAX_BUFFER_WIDTH ? state->width : MAX_BUFFER_WIDTH; + // const int32_t height = state->height < MAX_BUFFER_HEIGHT ? state->height : MAX_BUFFER_HEIGHT; + + /* Draw checkerboxed background */ + // uint32_t* data = state->swapchain.data + ij * MAX_BUFFER_WIDTH * MAX_BUFFER_HEIGHT; + // draw_frame(state, data, width, height); + // wl_surface_attach(state->wl_surface, state->swapchain.used_buffers[ij], 0, 0); + // wl_surface_damage_buffer(state->wl_surface, 0, 0, INT32_MAX, INT32_MAX); + // wl_surface_commit(state->wl_surface); +// } + +static void main_h_xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial){ + state_t *state = data; + printf("XDG surface configured!\n"); + xdg_surface_ack_configure(xdg_surface, serial); + // state->swapchain.want_to_draw = true; + // try_drawing_frame(state); +} + +static const struct xdg_surface_listener xdg_surface_listener = { + .configure = main_h_xdg_surface_configure, +}; + +static void main_h_xdg_toplevel_configure( + void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height, struct wl_array *states + ){ + state_t *state = data; + printf("XDG toplevel configured to (%d %d)\n", width, height); + if (width <= 0 || height < 0) + return; + state->width = width; + state->height = height; +} + +static void main_h_xdg_toplevel_close(void *data, struct xdg_toplevel *toplevel){ + state_t *state = data; + state->closed = true; +} + +static const struct xdg_toplevel_listener main_h_xdg_toplevel_listener = { + .configure = main_h_xdg_toplevel_configure, + .close = main_h_xdg_toplevel_close, +}; + +static void main_h_xdg_wm_base_ping(void *data, struct xdg_wm_base *xdg_wm_base, uint32_t serial){ + xdg_wm_base_pong(xdg_wm_base, serial); +} + +static const struct xdg_wm_base_listener main_h_xdg_wm_base_listener = { + .ping = main_h_xdg_wm_base_ping, +}; + + + +static void main_h_wl_keyboard_keymap( + void *data, struct wl_keyboard *wl_keyboard, uint32_t format, int32_t fd, uint32_t size + ) { + state_t* state = data; + if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) + abortf("O_o"); + char *map_shm = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + if (map_shm == MAP_FAILED) + abortf("Couldn't mmap new keymap ha-ha\n"); + + xkb_keymap_unref(state->xkb_keymap); + xkb_state_unref(state->xkb_state); + state->xkb_keymap = xkb_keymap_new_from_string( + state->xkb_context, map_shm, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); + munmap(map_shm, size); + close(fd); + state->xkb_state = xkb_state_new(state->xkb_keymap); + printf("Keymap changed!\n"); + memset(&state->first_0x80_keys, 0, sizeof(state->first_0x80_keys)); +} + +static void main_h_wl_keyboard_enter( + void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys + ) { + state_t* state = data; + printf("keyboard enter; keys pressed are:\n"); + uint32_t *key; + wl_array_for_each(key, keys) { + uint32_t actual_key = *key + 8; + xkb_keysym_t sym = xkb_state_key_get_one_sym(state->xkb_state, actual_key); + if (sym < 0x80) + state->first_0x80_keys[sym] = true; + } +} + +static void main_h_wl_keyboard_leave( + void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface + ) { + state_t* state = data; + memset(&state->first_0x80_keys, 0, sizeof(state->first_0x80_keys)); +} + +static void main_h_wl_keyboard_key( + void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t key_action + ) { + state_t* state = data; + uint32_t actual_key = key + 8; + /* It actually could be nokey keysym. You never know without checking */ + xkb_keysym_t keysym = xkb_state_key_get_one_sym(state->xkb_state, actual_key); + if (keysym < 0x80 && key_action == WL_KEYBOARD_KEY_STATE_RELEASED) { + state->first_0x80_keys[keysym] = false; + } else if (keysym < 0x80 && key_action == WL_KEYBOARD_KEY_STATE_PRESSED) { + state->first_0x80_keys[keysym] = true; + } +} + +static void main_h_wl_keyboard_modifiers( + void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, + uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group + ) { + state_t* state = data; + xkb_state_update_mask(state->xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group); +} + +static void main_h_wl_keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay){ + printf("Repeat timings changed: rate = %d, delay = %d", rate, delay); // todo: use this information +} + +static const struct wl_keyboard_listener main_h_wl_keyboard_listener = { + .keymap = main_h_wl_keyboard_keymap, + .enter = main_h_wl_keyboard_enter, + .leave = main_h_wl_keyboard_leave, + .key = main_h_wl_keyboard_key, + .modifiers = main_h_wl_keyboard_modifiers, + .repeat_info = main_h_wl_keyboard_repeat_info, +}; + + +static void main_h_wl_pointer_enter( + void *data, struct wl_pointer *wl_pointer, uint32_t serial, + struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y + ) { + state_t *state = data; +} + +static void main_h_wl_pointer_leave( + void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface + ) { + state_t *state = data; +} + +static void main_h_wl_pointer_motion( + void *data,struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y + ) { + state_t *state = data; +} + +static void main_h_wl_pointer_button( + void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t btn_action + ) { + state_t *state = data; +} + +static void main_h_wl_pointer_axis( + void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value + ) { + state_t *state = data; +} + +static void main_h_wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) { + state_t *state = data; +} + +const struct wl_pointer_listener main_h_wl_pointer_listener = { + .enter = main_h_wl_pointer_enter, + .leave = main_h_wl_pointer_leave, + .motion = main_h_wl_pointer_motion, + .button = main_h_wl_pointer_button, + .axis = main_h_wl_pointer_axis, + .frame = main_h_wl_pointer_frame +}; + +static void main_h_wl_seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capabilities) { + state_t* state = data; + if (capabilities & WL_SEAT_CAPABILITY_POINTER) { + state->pointer = wl_seat_get_pointer(wl_seat); + if (!state->pointer) + abortf("wl_seat_get_pointer\n"); + wl_pointer_add_listener(state->pointer, &main_h_wl_pointer_listener, state); + } + if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) { + state->keyboard = wl_seat_get_keyboard(wl_seat); + if (!state->keyboard) + abortf("wl_seat_get_keyboard\n"); + wl_keyboard_add_listener(state->keyboard, &main_h_wl_keyboard_listener, state); + } +} + +static void main_h_wl_seat_name(void* data, struct wl_seat* wl_seat, const char* name) { + printf("Our seat name: %s\n", name); +} + +static const struct wl_seat_listener main_h_wl_seat_listener = { + .capabilities = main_h_wl_seat_capabilities, + .name = main_h_wl_seat_name, +}; + +static void main_h_wl_registry_global( + void *data, struct wl_registry *wl_registry, uint32_t name, const char *interface, uint32_t version + ) { + state_t *state = data; + if (strcmp(interface, wl_compositor_interface.name) == 0) { + state->wl_compositor = wl_registry_bind(wl_registry, name, &wl_compositor_interface, 4); + if (!state->wl_compositor) + abortf("wl_registry_bind\n"); + } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { + state->xdg_wm_base = wl_registry_bind(wl_registry, name, &xdg_wm_base_interface, 1); + if (!state->xdg_wm_base) + abortf("wl_registry_bind\n"); + xdg_wm_base_add_listener(state->xdg_wm_base, &main_h_xdg_wm_base_listener, state); + } else if (strcmp(interface, wl_seat_interface.name) == 0) { + if (state->wl_seat) { + printf("We got second seat, but we only need one\n"); + return; + } + state->wl_seat = wl_registry_bind(wl_registry, name, &wl_seat_interface, 4); + if (!state->wl_seat) + abortf("wl_registry_bind\n"); + wl_seat_add_listener(state->wl_seat, &main_h_wl_seat_listener, state); + } +} + +static void main_h_wl_registry_global_remove(void *data, struct wl_registry *wl_registry, uint32_t name){ + +} + +static const struct wl_registry_listener main_h_wl_registry_listener = { + .global = main_h_wl_registry_global, + .global_remove = main_h_wl_registry_global_remove, +}; + +// static const struct wl_callback_listener main_h_wl_surface_frame_listener; +// +// static void main_h_wl_surface_frame_done(void *data, struct wl_callback *cb, uint32_t time){ +// state_t *state = data; +// wl_callback_destroy(cb); +// // todo: when I add multiple surfaces, gonna need to think of something smarter +// state->wl_callback = wl_surface_frame(state->wl_surface); +// if (!state->wl_callback) +// abortf("wl_surface_frame\n"); +// wl_callback_add_listener(state->wl_callback, &main_h_wl_surface_frame_listener, state); +// +// if (state->last_frame_time != 0) { +// update_state(state, time - state->last_frame_time); +// } +// +// state->swapchain.want_to_draw = true; +// try_drawing_frame(state); +// +// state->last_frame_time = time; +// } +// +// static const struct wl_callback_listener main_h_wl_surface_frame_listener = { +// .done = main_h_wl_surface_frame_done, +// }; + + +int main() { + state_t state = { .width = 800, .height = 480 }; + state.wl_display = wl_display_connect(NULL); + + if (!state.wl_display) + abortf("Could not connect"); + state.wl_registry = wl_display_get_registry(state.wl_display); + if (!state.wl_registry) + abortf("wl_display_get_registry"); + wl_registry_add_listener(state.wl_registry, &main_h_wl_registry_listener, &state); + wl_display_roundtrip(state.wl_display); + if (!state.wl_compositor) + abortf("No wl_compositor"); + if (!state.xdg_wm_base) + abortf("No xdg_wm_base"); + state.xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + if (!state.xkb_context) + abortf("xkb_context_new\n"); + state.wl_surface = wl_compositor_create_surface(state.wl_compositor); + if (!state.wl_surface) + abortf("wl_compositor_create_surface\n"); + state.xdg_surface = xdg_wm_base_get_xdg_surface( + state.xdg_wm_base, state.wl_surface); + if (!state.xdg_surface) + abortf("xdg_wm_base_get_xdg_surface\n"); + xdg_surface_add_listener(state.xdg_surface, &xdg_surface_listener, &state); + state.xdg_toplevel = xdg_surface_get_toplevel(state.xdg_surface); + if (!state.xdg_toplevel) + abortf("xdg_surface_get_toplevel\n"); + xdg_toplevel_add_listener(state.xdg_toplevel, &main_h_xdg_toplevel_listener, &state); + xdg_toplevel_set_title(state.xdg_toplevel, "r3"); + wl_surface_commit(state.wl_surface); + + // state.wl_callback = wl_surface_frame(state.wl_surface); + // if (!state.wl_callback) + // abortf("wl_surface_frame\n"); + // wl_callback_add_listener(state.wl_callback, &main_h_wl_surface_frame_listener, &state); + + MargaretInstanceAndItsDebug vk_instance_and_debug = MargaretInstanceAndItsDebug_new(true); + VkInstance vk_instance = vk_instance_and_debug.instance; + VkSurfaceKHR vk_surface = margaret_create_surface(vk_instance, state.wl_display, state.wl_surface); + VkPhysicalDevice physical_device = margaret_select_one_physical_device(vk_instance, vk_surface, cstr("nvidia"), cstr("NOT SPECIFIED")); + ResultMargaretChosenQueueFamiliesOrSpanU8 queue_fam_res = margaret_choose_good_queue_families(physical_device, vk_surface); + if (queue_fam_res.variant != Result_Ok) + abortf("queue_fam_res.variant != Result_Ok"); + MargaretChosenQueueFamilies queue_fam = queue_fam_res.ok; + VkDevice device = margaret_create_logical_device(physical_device, queue_fam); + VkQueue graphics_queue; + vkGetDeviceQueue(device, queue_fam.for_graphics, 0, &graphics_queue); + VkQueue presentation_queue; + vkGetDeviceQueue(device, queue_fam.for_graphics, 0, &presentation_queue); + ResultMargaretChosenSwapchainDetailsOrSpanU8 swapchain_details_res = margaret_choose_swapchain_details(physical_device, vk_surface); + if (swapchain_details_res.variant != Result_Ok) + abortf("swapchain_details_res.variant != Result_Ok"); + MargaretChosenSwapchainDetails swapchain_details = swapchain_details_res.ok; + VkRenderPass render_pass_0 = create_render_pass_0(device, swapchain_details.surface_format.format); + // PipelineHands pipeline_hands_0 = create_graphics_pipeline_0(device, render_pass_0, 0); + + MargaretSwapchainBundle swfb = MargaretSwapchainBundle_new(device, queue_fam, swapchain_details_res.ok, vk_surface, render_pass_0, NULL); + VkCommandPool command_pool = margaret_create_resettable_command_pool(device, queue_fam.for_graphics); + VkCommandBuffer rendering_command_buffer_0 = margaret_allocate_command_buffer(device, command_pool); + + Jane_r3 jane = Jane_r3_create(device); + + // todo: write what I need to write + int wl_display_loop_fd = wl_display_get_fd(state.wl_display); + struct pollfd mainloop_fds[1] = {(struct pollfd){}}; + while (wl_display_dispatch(state.wl_display)) { + if (state.closed) + break; + } + // if (state.wl_callback) + // wl_callback_destroy(state.wl_callback); + xdg_toplevel_destroy(state.xdg_toplevel); + xdg_surface_destroy(state.xdg_surface); + xdg_wm_base_destroy(state.xdg_wm_base); + wl_surface_destroy(state.wl_surface); + wl_compositor_destroy(state.wl_compositor); + if (state.pointer) + wl_pointer_destroy(state.pointer); + xkb_context_unref(state.xkb_context); + xkb_keymap_unref(state.xkb_keymap); + xkb_state_unref(state.xkb_state); + if (state.keyboard) + wl_keyboard_destroy(state.keyboard); + if (state.wl_seat) + wl_seat_destroy(state.wl_seat); + wl_registry_destroy(state.wl_registry); + wl_display_disconnect(state.wl_display); + return 0; +} diff --git a/src/l2/tests/r1/shader_compile.sh b/src/l2/tests/r3/shader_compile.sh similarity index 100% rename from src/l2/tests/r1/shader_compile.sh rename to src/l2/tests/r3/shader_compile.sh diff --git a/src/l2/tests/r1/shaders/glsl/0/0.frag b/src/l2/tests/r3/shaders/glsl/0/0.frag similarity index 100% rename from src/l2/tests/r1/shaders/glsl/0/0.frag rename to src/l2/tests/r3/shaders/glsl/0/0.frag diff --git a/src/l2/tests/r1/shaders/glsl/0/0.vert b/src/l2/tests/r3/shaders/glsl/0/0.vert similarity index 100% rename from src/l2/tests/r1/shaders/glsl/0/0.vert rename to src/l2/tests/r3/shaders/glsl/0/0.vert