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

This commit is contained in:
Андреев Григорий 2025-08-28 03:07:43 +03:00
parent 555712a19d
commit 19b08636a9
15 changed files with 1791 additions and 358 deletions

View File

@ -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)

View File

@ -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

View File

@ -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() {

View File

@ -8,9 +8,11 @@
#include <X11/Xutil.h>
#include <vulkan/vulkan.h>
#include <vulkan/vulkan_xlib.h>
#include <vulkan/vulkan_wayland.h>
#include "../core/stringop.h"
#include "../../l1/system/fileio.h"
#include "time.h"
#include <wayland-client.h>
// 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,

View File

@ -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

View File

@ -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);
}

View File

@ -12,50 +12,37 @@
#include "../../marie/graphics_geom.h"
#include "../../marie/rasterization.h"
#include <sys/poll.h>
#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);

65
src/l2/tests/r2/liza.h Normal file
View File

@ -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<Trait> 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

View File

@ -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

View File

@ -4,154 +4,63 @@
#include <math.h>
#include <stdio.h>
#include "../../../l1/system/pthread.h"
#include "../../margaret/time.h"
#include "../../marie/graphics_geom.h"
#include "../../marie/rasterization.h"
#include <fcntl.h>
#include <limits.h>
#include <sys/mman.h>
#include <wayland-client.h>
#include "../../../../gen/l_wl_protocols/xdg-shell-client.h"
#include <xkbcommon/xkbcommon.h>
#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, &registry_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);

View File

@ -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;

514
src/l2/tests/r3/r3.c Normal file
View File

@ -0,0 +1,514 @@
#include <fcntl.h>
#include <limits.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>
#include <errno.h>
#include <wayland-client.h>
#include "../../../../gen/l_wl_protocols/xdg-shell-client.h"
#include <xkbcommon/xkbcommon.h>
#include "../../../l1/core/util.h"
#include "../../margaret/vulkan.h"
#include <poll.h>
#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;
}