Upgrade to Pro — share decks privately, control downloads, hide ads and more …

低レイヤーから始める GUI

Fadis
January 15, 2023

低レイヤーから始める GUI

QtとかGTK+とかXとかWaylandとかそういうものに頼らないでLinux上でGUIする方法を解説します
これは2023年1月15日に行われた カーネル/VM探検隊 online part6での発表資料です

発表動画: https://youtu.be/nOLjuPb_dPo
ソースコード: https://github.com/Fadis/gct/tree/kernelvm_20230115

Fadis

January 15, 2023
Tweet

More Decks by Fadis

Other Decks in Programming

Transcript

  1. ௿ϨΠϠʔ͔Β࢝ΊΔ GUI NAOMASA MATSUBAYASHI (@fadis_)

  2. GPU

  3. ϏσΦϝϞϦ GPUͷ2ͭͷػೳ ϏσΦϝϞϦͷ಺༰Λը໘ʹ දࣔ͢Δ ϏσΦϝϞϦ্ͷ஋ΛಡΈॻ͖ͯ͠ ܭࢉ͢Δ

  4. GPU࢖͍͍ͨ GPU࢖͍͍ͨ ϓϩηε1 ϓϩηε2 εέδϡʔϥ ࠓ͸ϓϩηε1ʹGPUΛ࢖ΘͤΑ͏ ଴ػ ϏσΦϝϞϦ ϓϩηε1ͷ σʔλ

    ϓϩηε2ͷ σʔλ ܭࢉͷػೳΛڞ༗͢Δ
  5. GPU࢖͍͍ͨ GPU࢖͍͍ͨ ϓϩηε1 ϓϩηε2 εέδϡʔϥ ࠓ͸ϓϩηε2ʹGPUΛ࢖ΘͤΑ͏ ଴ػ ϏσΦϝϞϦ ϓϩηε1ͷ σʔλ

    ϓϩηε2ͷ σʔλ ܭࢉͷػೳΛڞ༗͢Δ
  6. ը໘ʹ Λද͍ࣔͨ͠ ը໘ʹ Λද͍ࣔͨ͠ ϓϩηε1 ϓϩηε2 ϏσΦϝϞϦ ? දࣔͷػೳΛڞ༗͢Δํ๏͸ࣗ໌Ͱ͸ͳ͍ ?

  7. Χʔωϧۭؒ DRM/KMS Ϣʔβۭؒ Ϣʔβۭؒ Ϣʔβۭؒ εέδϡʔϥ Ϩϯμʔϊʔυ ܭࢉ දࣔ LinuxͰͷ

    GPUͷѻ͍ Linux ϓϥΠϚϦϊʔυ
  8. Χʔωϧۭؒ DRM/KMS Ϣʔβۭؒ Ϣʔβۭؒ Ϣʔβۭؒ εέδϡʔϥ Ϩϯμʔϊʔυ ܭࢉ දࣔ LinuxͰͷ

    GPUͷѻ͍ Linux ϓϥΠϚϦϊʔυ Ϩϯμʔϊʔυ /dev/dri/renderD128ͱ͔ GPUͷϝϞϦΛ֬อͰ͖Δ GPUͰܭࢉͰ͖Δ දࣔ͸Ͱ͖ͳ͍ ͜ͷσόΠεΛհͯ͠ ෳ਺ͷϓϩηε͕ಉ࣌ʹ ͜ͷσόΠεΛ։͚Δ
  9. ϧۭؒ DRM/KMS Ϣʔβۭؒ Ϣʔβۭؒ Ϣʔβۭؒ εέδϡʔϥ Ϩϯμʔϊʔυ ܭࢉ දࣔ uxͰͷ

    Uͷѻ͍ ux ϓϥΠϚϦϊʔυ ϓϥΠϚϦϊʔυ /dev/dri/card0ͱ͔ GPUͷϝϞϦΛ֬อͰ͖Δ GPUͰܭࢉͰ͖Δ දࣔ΋Ͱ͖Δ ͜ͷσόΠεΛհͯ͠ ಉ࣌ʹ͜ͷσόΠεΛ ։͚Δϓϩηε͸1͚ͭͩ
  10. ϏσΦϝϞϦ Χʔωϧۭؒ Ϣʔβۭؒ Ϣʔβۭؒ Ϣʔβۭؒ ද ࣔ Linux αʔό ΫϥΠΞϯτ

    ΫϥΠΞϯτ ॻ ͘ ಡ Ή Ϩϯμʔϊʔυ ಡ Ή ॻ ͘ ॻ ͘ ϓϥΠϚϦϊʔυ ͓લɺڞ༗ϝϞϦʹॻ͘ Զɺڞ༗ϝϞϦΛಡΉ ॻ͍ͨ Window System
  11. ϏσΦϝϞϦ Χʔωϧۭؒ Ϣʔβۭؒ Ϣʔβۭؒ Ϣʔβۭؒ ද ࣔ Linux Wayland ίϯϙδλ

    Wayland ΫϥΠΞϯτ ॻ ͘ ಡ Ή Ϩϯμʔϊʔυ ಡ Ή ॻ ͘ ॻ ͘ ϓϥΠϚϦϊʔυ ͓લɺڞ༗ϝϞϦʹॻ͘ Զɺڞ༗ϝϞϦΛಡΉ ॻ͍ͨ Wayland Wayland ΫϥΠΞϯτ
  12. ϏσΦϝϞϦ Χʔωϧۭؒ Ϣʔβۭؒ Linux Mesa ϋʔυ΢ΣΞඇґଘͷ GPUͷૢ࡞ ಛఆͷGPU޲͚ͷ ίϚϯυ Ϩϯμʔϊʔυ

    libdrm QtɺGTK+౳ ΞϓϦέʔγϣϯ ΢Οδοτͷૢ࡞ ڞ༗ϝϞϦ ϓϥΠϚϦϊʔυ
  13. ϏσΦϝϞϦ Χʔωϧۭؒ Ϣʔβۭؒ Linux Mesa Ϩϯμʔϊʔυ libdrm QtɺGTK+౳ ΞϓϦέʔγϣϯ Ϣʔβۭؒ

    αʔό xcb ڞ༗ϝϞϦ͍ͩ͘͞ ڞ༗ϝϞϦ ͜ΕΛ࢖ͬͯ ͜͜ʹඳ͍ͯ ڞ༗ϝϞϦ ͍ͩ͘͞ Mesa libdrm ϓϥΠϚϦϊʔυ ͜ΕΛ ࢖ͬͯ
  14. ϏσΦϝϞϦ Χʔωϧۭؒ Ϣʔβۭؒ Linux Mesa Ϩϯμʔϊʔυ libdrm QtɺGTK+౳ ΞϓϦέʔγϣϯ Ϣʔβۭؒ

    αʔό xcb ඳ͖ऴΘͬͨ Mesa libdrm ϓϥΠϚϦϊʔυ ͔͜͜Β ͜͜΁ ίϐʔ ඳ͖ऴΘͬͨ ϋʔυ΢ΣΞඇґଘͷ GPUͷૢ࡞ ಛఆͷGPU޲͚ͷ ίϚϯυ
  15. ϏσΦϝϞϦ Χʔωϧۭؒ Ϣʔβۭؒ Linux Mesa Ϩϯμʔϊʔυ libdrm QtɺGTK+౳ ΞϓϦέʔγϣϯ Ϣʔβۭؒ

    αʔό xcb Mesa libdrm ϓϥΠϚϦϊʔυ ͜ΕΛදࣔͯ͠ ϋʔυ΢ΣΞඇґଘͷ GPUͷૢ࡞ ಛఆͷGPU޲͚ͷ ίϚϯυ
  16. ϏσΦϝϞϦ Χʔωϧۭؒ Ϣʔβۭؒ Linux Mesa Ϩϯμʔϊʔυ libdrm ΞϓϦέʔγϣϯ Ϣʔβۭؒ αʔό

    xcb ڞ༗ϝϞϦ Mesa libdrm ϓϥΠϚϦϊʔυ ϋʔυ΢ΣΞඇґଘͷ GPUͷૢ࡞ ಛఆͷGPU޲͚ͷ ίϚϯυ ΞϓϦέʔγϣϯ͕ Vulkan΍OpenGLΛ ௚઀஻Δ৔߹
  17. Ϣʔβۭؒ Mesa ΞϓϦέʔγϣϯ ϋʔυ΢ΣΞඇґଘͷ GPUͷૢ࡞ ಛఆͷGPU޲͚ͷ ίϚϯυ ϏσΦϝϞϦ Χʔωϧۭؒ Linux

    Ϩϯμʔϊʔυ ϓϥΠϚϦϊʔυ libdrm ඳ͍ͯ ΋͠ը໘ʹද͕ࣔͨ͠Δϓϩηε͕ ϗετʹ1͔ͭ͠ͳ͍ͳΒ
  18. ϏσΦϝϞϦ Χʔωϧۭؒ Ϣʔβۭؒ Linux Mesa Ϩϯμʔϊʔυ ΞϓϦέʔγϣϯ ϓϥΠϚϦϊʔυ ΋͠ը໘ʹද͕ࣔͨ͠Δϓϩηε͕ ϗετʹ1͔ͭ͠ͳ͍ͳΒ

    Q. ͜Ε͕Ͱ͖ΔഺͰ͸ libdrm ͜ΕΛදࣔͯ͠ ϋʔυ΢ΣΞඇґଘͷ GPUͷૢ࡞ ಛఆͷGPU޲͚ͷ ίϚϯυ
  19. VK_KHR_display A. Ͱ͖Δ

  20. Instance Extensions: count = 20 =============================== VK_EXT_acquire_drm_display : extension revision

    1 VK_EXT_acquire_xlib_display : extension revision 1 VK_EXT_debug_report : extension revision 10 VK_EXT_debug_utils : extension revision 2 VK_EXT_direct_mode_display : extension revision 1 VK_EXT_display_surface_counter : extension revision 1 VK_KHR_device_group_creation : extension revision 1 VK_KHR_display : extension revision 23 VK_KHR_external_fence_capabilities : extension revision 1 VK_KHR_external_memory_capabilities : extension revision 1 VK_KHR_external_semaphore_capabilities : extension revision 1 VK_KHR_get_display_properties2 : extension revision 1 VK_KHR_get_physical_device_properties2 : extension revision 2 VK_KHR_get_surface_capabilities2 : extension revision 1 VK_KHR_portability_enumeration : extension revision 1 VK_KHR_surface : extension revision 25 VK_KHR_surface_protected_capabilities : extension revision 1 VK_KHR_wayland_surface : extension revision 6 VK_KHR_xcb_surface : extension revision 6 ͜Ε
  21. #include <iostream> #include <cstdint> #include <vector> #include <vulkan/vulkan.hpp> #include <nlohmann/json.hpp>

    int main( int argc, const char *argv[] ) { #ifdef VULKAN_HPP_DISPATCH_LOADER_DYNAMIC // Vulkan-Hpp͔ΒvkCreateInstanceΛݺ΂ΔΑ͏ʹ͢Δ vk::DynamicLoader dl; PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = dl.getProcAddress<PFN_vkGetInstanceProcAddr>( "vkGetInstanceProcAddr" ); VULKAN_HPP_DEFAULT_DISPATCHER.init( vkGetInstanceProcAddr ); #endif const auto app_info = vk::ApplicationInfo() // ΞϓϦέʔγϣϯͷ໊લ .setPApplicationName( argc ? argv[ 0 ] : "my_application" ) // ΞϓϦέʔγϣϯͷόʔδϣϯ .setApplicationVersion( VK_MAKE_VERSION(1, 0, 0) ) // Τϯδϯͷ໊લ
  22. const std::vector< const char * > layers{ "VK_LAYER_KHRONOS_validation" }; //

    ҎԼͷ֦ுΛ࢖͏ const std::vector< const char* > exts{ "VK_KHR_surface", // දࣔ͢ΔҝͷඳըΛߦ͏ "VK_KHR_display" // σΟεϓϨΠͷૢ࡞Λߦ͏ }; // ΠϯελϯεΛ࡞੒ auto instance = vk::createInstanceUnique( vk::InstanceCreateInfo() // ΞϓϦέʔγϣϯͷ৘ใΛࢦఆ .setPApplicationInfo( &app_info ) // ࢖༻͢ΔϨΠϠʔΛࢦఆ .setPEnabledLayerNames( layers ) // ࢖༻͢Δ֦ுΛࢦఆ .setPEnabledExtensionNames( exts ) );
  23. const auto devices = instance->enumeratePhysicalDevices(); for( const auto &device: devices

    ) { // GPUͷ৘ใΛऔಘ const auto props = device.getProperties(); // ͜ͷGPUʹܨ͕͍ͬͯΔσΟεϓϨΠͷ৘ใΛऔಘ const auto displays = device.getDisplayPropertiesKHR(); for( const auto &display: displays ) { std::cout << "GPU " << props.deviceName << " ʹσΟεϓϨΠ " << display.displayName << " ͕઀ଓ͞Ε͍ͯΔ" << std::endl; std::cout << " ࠷େղ૾౓ : " << display.physicalResolution.width << "x" << display.physicalResolution.height << std::endl; // σΟεϓϨΠͷରԠϞʔυΛऔಘ const auto modes = device.getDisplayModePropertiesKHR( display.display ); for( const auto &mode: modes ) { std::cout << " ରԠϞʔυ : " << GPUͱσΟεϓϨΠͷ৘ใΛऔಘ
  24. for( const auto &display: displays ) { std::cout << "GPU

    " << props.deviceName << " ʹσΟεϓϨΠ " << display.displayName << " ͕઀ଓ͞Ε͍ͯΔ" << std::endl; std::cout << " ࠷େղ૾౓ : " << display.physicalResolution.width << "x" << display.physicalResolution.height << std::endl; // σΟεϓϨΠͷରԠϞʔυΛऔಘ const auto modes = device.getDisplayModePropertiesKHR( display.display ); for( const auto &mode: modes ) { std::cout << " ରԠϞʔυ : " << mode.parameters.visibleRegion.width << "x" << mode.parameters.visibleRegion.height << "@" << float( mode.parameters.refreshRate ) / 1000.f << "Hz" << std::endl; } } } ར༻ՄೳͳදࣔϞʔυΛऔಘ
  25. $ ./src/example/list_displays/list_displays Vulkan 1.3.224 GPU NVIDIA GeForce RTX 2070 ʹσΟεϓϨΠ

    Asustek Computer Inc PA32UCX (DP-0) ͕઀ଓ͞Ε͍ͯΔ ࠷େղ૾౓ : 3840x2160 ରԠϞʔυ : [email protected] ରԠϞʔυ : [email protected] ରԠϞʔυ : [email protected] ରԠϞʔυ : [email protected] ରԠϞʔυ : [email protected] ରԠϞʔυ : [email protected] ରԠϞʔυ : [email protected]
  26. // Πϯελϯε͕αϙʔτ͢ΔVulkanͷόʔδϣϯΛऔಘ const auto version = vk::enumerateInstanceVersion(); std::cout << "Vulkan

    " << VK_VERSION_MAJOR( version ) << "." << VK_VERSION_MINOR( version ) << "." << VK_VERSION_PATCH( version ) << std::endl; // 1ݸ໨ͷGPUʹܨ͕͍ͬͯΔ1ݸ໨ͷσΟεϓϨΠΛ1ݸ໨ͷදࣔϞʔυͰ࢖͏ const auto devices = instance->enumeratePhysicalDevices(); if( devices.empty() ) std::abort(); const auto displays = devices[ 0 ].getDisplayPropertiesKHR(); if( displays.empty() ) std::abort(); const auto modes = devices[ 0 ].getDisplayModePropertiesKHR( displays[ 0 ].display ); if( modes.empty() ) std::abort(); // ͜ͷ৚݅ͰαʔϑΣεΛ࡞Δ const auto surface = instance->createDisplayPlaneSurfaceKHRUnique( vk::DisplaySurfaceCreateInfoKHR() .setDisplayMode( modes[ 0 ].displayMode ) .setImageExtent( modes[ 0 ].parameters.visibleRegion )
  27. if( modes.empty() ) std::abort(); // ͜ͷ৚݅ͰαʔϑΣεΛ࡞Δ const auto surface =

    instance->createDisplayPlaneSurfaceKHRUnique( vk::DisplaySurfaceCreateInfoKHR() .setDisplayMode( modes[ 0 ].displayMode ) .setImageExtent( modes[ 0 ].parameters.visibleRegion ) ); // αʔϑΣεͰར༻ՄೳͳϐΫηϧϑΥʔϚοτΛऔಘ͢Δ const auto available_formats = devices[ 0 ].getSurfaceFormatsKHR( *surface ); for( const auto &format: available_formats ) { std::cout << "ར༻ՄೳͳϑΥʔϚοτ : " << nlohmann::json( format ) << std::endl; } // ཉ͍͠ϐΫηϧύοΩϯάํ๏ const std::vector< vk::Format > expected_formats{ vk::Format::eA2B10G10R10UnormPack32, // 30bit BGR vk::Format::eA8B8G8R8UnormPack32, // 24bit BGR
  28. const std::vector< vk::Format > expected_formats{ vk::Format::eA2B10G10R10UnormPack32, // 30bit BGR vk::Format::eA8B8G8R8UnormPack32,

    // 24bit BGR vk::Format::eB8G8R8A8Unorm, // 24bit RGB vk::Format::eR5G6B5UnormPack16 // 16bit RGB }; // ৭ۭ͕ؒsRGBͰ্هͷํ๏ͰύοΩϯά͞ΕͨϑΥʔϚοτΛ୳͢ vk::Format selected_format = vk::Format::eUndefined; for( const auto &e: expected_formats ) { if( std::find_if( available_formats.begin(), available_formats.end(), [e]( const auto &f ) { return f.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear && f.format == e; } ) != available_formats.end() ) { selected_format = e; break; } }
  29. // αʔϑΣεͷ৘ใΛऔಘ const auto surface_capabilities = devices[ 0 ].getSurfaceCapabilitiesKHR( *surface

    ); std::cout << "αʔϑΣεͷ࠷খΠϝʔδ਺ : " << surface_capabilities.minImageCount << std::endl; std::cout << "αʔϑΣεͷ࠷େΠϝʔδ਺ : " << surface_capabilities.maxImageCount << std::endl; // σόΠεʹඋΘ͍ͬͯΔΩϡʔΛऔಘ const auto queue_props = devices[ 0 ].getQueueFamilyProperties(); uint32_t queue_family_index = 0u; // ඳըཁٻΛड͚෇͚ΔΩϡʔΛ୳͢ for( uint32_t i = 0; i < queue_props.size(); ++i ) { if( queue_props[ i ].queueFlags & vk::QueueFlagBits::eGraphics ) { queue_family_index = i; break; } } αʔϑΣε্Ͱར༻ՄೳͳΠϝʔδͷ਺Λऔಘ
  30. ϏσΦϝϞϦ Χʔωϧۭؒ Ϣʔβۭؒ Linux Mesa ΞϓϦέʔγϣϯ දࣔ ϋʔυ΢ΣΞඇґଘͷ GPUͷૢ࡞ ಛఆͷGPU޲͚ͷ

    ίϚϯυ ඳը ϓϥΠϚϦϊʔυ ੾Γସ͑ දࣔத libdrm ඳ͖͔͚͕ දࣔ͞Εͯ͸͍͚ͳ͍ αʔϑΣε͸ 2ը໘෼Ҏ্ͷαΠζͰ ֬อ͞ΕΔ ඳ͖ऴΘͬͨΒ දࣔ͢ΔΠϝʔδΛ ੾Γସ͑Δ
  31. // σόΠεʹඋΘ͍ͬͯΔΩϡʔΛऔಘ const auto queue_props = devices[ 0 ].getQueueFamilyProperties(); uint32_t

    queue_family_index = 0u; // ඳըཁٻΛड͚෇͚ΔΩϡʔΛ୳͢ for( uint32_t i = 0; i < queue_props.size(); ++i ) { if( queue_props[ i ].queueFlags & vk::QueueFlagBits::eGraphics ) { queue_family_index = i; break; } } // ҎԼͷσόΠε֦ுΛ࢖͏ std::vector< const char* > dexts{ "VK_KHR_swapchain" // εϫοϓνΣΠϯΛ࢖͏ }; const float priority = 0.0f; // ඳըཁٻΛड͚෇͚ΔΩϡʔΛ1͍ͭͩ͘͞ std::vector< vk::DeviceQueueCreateInfo > queues{ ෺ཧσόΠε શछରԠ શछରԠ ܭࢉઐ༻ ܭࢉઐ༻ సૹઐ༻ Ωϡʔ ͜͜ʹίϚϯυΛྲྀ͢ͱ GPUͰίϚϯυ͕ ࣮ߦ͞ΕΔ
  32. // σόΠεʹඋΘ͍ͬͯΔΩϡʔΛऔಘ const auto queue_props = devices[ 0 ].getQueueFamilyProperties(); uint32_t

    queue_family_index = 0u; // ඳըཁٻΛड͚෇͚ΔΩϡʔΛ୳͢ for( uint32_t i = 0; i < queue_props.size(); ++i ) { if( queue_props[ i ].queueFlags & vk::QueueFlagBits::eGraphics ) { queue_family_index = i; break; } } // ҎԼͷσόΠε֦ுΛ࢖͏ std::vector< const char* > dexts{ "VK_KHR_swapchain" // εϫοϓνΣΠϯΛ࢖͏ }; const float priority = 0.0f; // ඳըཁٻΛड͚෇͚ΔΩϡʔΛ1͍ͭͩ͘͞ std::vector< vk::DeviceQueueCreateInfo > queues{ ෺ཧσόΠε GPUͷৼΔ෣͍ͷઃఆ ࿦ཧσόΠε ࢖͏෺ཧσόΠε ࢖͏Ωϡʔ GPUͷઃఆ ࿦ཧσόΠε + + =
  33. const float priority = 0.0f; // ඳըཁٻΛड͚෇͚ΔΩϡʔΛ1͍ͭͩ͘͞ std::vector< vk::DeviceQueueCreateInfo >

    queues{ vk::DeviceQueueCreateInfo() .setQueueFamilyIndex( queue_family_index ) .setQueuePriorities( priority ) }; // ࿦ཧσόΠεΛ࡞Δ auto device = devices[ 0 ].createDeviceUnique( vk::DeviceCreateInfo() // ͜ͷΩϡʔΛ࢖͏ .setQueueCreateInfos( queues ) // ͜ͷσόΠε֦ுΛ࢖͏ .setPEnabledExtensionNames( dexts ) ); // σόΠε͔ΒΩϡʔΛऔಘ auto queue = device->getQueue( queue_family_index, 0u );
  34. // εϫοϓνΣʔϯΛ࡞Δ const auto swapchain = device->createSwapchainKHRUnique( vk::SwapchainCreateInfoKHR() // ͜ͷαʔϑΣεʹඳը݁ՌΛૹΔ

    .setSurface( *surface ) // εϫοϓνΣʔϯͷΠϝʔδͷ਺ .setMinImageCount( surface_capabilities.minImageCount ) // εϫοϓνΣʔϯͷΠϝʔδͷϑΥʔϚοτ .setImageFormat( selected_format ) // εϫοϓνΣʔϯͷΠϝʔδͷ৭ۭؒ .setImageColorSpace( vk::ColorSpaceKHR::eSrgbNonlinear ) // εϫοϓνΣʔϯͷΠϝʔδͷେ͖͞ .setImageExtent( surface_capabilities.currentExtent ) // ϨΠϠʔ͸1ͭ .setImageArrayLayers( 1u ) // εϫοϓνΣʔϯͷΠϝʔδ͸సૹઌʹ΋࢖͑Δඞཁ͕͋Δ .setImageUsage( vk::ImageUsageFlagBits::eTransferDst αʔϑΣεͷͲͷΠϝʔδ͕දࣔத͔Λอ࣋͢Δ දࣔதͩͬͨΠϝʔδ͕දࣔதͰͳ͘ͳͬͨΒڭ͑ͯ͘ΕΔ εϫοϓνΣΠϯ
  35. // εϫοϓνΣʔϯͷΠϝʔδΛऔಘ͢Δ const auto swapchain_images = device- >getSwapchainImagesKHR( *swapchain );

    std::cout << "εϫοϓνΣΠϯͷΠϝʔδͷ਺ : " << swapchain_images.size() << std::endl; // ίϚϯυϓʔϧΛ࡞Δ const auto command_pool = device->createCommandPoolUnique( vk::CommandPoolCreateInfo() .setQueueFamilyIndex( 0u ) ); { // ίϚϯυόοϑΝΛ࡞Δ const auto command_buffers = device->allocateCommandBuffersUnique( vk::CommandBufferAllocateInfo() .setCommandPool( *command_pool ) .setLevel( vk::CommandBufferLevel::ePrimary ) .setCommandBufferCount( 1u ) εϫοϓνΣΠϯ͔ΒશͯͷΠϝʔδΛऔಘ͠
  36. // εϫοϓνΣΠϯͷΠϝʔδΛ੨ͰృΓͭͿ͢ command_buffer->clearColorImage( image, // ͜ͷΠϝʔδΛ vk::ImageLayout::eTransferDstOptimal, // ੨ vk::ClearColorValue()

    .setFloat32( std::array< float, 4u >{ 0.f, 0.f, 1.f, 1.f } ), { vk::ImageSubresourceRange() // ৭Λॻ͖׵͑Δ .setAspectMask( vk::ImageAspectFlagBits::eColor ) // 0൪໨ͷϛοϓϨϕϧ .setBaseMipLevel( 0u ) .setLevelCount( 1u ) // 0൪໨ͷϨΠϠʔ .setBaseArrayLayer( 0u ) .setLayerCount( 1u ) } ); ੨ͰృΓͭͿ͢ίϚϯυΛ༻ҙ
  37. .setAspectMask( vk::ImageAspectFlagBits::eColor ) .setBaseMipLevel( 0u ) .setLevelCount( 1u ) .setBaseArrayLayer(

    0u ) .setLayerCount( 1u ) ) } ); } // ίϚϯυόοϑΝͷه࿥ΛऴΘΔ command_buffer->end(); // ίϚϯυόοϑΝΛΩϡʔʹྲྀ͢ queue.submit( vk::SubmitInfo() .setCommandBufferCount( 1u ) .setPCommandBuffers( &*command_buffer ), VK_NULL_HANDLE ); // Ωϡʔʹྲྀͨ͠ίϚϯυ͕׬ྃ͢Δ·Ͱ଴ͭ queue.waitIdle(); } ը໘ʹදࣔΛ࢝ΊΔલʹ αʔϑΣεΛ੨ͰృΓͭͿ͓ͯ͘͠ ίϚϯυΛΩϡʔʹྲྀͯ͠׬ྃΛ଴ͭ
  38. uint32_t current_frame = 0u; while( 1 ) { const auto

    begin_time = std::chrono::high_resolution_clock::now(); // εϫοϓνΣΠϯ͔Β࣍ʹॻ͘΂͖ΠϝʔδͷindexΛ໯͏ std::uint32_t image_index = device->acquireNextImageKHR( // ͜ͷεϫοϓνΣʔϯ͔Β *swapchain, // Πϝʔδ͕໯͑Δ·Ͱ͍͘ΒͰ΋଴ͭ std::numeric_limits< std::uint64_t >::max(), // Πϝʔδ͕ද͔ࣔΒ֎ΕͨΒ͜ͷηϚϑΥʹ௨஌ *image_acquired[ current_frame ], VK_NULL_HANDLE ).value; // ΠϝʔδΛॻ͖׵͑ͳ͍Ͱ͙͢ʹදࣔʹ·Θ͢ if( queue.presentKHR( vk::PresentInfoKHR() εϫοϓνΣΠϯ͔Β ඳ͍ͯ΋େৎ෉ͳαʔϑΣεͷΠϝʔδͷΠϯσοΫεΛ໯͍
  39. VK_NULL_HANDLE ).value; // ΠϝʔδΛॻ͖׵͑ͳ͍Ͱ͙͢ʹදࣔʹ·Θ͢ if( queue.presentKHR( vk::PresentInfoKHR() .setWaitSemaphoreCount( 1u )

    // ͜ͷηϑΥʹ௨஌͕དྷΔ·Ͱ଴͔ͬͯΒ .setPWaitSemaphores( &*image_acquired[ current_frame ] ) .setSwapchainCount( 1 ) // ͜ͷεϫοϓνΣʔϯͷ .setPSwapchains( &*swapchain ) // ͜ͷΠϝʔδΛαʔϑΣεʹૹΔ .setPImageIndices( &image_index ) ) != vk::Result::eSuccess ) std::abort(); ++current_frame; current_frame %= swapchain_images.size(); // 1ϑϨʔϜͷද͕ࣔ࣌ؒܦա͢Δ·Ͱ଴ͭ ಺༰Λॻ͖׵͑ͳ͍Ͱ͙͢ʹදࣔʹ·Θ͢
  40. // ͜ͷηϑΥʹ௨஌͕དྷΔ·Ͱ଴͔ͬͯΒ .setPWaitSemaphores( &*image_acquired[ current_frame ] ) .setSwapchainCount( 1 )

    // ͜ͷεϫοϓνΣʔϯͷ .setPSwapchains( &*swapchain ) // ͜ͷΠϝʔδΛαʔϑΣεʹૹΔ .setPImageIndices( &image_index ) ) != vk::Result::eSuccess ) std::abort(); ++current_frame; current_frame %= swapchain_images.size(); // 1ϑϨʔϜͷද͕ࣔ࣌ؒܦա͢Δ·Ͱ଴ͭ const auto end_time = std::chrono::high_resolution_clock::now(); const auto elapsed_time = end_time - begin_time; if( elapsed_time < frame_time ) { const auto sleep_for = frame_time - elapsed_time; std::this_thread::sleep_for( sleep_for ); } } ͜ΕΛσΟεϓϨΠͷϦϑϨογϡϨʔτʹಉظͯ͠܁Γฦ͢
  41. https://speakerdeck.com/fadis/imadokifalsevulkan ৄ͍͠Vulkanͷ࢖͍ํ͸ Ҏલղઆͨ͠΍͕ͭ͋Δ͔Β ͦͬͪΛݟͯͶ

  42. None
  43. None
  44. UIΛඳ͖͍ͨ

  45. UIͷத਎͸ Πϕϯτ͕ى͜Βͳ͍ݶΓ มԽ͠ͳ͍ UIͷ֎͸ ຖϑϨʔϜಈ͔͘΋͠Εͳ͍͚Ͳ

  46. ϏσΦϝϞϦ ΞϓϦέʔγϣϯ ຖϑϨʔϜߦ͏ ಺༰͕มΘ͚ͬͨ࣌ͩߦ͏ ίϐʔ ඳը ඳը දࣔ UIͷத਎͚ͩผͷΠϝʔδʹඳը UIͷத਎͕มԽ͚ͨ࣌ͩ͠ߋ৽͠Α͏

  47. จࣈ จࣈจࣈจࣈ จࣈจࣈจࣈจࣈจࣈจࣈจࣈจࣈ จࣈจࣈจࣈจࣈจࣈจࣈ จࣈจࣈจࣈ จࣈจࣈจࣈจࣈจࣈจࣈจࣈจࣈ จࣈจࣈจࣈจࣈจࣈจࣈ จࣈ ০Βͳ͍ΠϚυΩͷUIͷߏ੒ཁૉ ௕ํܗ

    จࣈ ه߸ ը૾
  48. ௕ํܗ จࣈ ه߸ ը૾ SVG౳ͷ ϕΫλը૾ TrueType౳ͷ ςΫενϟΛషͬͨ ςΫενϟΛష͍ͬͯͳ͍ ௕ํܗ

  49. ௕ํܗ จࣈ ه߸ ը૾ SVG౳ͷ ϕΫλը૾ TrueType౳ͷ ςΫενϟΛషͬͨ ςΫενϟΛష͍ͬͯͳ͍ ௕ํܗ

  50. ௕ํܗ ΛGPUͰେྔʹඳ͖͍ͨ

  51. GPU CPU ௕ํܗ ͷඳըཁٻΛେྔʹ౤͛Δͱ CPUଆͷෛ୲͕όΧʹͳΒͳ͍ ௕ํܗ ͸Α࣍ GPU͸εϨου਺ͰੑೳΛՔ͙ͷͰ খ͍͞ඳըཁٻͰ͸ੑೳ͕Ͱͳ͍

  52. CPU͔Β΋GPU͔Β΋ݟ͑ΔϝϞϦ GPU CPU ௕ํܗ × 500 Πϯελϯγϯά 1ίϚϯυͰಉ͡τϙϩδͷਤܗΛେྔʹඳ͘ ௕ํܗ0ͷ഑ஔ ௕ํܗ1ͷ഑ஔ

    ௕ํܗ2ͷ഑ஔ ௕ํܗ3ͷ഑ஔ ⋮
  53. struct rect_info { vec4 color; u16vec2 offset; u16vec2 extent; u16vec2

    semantic; uint16_t texid; uint16_t depth; }; layout(std430,binding = 0) buffer Uniforms { mat4 world_matrix; rect_info rects[ 65536 ]; float scale_factor; } uniforms; CPU͔Βॻ͍ͯGPUͰಡΉ৘ใͷߏ଄ମ ϐΫηϧ࠲ඪܥ΁ͷม׵ߦྻ (−1, − 1) (−1,1) (1,1) (1, − 1) VulkanͷεΫϦʔϯ࠲ඪܥ ϐΫηϧ࠲ඪܥ (0,2040) (0,0) (3840,0) (3840,2040)
  54. struct rect_info { vec4 color; u16vec2 offset; u16vec2 extent; u16vec2

    semantic; uint16_t texid; uint16_t depth; }; layout(std430,binding = 0) buffer Uniforms { mat4 world_matrix; rect_info rects[ 65536 ]; float scale_factor; } uniforms; CPU͔Βॻ͍ͯGPUͰಡΉ৘ใͷߏ଄ମ ௕ํܗͷ৘ใͷ഑ྻ ͜ͷ৭Ͱ ͜ͷϐΫηϧ͔Β ͜ͷαΠζͷ ௕ํܗΛඳ͍ͯ (0,2040) (0,0) (3840,0) (3840,2040)
  55. struct rect_info { vec4 color; u16vec2 offset; u16vec2 extent; u16vec2

    semantic; uint16_t texid; uint16_t depth; }; layout(std430,binding = 0) buffer Uniforms { mat4 world_matrix; rect_info rects[ 65536 ]; float scale_factor; } uniforms; Ұ౓ͷDrawͰ࠷େ65536ݸͷ Λඳ͘ ௕ํܗ ͷ਺͕65536ݸΛ௒͑Δ৔߹͸ෳ਺ͷίϚϯυʹ෼͚Δ ௕ํܗ
  56. void main() { vec4 pixel_pos = vec4( ( input_position.x *

    float( uint( uniforms.rects[ gl_InstanceIndex ].extent.x ) ) + float( uint( uniforms.rects[ gl_InstanceIndex ].offset.x ) ) ) * uniforms.scale_factor, ( input_position.y * float( uint( uniforms.rects[ gl_InstanceIndex ].extent.y ) ) + float( uint( uniforms.rects[ gl_InstanceIndex ].offset.y ) ) ) * uniforms.scale_factor, float( 65535 - uint( uniforms.rects[ gl_InstanceIndex ].depth ) ) / 65536.f, 1.0 ); gl_Position = uniforms.world_matrix * pixel_pos; output_color = vec4( uniforms.rects[ gl_InstanceIndex ].color.r, uniforms.rects[ gl_InstanceIndex ].color.g, uniforms.rects[ gl_InstanceIndex ].color.b, 1.0 ); Πϯελϯεຖʹ͜ͷ஋͕มΘΔ (0,1) (0,0) (1,0) (1,1) ΛඞཁͳαΠζʹͯ͠ εΫϦʔϯ࠲ඪܥʹม׵ͯ͠ϥελϥΠβ΁
  57. void rect_renderer::add_rectangle_internal( const vk::Rect2D &rect, const std::array< float, 4u >

    &color, std::uint16_t depth, std::uint16_t semantic_id, std::uint16_t texid ) { const auto instance_id = instance_count++; uniforms_host.rects[ instance_id ].offset[ 0 ] = rect.offset.x * scale; uniforms_host.rects[ instance_id ].offset[ 1 ] = rect.offset.y * scale; uniforms_host.rects[ instance_id ].extent[ 0 ] = rect.extent.width * scale; uniforms_host.rects[ instance_id ].extent[ 1 ] = rect.extent.height * scale; uniforms_host.rects[ instance_id ].color = glm::vec4( color[ 0 ], color[ 1 ], color[ 2 ], color[ 3 ] ); uniforms_host.rects[ instance_id ].depth = depth; uniforms_host.rects[ instance_id ].texid = texid; uniforms_host.rects[ instance_id ].semantic[ 0 ] = 0; uniforms_host.rects[ instance_id ].semantic[ 1 ] = semantic_id; } ௕ํܗͷ௥ՃΛཁٻ͞ΕͨΒ GPU͔ΒಡΉߏ଄ମʹ৘ใΛੵΜͰ͓͘
  58. std::random_device seedgen; std::mt19937 rng( seedgen() ); std::uniform_int_distribution<> xdist( 0u, 3839u

    ); std::uniform_int_distribution<> ydist( 0u, 2159u ); std::uniform_int_distribution<> cdist( 0u, 0xFFFFFFu ); std::uniform_int_distribution<> ddist( 0u, 32767u ); for( std::size_t i = 0u; i != 1024u; ++i ) { auto x0 = xdist( rng ); auto x1 = xdist( rng ); if( x0 > x1 ) std::swap( x0, x1 ); auto y0 = ydist( rng ); auto y1 = ydist( rng ); if( y0 > y1 ) std::swap( y0, y1 ); auto c = cdist( rng ); auto d = ddist( rng ); window.add_rectangle( vk::Rect2D{ vk::Offset2D{ x0, y0 }, vk::Extent2D{ x1 - x0, y1 - y0 } }, gct::srgb_oetf( gct::html_color( c ) ), d, true, 0u, 0u, 0u ); } ཚ਺ͰܾΊͨҐஔʹཚ਺ͰܾΊͨ৭ͷ Λඳ͘ ௕ํܗ
  59. void rect_renderer::draw( gct::command_buffer_recorder_t &recorder ) { recorder.bind_pipeline( pipeline ); recorder.bind_descriptor_set(

    vk::PipelineBindPoint::eGraphics, pipeline_layout, descriptor_set ); recorder.bind_vertex_buffer( shape.vertex_buffer ); recorder->draw( shape.vertex_count, instance_count, 0, 0 ); } ͜ͷඳ͖ํͰ ͔͜͜Β ߏ଄ମͷσʔλΛरͬͯ (0,1) (0,0) (1,0) (1,1) Λ 6௖఺ 1024ݸ
  60. None
  61. ௕ํܗ จࣈ ه߸ ը૾ SVG౳ͷ ϕΫλը૾ TrueType౳ͷ ςΫενϟΛషͬͨ ςΫενϟΛష͍ͬͯͳ͍ ௕ํܗ

    ࣍͸͜Ε
  62. + = GPUͰϝογϡʹϥελը૾ΛషΔͳΒ ςΫενϟαϯϓϦϯά

  63. flat layout (location = 0) in vec4 input_color; flat layout

    (location = 1) in uvec2 input_semantic; flat layout (location = 2) in uint input_texid; layout (location = 3) in vec2 input_texcoord; layout (location = 0) out vec4 output_color; layout (location = 1) out vec2 output_semantic; layout(binding = 1) uniform sampler2D tex[ 16 ]; void main() { if( input_texid == 0 ) { if( input_color.a == 0.0 ) { discard; } output_color = input_color; } else { vec4 sampled = texture( tex[ input_texid - 1 ], input_texcoord ); if( sampled.a == 0.0 ) { discard; } output_color = sampled; } output_semantic = input_semantic; } input_texid ͕0Ͱͳ͚Ε͹ -1൪໨ͷςΫενϟ͔Β৭ΛऔΔ input_texid
  64. for( const auto &image: input_images ) { updates.push_back( gct::write_descriptor_set_t() .set_basic(

    (*descriptor_set)[ "tex" ] .setDstArrayElement( tex_id ) .setDescriptorCount( 1 ) ) .add_image( gct::descriptor_image_info_t() .set_sampler( sampler ) .set_image_view( image ) .set_basic( vk::DescriptorImageInfo() .setImageLayout( vk::ImageLayout::eShaderReadOnlyOptimal ) ) ) ); ++tex_id; } canvasʹηοτ͞ΕͨΠϝʔδΛ ςΫενϟαϯϓϥʔʹ݁ͼ͚ͭΔ
  65. gct::canvas window( device, allocator, pipeline_cache, descriptor_pool, wvs, wfs, vk::Extent2D{ width,

    height }, std::array< float, 4u >{ 0.f, 0.f, 0.f, 0.f }, {}, { image->get_view( gct::image_view_create_info_t() .set_basic( vk::ImageViewCreateInfo() .setSubresourceRange( vk::ImageSubresourceRange() .setAspectMask( vk::ImageAspectFlagBits::eColor ) .setBaseMipLevel( 0 ) .setLevelCount( image->get_props().get_basic().mipLevels ) .setBaseArrayLayer( 0 ) .setLayerCount( image->get_props().get_basic().arrayLayers ) ) .setViewType( gct::to_image_view_type( image->get_props().get_basic().imageType ) ) .setFormat( vk::Format::eR8G8B8A8Srgb ) ) .rebuild_chain() ) } ); canvasͷҾ਺ʹΠϝʔδΛ౉͢
  66. None
  67. gct::canvas window( device, allocator, pipeline_cache, descriptor_pool, wvs, wfs, vk::Extent2D{ 640u,

    300u }, std::array< float, 4u >{ 0.f, 0.f, 0.f, 0.f }, {}, { icon->get_view( gct::image_view_create_info_t() .set_basic( vk::ImageViewCreateInfo() .setSubresourceRange( vk::ImageSubresourceRange() .setAspectMask( vk::ImageAspectFlagBits::eColor ) .setBaseMipLevel( 0 ) .setLevelCount( icon->get_props().get_basic().mipLevels ) .setBaseArrayLayer( 0 ) 640x300ͷ΢Οϯυ΢Λ࡞ͬͯ
  68. { icon->get_view( gct::image_view_create_info_t() .set_basic( vk::ImageViewCreateInfo() .setSubresourceRange( vk::ImageSubresourceRange() .setAspectMask( vk::ImageAspectFlagBits::eColor )

    .setBaseMipLevel( 0 ) .setLevelCount( icon->get_props().get_basic().mipLevels ) .setBaseArrayLayer( 0 ) .setLayerCount( icon->get_props().get_basic().arrayLayers ) ) .setViewType( gct::to_image_view_type( icon->get_props().get_basic().imageType ) ) .setFormat( vk::Format::eR8G8B8A8Srgb ) ) .rebuild_chain() ) } ); ࢖͏ը૾Ληοτͯ͠
  69. window.add_rectangle( vk::Rect2D{ vk::Offset2D{ 0, 0 }, vk::Extent2D{ 640, 300 }

    }, gct::srgb_oetf( gct::html_color( 0xf0f8ff ) ), 0u, true, 0u, 0u, 0u ); window.add_rectangle( vk::Rect2D{ vk::Offset2D{ 95, 50 }, vk::Extent2D{ 200, 70 } }, gct::srgb_oetf( gct::html_color( 0xc0c0c0 ) ), 1u, true, 0u, 0u, 1u ); window.add_rectangle( vk::Rect2D{ vk::Offset2D{ 345, 50 }, vk::Extent2D{ 200, 70 } }, gct::srgb_oetf( gct::html_color( 0x4169e1 ) ), 1u, true, 0u, 0u, 2u ); window.add_rectangle( vk::Rect2D{ vk::Offset2D{ 0, 0 }, vk::Extent2D{ 640, 300 } }, gct::srgb_oetf( gct::html_color( 0x000000 ) ), 1u, false, 1u, 0u, 0u ); window.add_rectangle( vk::Rect2D{ vk::Offset2D{ 50, 150 }, vk::Extent2D{ 128, 128 } }, Ϙλϯͱ͔ฒ΂ͯ
  70. desktop.push_back( std::shared_ptr< gct::canvas >( new gct::canvas( device, allocator, pipeline_cache, descriptor_pool,

    wvs, dfs, vk::Extent2D{ width, height }, gct::srgb_oetf( gct::html_color( 0x008080 ) ), {}, { window.get_color()->get_view( vk::ImageAspectFlagBits::eColor ), } ) ) ); desktop.back()->add_rectangle( vk::Rect2D{ vk::Offset2D{ ( width - 640u ) / 2u, ( height - 300u ) / 2u }, vk::Extent2D{ 640, 300 } }, gct::srgb_oetf( gct::html_color( 0x000000 ) ), 1u, true, 0u, 1u, 0u ); canvasʹ͸ผͷcanvasͷඳը݁ՌΛ ೖྗͱͯ͠౉ͤΔ
  71. None
  72. ௕ํܗ จࣈ ه߸ ը૾ SVG౳ͷ ϕΫλը૾ TrueType౳ͷ ςΫενϟΛషͬͨ ςΫενϟΛష͍ͬͯͳ͍ ௕ํܗ

    ࣍͸͜Ε
  73. จࣈ͸ 2࣍BézierεϓϥΠϯۂઢͰ දݱ͞ΕͨϕΫλը૾ TrueType

  74. PathFinder https://github.com/servo/pathfinder ίϯϐϡʔτγΣʔμͰϥελϥΠζ NanoVG libtess2 https://github.com/memononen/libtess2 CPUͰࡾ֯ܗ෼ׂɺGPUͰඳը https://github.com/memononen/nanovg CPUͰߥ͘ࡾ֯ܗ෼ׂɺGPUͰඳըɺεςϯγϧͰमਖ਼ Cairo

    https://www.cairographics.org/ CPUͰϥελϥΠζ CPUͰ΍Δ GPUͰ΍Δ GPUͰૉૣ͘ϕΫλը૾Λඳ͘ͷ͸؆୯Ͱ͸ͳ͍ OpenGL༻ OpenGL༻ OpenGL༻ ͔͠΋طଘͷ࣮૷͸େମVulkanͳΞϓϦέʔγϣϯʹ૊ΈࠐΊͳ͍
  75. ࣄલʹϑΥϯτΛࡾ֯ܗ෼ׂͨ͠σʔλΛ༻ҙͯ͠ ࣮ߦ࣌͸ͦΕΛͦͷ··GPUͰඳը͠Α͏

  76. https://github.com/fetisov/ttf2mesh ttf2mesh TrueTypeͷ ϑΥϯτΛಡΜͰ ࡾ֯ܗ෼ׂͯ͠ ௖఺ͷϦετΛฦ͢ ϥΠϒϥϦ ͜ΕΛ࢖ͬͯ ࣄલʹϑΥϯτΛ ௖఺഑ྻʹ͠Α͏

  77. ttf2meshͷ໰୊఺ Format 4ͷcmap͔͠ಡ·ͳ͍

  78. Φϑηοτςʔϒϧ ςʔϒϧ ςʔϒϧ ςʔϒϧ ⋮ 55'ͷόʔδϣϯ ςʔϒϧ਺ ςʔϒϧͷछྨ νΣοΫαϜ ςʔϒϧͷઌ಄ͷҐஔ

    ςʔϒϧͷ௕͞ ςʔϒϧͷछྨ νΣοΫαϜ ςʔϒϧͷઌ಄ͷҐஔ ςʔϒϧͷ௕͞ ⋮ TrueTypeͷߏ଄
  79. TrueTypeͷߏ଄ ςʔϒϧ໊ ಺༰ head name OS/2 maxp post ϝλσʔλ glyf

    άϦϑΛߏ੒͢Δۂઢͷύϥϝʔλ loca Կ൪໨ͷάϦϑ͕glyfͷ Կཁૉ໨͔Β࢝·Δ͔ cmap locaͷԿ൪໨ͷάϦϑ͕ จࣈίʔυͰݴ͏ԿͷจࣈʹରԠ͢Δ͔
  80. TrueTypeͷߏ଄ - cmap DNBQͷܗࣜ ಺༰ Format 0 256ཁૉͷ഑ྻͰ8bitจࣈΛม׵ Format 2

    2όΠτจࣈΛ্Ґ8bitͱԼҐ8bitʹ۠੾ͬͯ2ஈ֊Ͱม׵ Format 4 16bitͷ೚ҙͷൣғͱάϦϑͷ೚ҙͷൣғͷରԠΛ࣋ͭ UnicodeͷBMPͷൣғ͚ͩΛѻ͑Δ Format 6 ࠷େ65536ཁૉͷ഑ྻͰ16bitจࣈΛม׵ Format 8 Unicodeͷ্Ґ16bitͱԼҐ16bitͰม׵දΛ෼͚ͯ2ஈ֊Ͱม׵͢Δ Format 10 ࠷େ4294967296ཁૉͷ഑ྻͰ32bitจࣈΛม׵ Format 12 32bitͷ೚ҙͷൣғͱάϦϑͷ೚ҙͷൣғͷରԠΛ࣋ͭ UnicodeͷશͯͷจࣈΛѻ͑Δ Format 13 32bitͷ೚ҙͷൣғͱಛఆͷάϦϑͷରԠΛ࣋ͭ UnicodeͷશͯͷจࣈΛѻ͑Δ Format 14 UnicodeͷҟࣈମηϨΫλͰબ͹ΕΔจࣈͱάϦϑͷରԠΛ࣋ͭ
  81. TrueTypeͷߏ଄ - cmap DNBQͷܗࣜ ಺༰ Format 0 256ཁૉͷ഑ྻͰ8bitจࣈΛม׵ Format 2

    2όΠτจࣈΛ্Ґ8bitͱԼҐ8bitʹ۠੾ͬͯ2ஈ֊Ͱม׵ Format 4 16bitͷ೚ҙͷൣғͱάϦϑͷ೚ҙͷൣғͷରԠΛ࣋ͭ UnicodeͷBMPͷൣғ͚ͩΛѻ͑Δ Format 6 ࠷େ65536ཁૉͷ഑ྻͰ16bitจࣈΛม׵ Format 8 Unicodeͷ্Ґ16bitͱԼҐ16bitͰม׵දΛ෼͚ͯ2ஈ֊Ͱม׵͢Δ Format 10 ࠷େ4294967296ཁૉͷ഑ྻͰ32bitจࣈΛม׵ Format 12 32bitͷ೚ҙͷൣғͱάϦϑͷ೚ҙͷൣғͷରԠΛ࣋ͭ UnicodeͷશͯͷจࣈΛѻ͑Δ Format 13 32bitͷ೚ҙͷൣғͱಛఆͷάϦϑͷରԠΛ࣋ͭ UnicodeͷશͯͷจࣈΛѻ͑Δ Format 14 UnicodeͷҟࣈମηϨΫλͰબ͹ΕΔจࣈͱάϦϑͷରԠΛ࣋ͭ 16bitͷUnicode͔͠ѻ͑ͳ͍ݹ͍ιϑτ΢ΣΞͰ΋ϑΥϯτΛ࢖͑ΔΑ͏ʹ 
 ͜ͷܗࣜͷcmap΋Α͘ಉࠝ͞Ε͍ͯΔ ΠϚυΩͷϑΥϯτ͸େମ͜ͷܗࣜͷcmapΛؚΜͰ͍Δ
  82. Unicodeͷۭؒ جຊଟݴޠ໘ #.1 ௥Ճଟݴޠ໘ 4.1 ௥Ճ׽ࣈ໘ 4*1 ୈࡾ׽ࣈ໘ 5*1 ະׂΓ౰ͯ

    ௥Ճಛघ༻్໘ 441 ࢲ༻໘ 0x000000 0x010000 0x020000 0x030000 0x040000 0x0F0000 0x110000 0x0E0000 16bitͰදݱͰ͖Δൣғ ೔ຊޠϑΥϯτͷจࣈ͕ ࢖͏ൣғ ttf2mesh͸ ௥Ճ׽ࣈ໘ͷ׽ࣈͷάϦϑͷ ίʔυϙΠϯτΛऔΕͳ͍
  83. diff --git a/ttf2mesh.h b/ttf2mesh.h index 76e5765..abfcac9 100644 --- a/ttf2mesh.h +++

    b/ttf2mesh.h @@ -145,8 +145,8 @@ struct ttf_file { int nchars; /* number of the font characters */ int nglyphs; /* number of glyphs (usually less than nchars) */ - uint16_t *chars; /* utf16 codes array with nchars length */ - uint16_t *char2glyph; /* glyph indeces array with nchars length */ + uint32_t *chars; /* utf32 codes array with nchars length */ + uint32_t *char2glyph; /* glyph indeces array with nchars length */ ttf_glyph_t *glyphs; /* array of the font glyphs with nglyphs length */ const char *filename; /* full path and file name of the font */ uint32_t glyf_csum; /* 'glyf' table checksum (used by ttf_list_fonts) */ @@ -455,7 +455,7 @@ int ttf_list_match_id(ttf_t **list, const char *requirements, ...); * @param utf16_char Unicode character * @return Glyph index in glyphs array or -1 */ -int ttf_find_glyph(const ttf_t *ttf, uint16_t utf16_char); +int ttf_find_glyph(const ttf_t *ttf, uint32_t utf32_char); UnicodeͷίʔυϙΠϯτΛ 16bit੔਺ʹಥͬࠐΜͰ͍ΔॴΛ 32bit੔਺ʹஔ͖׵͑ͯ
  84. @@ -1318,6 +1335,59 @@ static int parse_fmt4(ttf_t *ttf, uint8_t *data,

    int dataSize, bool headers_only return TTF_DONE; } +static int parse_fmt12(ttf_t *ttf, uint8_t *data, int dataSize, bool headers_only) +{ + ttf_fmt12_t *tab; + int smgSize; + int i, j, k; + int maxGlyphID = 0; + int endGlyphID; + ttf_fmt12_smg_t *smgs; + + if (dataSize < (int)sizeof(ttf_fmt12_t)) return TTF_ERR_FMT; + tab = (ttf_fmt12_t *)data; + conv32(tab->length); + conv32(tab->numGroups); + if (tab->length > dataSize) + return TTF_ERR_FMT; Format 12ͷcmap͕͋ͬͨΒ ༏ઌతʹಡΉίʔυΛ௥Ճ
  85. Format 12ରԠ൛ttf2mesh https://github.com/Fadis/ttf2mesh

  86. GPUʹૹΔόΠφϦ ⋮ (−0.3515625,0.7128906) (−0.3203125,0.7988281) (−0.2734375,0.8535156) (−0.21484375,0.8847656) (−0.14453125,0.89453125) (−0.076171875,0.8847656) (−0.017578125,0.8515625) (0.029296875,0.7949219)

    (0.068359375,0.7128906) (0,0.6875) ⋮ 16 15 14 17 18 21 20 12 14 13 ⋮ ⋮ ⋮ ௖఺ΠϯσοΫε ௖఺഑ྻ શͯͷจࣈͷάϦϑΛ 1ͭͷόΠφϦʹؚΊ͍ͨ ͔͜͜Β ͜͜·Ͱ όΠφϦͷͲͷ෦෼͕ ͲͷάϦϑͷ෺͔Λ ผͷ৔ॴʹه࿥͢Δඞཁ͕͋Δ 1จࣈ෼
  87. glTF 3DϞσϧσʔλͷϑΥʔϚοτ JSONܗࣜͷϔομͰ GPUʹૹΔόΠφϦΛ ͲͷΑ͏ʹղऍ͢΂͖͔Λهड़ ͜ͷϔομʹόΠφϦຊମΛ σʔλͱ͚ͯͬͭͨ͘͠෺ https://www.khronos.org/gltf/

  88. TrueType glTF ttf2mesh

  89. ttf_t *font = nullptr; ttf_load_from_file( input.c_str(), &font, false); std::vector< float

    > vertices; std::unordered_map< std::pair< float, float >, std::uint32_t, pair_hash > vhash; std::vector< std::uint32_t > faces; std::vector< std::tuple< unsigned int, std::uint32_t, std::uint32_t > > glyphs; std::pair< float, float > min = std::make_pair( std::numeric_limits< float >::max(), std::numeric_limits< float >::max() ); std::pair< float, float > max = std::make_pair( std::numeric_limits< float >::min(), std::numeric_limits< float >::min() ); for( unsigned int glyph_id : tq::trange( font->nglyphs ) ) { ttf_mesh_t *out; if( font->glyphs[ glyph_id ].index && font->glyphs[ glyph_id ].outline ) { if( font->glyphs[ glyph_id ].symbol >= min_code && font->glyphs[ glyph_id ].symbol <= max_code ) { auto result = ttf_glyph2mesh( ttf2meshͰϑΥϯτΛಡΉ
  90. for( unsigned int glyph_id : tq::trange( font->nglyphs ) ) {

    ttf_mesh_t *out; if( font->glyphs[ glyph_id ].index && font->glyphs[ glyph_id ].outline ) { if( font->glyphs[ glyph_id ].symbol >= min_code && font->glyphs[ glyph_id ].symbol <= max_code ) { auto result = ttf_glyph2mesh( &font->glyphs[ glyph_id ], &out, TTF_QUALITY_LOW, TTF_FEATURE_IGN_ERR ); if( result == TTF_DONE) { std::vector< std::uint32_t > vertex_indices; for( unsigned int i = 0u; i != out->nvert; ++i ) { vertex_indices.push_back( get_index( vhash, vertices, out->vert[ i ].x, out->vert[ i ].y, align ) ); min.first = std::min( min.first, vertices[ vertex_indices.back() * 3u ] ); min.second = std::min( min.second, vertices[ vertex_indices.back() * 3u + 1u ] ); max.first = std::max( άϦϑͷ৘ใ͕ਖ਼͘͠औΕ͍ͯͯ ཉ͍͠ൣғͷจࣈͷάϦϑͩͬͨΒ
  91. auto result = ttf_glyph2mesh( &font->glyphs[ glyph_id ], &out, TTF_QUALITY_LOW, TTF_FEATURE_IGN_ERR

    ); if( result == TTF_DONE) { std::vector< std::uint32_t > vertex_indices; for( unsigned int i = 0u; i != out->nvert; ++i ) { vertex_indices.push_back( get_index( vhash, vertices, out->vert[ i ].x, out->vert[ i ].y, align ) ); min.first = std::min( min.first, vertices[ vertex_indices.back() * 3u ] ); min.second = std::min( min.second, vertices[ vertex_indices.back() * 3u + 1u ] ); max.first = std::max( max.first, vertices[ vertex_indices.back() * 3u ] ); max.second = std::max( max.second, vertices[ vertex_indices.back() * 3u + 1u ] ); } glyphs.push_back( std::make_tuple( glyph_id, faces.size(), 0u ) ); ࡾ֯ܗ෼ׂ ௖఺഑ྻʹಉ͡࠲ඪͷ௖఺͕طʹ͋ͬͨΒͦͷΠϯσοΫεΛฦ͢ ແ͔ͬͨΒ௖఺഑ྻʹ௖఺Λ௥Ճͯͦ͠ͷΠϯσοΫεΛฦ͢
  92. ); min.second = std::min( min.second, vertices[ vertex_indices.back() * 3u +

    1u ] ); max.first = std::max( max.first, vertices[ vertex_indices.back() * 3u ] ); max.second = std::max( max.second, vertices[ vertex_indices.back() * 3u + 1u ] ); } glyphs.push_back( std::make_tuple( glyph_id, faces.size(), 0u ) ); for( unsigned int i = 0u; i != out->nfaces; ++i ) { faces.push_back( vertex_indices[ out->faces[ i ].v1 ] ); faces.push_back( vertex_indices[ out->faces[ i ].v2 ] ); faces.push_back( vertex_indices[ out->faces[ i ].v3 ] ); } std::get< 2 >( glyphs.back() ) = faces.size() - std::get< 1 >( glyphs.back() ); ttf_free_mesh( out ); } } } } ௖఺ΠϯσοΫεΛ௥Ճ͢Δ ௖఺ΠϯσοΫεͷ͔͜͜Β ͜ͷ਺ͷ௖఺͕glyph_idͷάϦϑͰ͢
  93. nlohmann::json root; root[ "asset" ] = nlohmann::json::object({ { "generator", "ttf_to_gltf"

    }, { "version", "2.0" } }); root[ "buffers" ] = nlohmann::json::array(); root[ "buffers" ].push_back( nlohmann::json::object({ { "uri", "font.bin" }, { "byteLength", vertices.size() * sizeof( float ) + faces.size() * sizeof( std::uint32_t ) } }) ); root[ "bufferViews" ] = nlohmann::json::array(); for( unsigned int i = 0u; i != glyphs.size(); ++i ) { const auto begin = std::get< 1 >( glyphs[ i ] ); const auto size = std::get< 2 >( glyphs[ i ] ); root[ "bufferViews" ].push_back( nlohmann::json::object({ { "buffer", 0 }, { "target", 34963 }, { "byteOffset", glTFͷϔομ͸ී௨ͷJSONͳͷͰ ී௨ͷJSONΤϯίʔμͰ࡞ΕΔ
  94. root[ "bufferViews" ] = nlohmann::json::array(); for( unsigned int i =

    0u; i != glyphs.size(); ++i ) { const auto begin = std::get< 1 >( glyphs[ i ] ); const auto size = std::get< 2 >( glyphs[ i ] ); root[ "bufferViews" ].push_back( nlohmann::json::object({ { "buffer", 0 }, { "target", 34963 }, { "byteOffset", vertices.size() * sizeof( float ) + begin * sizeof( std::uint32_t ) }, { "byteLength", size * sizeof( std::uint32_t ) }, }) ); } root[ "bufferViews" ].push_back( nlohmann::json::object({ { "buffer", 0 }, { "target", 34962 }, { "byteLength", vertices.size() * sizeof( float ) }, }) ); root[ "accessors" ] = nlohmann::json::array(); for( unsigned int i = 0u; i != glyphs.size(); ++i ) { const auto begin = std::get< 1 >( glyphs[ i ] ); const auto size = std::get< 2 >( glyphs[ i ] ); ֤άϦϑͷઌ಄Λࢦ͢ όοϑΝϏϡʔΛ࡞Δ ࠷ޙʹ௖఺഑ྻͷ όοϑΝϏϡʔΛ࡞Δ
  95. root[ "accessors" ] = nlohmann::json::array(); for( unsigned int i =

    0u; i != glyphs.size(); ++i ) { const auto begin = std::get< 1 >( glyphs[ i ] ); const auto size = std::get< 2 >( glyphs[ i ] ); root[ "accessors" ].push_back( nlohmann::json::object({ { "bufferView", i }, { "componentType", 5125 }, // 32bit int { "type", "SCALAR" }, { "count", size } }) ); } root[ "accessors" ].push_back( nlohmann::json::object({ { "bufferView", glyphs.size() }, { "componentType", 5126 }, // 32bit float { "type", "VEC3" }, { "count", vertices.size() / 3u }, { "min", nlohmann::json::array({ min.first, min.second, 0.f })}, { "max", nlohmann::json::array({ max.first, max.second, 0.f })} }) ); ֤όοϑΝϏϡʔΛࢦ͢ ΞΫηαΛ࡞Δ ௖఺഑ྻΛࢦ͢ΞΫηαΛ࡞Δ
  96. for( unsigned int i = 0u; i != glyphs.size(); ++i

    ) { const auto glyph_id = std::get< 0 >( glyphs[ i ] ); root[ "meshes" ].push_back( nlohmann::json::object({ { "primitives", nlohmann::json::array({ nlohmann::json::object({ { "attributes", nlohmann::json::object({ { "POSITION", glyphs.size() } })}, { "indices", i } })})}, { "name", std::to_string( glyph_id ) } }) ); } root[ "nodes" ] = nlohmann::json::array(); for( unsigned int i = 0u; i != glyphs.size(); ++i ) { const auto glyph_id = std::get< 0 >( glyphs[ i ] ); root[ "nodes" ].push_back( nlohmann::json::object({ { "mesh", i }, { "name", std::to_string( glyph_id ) } }) ); } root[ "scene" ] = 0; root[ "scenes" ].push_back( nlohmann::json::object({ { "nodes", nlohmann::json::array() } ͜ͷΞΫηα͔Β ௖఺ΠϯσοΫεΛಡΉ ͜ͷΞΫηα͔Β ௖఺഑ྻΛಡΈ ͦΜͳϝογϡΛ 1ؚͭΉϊʔυ ϊʔυ໊͸glyph_idʹ͓ͯ͘͠
  97. { std::fstream bin( bin_filename, std::ios::out|std::ios::binary ); bin.write( reinterpret_cast< const char*

    >( vertices.data() ), vertices.size() * sizeof( float ) ); bin.write( reinterpret_cast< const char* >( faces.data() ), faces.size() * sizeof( std::uint32_t ) ); } { if( format == "json" ) { std::fstream json( json_filename, std::ios::out ); json << root.dump( 2 ) << std::endl; } else if( format == "bson" ) { std::fstream json( json_filename, std::ios::out|std::ios::binary ); auto serialized = nlohmann::json::to_bson( root ); json.write( reinterpret_cast< const char* >( serialized.data() ), serialized.size() ); } GPUʹૹΔόΠφϦΛ ϑΝΠϧʹు͘ ϔομͷJSONΛ ϑΝΠϧʹు͘
  98. ී௨ͷglTF͔ͩΒ BlenderͰಡΊΔ 1ͭͷάϦϑ͕ 1ͭͷϊʔυʹͳ͍ͬͯΔ ΛϕʔεϥΠϯͱͯ͠ άϦϑ͕ฒΜͰ͍Δ y = 0

  99. font command_buffer_recorder_t::load_font( std::filesystem::path path, const std::shared_ptr< allocator_t > &allocator )

    { fx::gltf::Document doc = fx::gltf::LoadFromText( path.string() ); if( doc.buffers.size() != 1u ) throw invalid_font(); auto buffer_path = std::filesystem::path( doc.buffers[ 0 ].uri ); if( buffer_path.is_relative() ) buffer_path = path.parent_path() / buffer_path; auto buffer = load_buffer_from_file( allocator, buffer_path.string(), vk::BufferUsageFlagBits::eVertexBuffer|vk::BufferUsageFlagBits::eIndexBuffer ); const auto vertex_buffer_view = std::find_if( doc.bufferViews.begin(), doc.bufferViews.end(), []( const auto &v ) { return v.target == fx::gltf::BufferView::TargetType::ArrayBuffer; } ); if( vertex_buffer_view == doc.bufferViews.end() ) throw invalid_font(); const std::uint32_t vertex_buffer_offset = vertex_buffer_view->byteOffset; const std::uint32_t vertex_buffer_length = vertex_buffer_view->byteLength; glTFΛύʔεͯ͠ όΠφϦΛGPUʹૹΔ
  100. if( accessor.type != fx::gltf::Accessor::Type::Scalar ) throw invalid_font(); if( accessor.bufferView >=

    doc.bufferViews.size() ) throw invalid_font(); const auto &buffer_view = doc.bufferViews[ accessor.bufferView ]; if( buffer_view.buffer != 0u ) throw invalid_font(); if( buffer_view.target != fx::gltf::BufferView::TargetType::ElementArrayBuffer ) throw invalid_font(); const auto offset = buffer_view.byteOffset - vertex_index_offset; constexpr static std::uint32_t face_size = ( sizeof( std::uint32_t ) * 3u ); if( offset % face_size ) throw invalid_font(); if( buffer_view.byteLength % face_size ) throw invalid_font(); hash.insert( std::make_pair( gid, glyph_index{ std::uint32_t( offset / sizeof( std::uint32_t ) ), std::uint32_t( buffer_view.byteLength / sizeof( std::uint32_t ) ) } ) ); } ͋Δglyph_id͕ཁٻ͞Εͨ࣌ʹ ௖఺ΠϯσοΫεͷ Ͳ͔͜ΒͲ͜·ͰΛ࢖͏΂͖͔Λه࿥ͨ͠ unordered_map
  101. None
  102. จࣈΛGPUͰେྔʹඳ͖͍ͨ

  103. CPU͔Β΋GPU͔Β΋ݟ͑ΔϝϞϦ GPU CPU ௕ํܗ × 500 Πϯελϯγϯά 1ίϚϯυͰಉ͡τϙϩδͷਤܗΛେྔʹඳ͘ ௕ํܗ0ͷ഑ஔ ௕ํܗ1ͷ഑ஔ

    ௕ํܗ2ͷ഑ஔ ௕ํܗ3ͷ഑ஔ ⋮ ݸʑͷจࣈ͸τϙϩδ͕ҟͳΔҝ ΠϯελϯγϯάͰ·ͱΊͯඳ͚ͳ͍
  104. CPU͔Β΋GPU͔Β΋ݟ͑ΔϝϞϦ GPU CPU Indirect Draw 1ίϚϯυͰҟͳΔτϙϩδͷਤܗΛେྔʹඳ͘ άϦϑ0ͷ഑ஔ άϦϑ1ͷ഑ஔ άϦϑ2ͷ഑ஔ άϦϑ3ͷ഑ஔ

    ⋮ άϦϑ0ͷ௖఺ͷಡΈํ άϦϑ1ͷ௖఺ͷಡΈํ άϦϑ2ͷ௖఺ͷಡΈํ άϦϑ3ͷ௖఺ͷಡΈํ 
 ⋮ ͜͜ʹྻڍͨ͠΍ͭඳ͍ͯ
  105. void text_renderer::add_glyph( std::uint32_t glyph_id, float x, float y, float size,

    const std::array< float, 4u > &color, std::uint16_t depth, std::uint16_t texid, std::uint16_t semid ) { auto glyph_info = font_info.index.find( glyph_id ); if( glyph_info != font_info.index.end() ) { const auto instance_id = instance_count++; uniforms_host.rects[ instance_id ].offset[ 0 ] = x * scale; uniforms_host.rects[ instance_id ].offset[ 1 ] = y * scale; uniforms_host.rects[ instance_id ].extent[ 0 ] = size * scale; uniforms_host.rects[ instance_id ].extent[ 1 ] = size * scale; uniforms_host.rects[ instance_id ].color = glm::vec4( color[ 0 ], color[ 1 ], color[ 2 ], color[ 3 ] ); uniforms_host.rects[ instance_id ].depth = depth; จࣈͷ௥ՃΛཁٻ͞ΕͨΒ ͦͷάϦϑ͕ GPUʹૹͬͨσʔλͷதʹ͋Δ͔Λௐ΂ͯ
  106. if( glyph_info != font_info.index.end() ) { const auto instance_id =

    instance_count++; uniforms_host.rects[ instance_id ].offset[ 0 ] = x * scale; uniforms_host.rects[ instance_id ].offset[ 1 ] = y * scale; uniforms_host.rects[ instance_id ].extent[ 0 ] = size * scale; uniforms_host.rects[ instance_id ].extent[ 1 ] = size * scale; uniforms_host.rects[ instance_id ].color = glm::vec4( color[ 0 ], color[ 1 ], color[ 2 ], color[ 3 ] ); uniforms_host.rects[ instance_id ].depth = depth; uniforms_host.rects[ instance_id ].texid = texid; uniforms_host.rects[ instance_id ].semantic[ 0 ] = semid; uniforms_host.rects[ instance_id ].semantic[ 1 ] = 0; indirect_host[ instance_id ].indexCount = glyph_info->second.vertex_count; indirect_host[ instance_id ].instanceCount = 1u; indirect_host[ instance_id ].firstIndex = glyph_info->second.vertex_offset; indirect_host[ instance_id ].vertexOffset = 0u; indirect_host[ instance_id ].firstInstance = instance_id; } } άϦϑͷ৭ͱ഑ஔ άϦϑͷ௖఺ΛͲͷҐஔ͔ΒಡΉ͔
  107. void text_renderer::draw( gct::command_buffer_recorder_t &recorder ) { recorder.bind_pipeline( pipeline ); recorder.bind_descriptor_set(

    vk::PipelineBindPoint::eGraphics, pipeline_layout, descriptor_set ); recorder.bind_vertex_buffer( font_info.buffer ); recorder.bind_index_buffer( font_info.buffer, font_info.offset, vk::IndexType::eUint32 ); recorder->drawIndexedIndirect( **indirect_device, 0, instance_count, sizeof( VkDrawIndexedIndirectCommand ) ); } ϑΥϯτͷ௖఺഑ྻΛ࢖͏ ϑΥϯτͷ௖఺ΠϯσοΫεΛ࢖͏ ͜͜ʹஔ͍ͯ͋Δ௖఺ͷಡΈํʹैͬͯ ͜ͷ਺ͷάϦϑΛඳը
  108. None
  109. จࣈαΠζ෼ͮͭӈʹਐΉ ຊ౰ʹͦΕͰେৎ෉?

  110. όΠτྻ ୯७ʹฒ΂Δͱ ਖ਼͍͠දࣔ H e l l o 48 65

    6c 6c 6f Hello Hello ӳޠ จࣈαΠζ෼ͮͭӈʹਐΉ
  111. όΠτྻ ୯७ʹฒ΂Δͱ ਖ਼͍͠දࣔ ͜ Μ ʹ ͪ ͸ e3 81

    93 e3 82 93 e3 81 ab e3 81 a1 e3 81 af ͜Μʹͪ͸ ͜Μʹͪ͸ ೔ຊޠ จࣈαΠζ෼ͮͭӈʹਐΉ
  112. όΠτྻ ୯७ʹฒ΂Δͱ ਖ਼͍͠දࣔ ส ว ั ส ด ี e0

    b8 aa e0 b8 a7 e0 b8 b1 e0 b8 aa e0 b8 94 e0 b8 b5 ส วั ส ดี ส วั ส ดี ্ʹ৐͔ͬͬͯΔ ด ส ว ส ั ี λΠޠ ෳ਺ͷίʔυϙΠϯτͷฒͼͰάϦϑΛඳ͘΂͖Ґஔ͕มΘΔ
  113. όΠτྻ ୯७ʹฒ΂Δͱ ਖ਼͍͠දࣔ न म स ् त े e0

    a4 a8 e0 a4 ae e0 a4 b8 e0 a5 8d e0 a4 a4 e0 a5 87 नम स्त े नमस ् त े नम स्ते ࣍ͷίʔυϙΠϯτͱ߹ମͯ͠ܗ͕มΘ͍ͬͯΔ ώϯσΟʔޠ ෳ਺ͷίʔυϙΠϯτͷฒͼͰඳ͘΂͖άϦϑ͕มΘΔ
  114. όΠτྻ ୯७ʹฒ΂Δͱ ਖ਼͍͠දࣔ ש ל ו ם d7 a9 d7

    9c d7 95 d7 9d םולש שלום םולש ӈ͔Βࠨ ϔϒϥΠޠ 1จࣈඳ͍ͨΒࠨʹਐΉ
  115. όΠτྻ ୯७ʹฒ΂Δͱ ਖ਼͍͠දࣔ م ر ح ب ً ط d9

    85 d8 b1 d8 ad d8 a8 d9 8b d8 a7 ﺎًﺑﺣرﻣ مرحبً ط ﺎًﺑﺣرﻣ ӈ͔Βࠨ͔ͭ࣍ͷίʔυϙΠϯτͱ߹ମͯ͠ผͷάϦϑʹมԽ ΞϥϏΞޠ
  116. HarfBuzz Φʔϓϯιʔεͳ ςΩετγΣΠϐϯάϥΠϒϥϦ ༩͑ΒΕͨจࣈྻ͔Β ϑΥϯτʹ͋ΔͲͷάϦϑΛ ͲͷҐஔʹ ඳ͘΂͖͔ΛٻΊΔ https://harfbuzz.github.io/

  117. ส วั ส ดี HarfBuzz͕΍ͬͯ͘ΕΔࣄ ࣍ͷจࣈΛ ඳ͘΂͖ҐஔΛٻΊΔ नम स्ते स

    ͲͷάϦϑͰ ඳ͘΂͖͔ΛٻΊΔ ࠓ͙͢μ΢ϯϩʔ υ HarfBuzz͕΍ͬͯ͘Εͳ͍ࣄ ௕͍จࣈྻͷંΓฦ͠ םולש שלום จࣈྻͷਐߦํ޲ͷ൑அ ? ? จࣈͷϨϯμϦϯά םולש ৗʹࠨ͔Β ॲཧͰ͖ΔΑ͏ʹฒ΂ସ͑
  118. ส วั ส ดี HarfBuzz͕΍ͬͯ͘ΕΔࣄ ࣍ͷจࣈΛ ඳ͘΂͖ҐஔΛٻΊΔ नम स्ते स

    ͲͷάϦϑͰ ඳ͘΂͖͔ΛٻΊΔ ࠓ͙͢μ΢ϯϩʔ υ HarfBuzz͕΍ͬͯ͘Εͳ͍ࣄ ௕͍จࣈྻͷંΓฦ͠ םולש שלום จࣈྻͷਐߦํ޲ͷ൑அ ? ? จࣈͷϨϯμϦϯά םולש ৗʹࠨ͔Β ॲཧͰ͖ΔΑ͏ʹฒ΂ସ͑ ࠨ͔Βӈʹॻ͘ݴޠͳͷ͔ ӈ͔Βࠨʹॻ͘ݴޠͳͷ͔͸ ΞϓϦέʔγϣϯ͕൑அ HarfBuzz͸ࢦఆ͞Εͨ޲͖ʹจࣈΛฒ΂Δॲཧ͚ͩΛ͢Δ
  119. https://unicode.org/reports/tr9/ Unicode Standard Annex #9 Unicode૒ํ޲ΞϧΰϦζϜ ӈ͔ΒࠨʹਐΉݴޠͷจࣈ͕ ؚ·Ε͍ͯΔ৔߹ͷ จࣈͷ഑ஔํ๏Λఆٛ͢Δ ΊΜͲ͍͘͞ϧʔϧ͕

    ௕ʑͱॻ͔Ε͍ͯΔ
  120. ICU Unicodeʹ·ͭΘΔ ΊΜͲ͍͘͞ॲཧΛ࣮૷ͨ͠ ϥΠϒϥϦ https://icu.unicode.org/

  121. U_CAPI UBiDiDirection U_EXPORT2 ubidi_getBaseDirection(const UChar *text, int32_t length ); unicode/ubidi.h

    ͜ͷจࣈྻͬͯͲͬͪ޲͖? UBiDiDirection::UBIDI_LTR ࠨ͔ΒӈͷςΩετͰ࢝·Δ UBiDiDirection::UBIDI_RTL ӈ͔ΒࠨͷςΩετͰ࢝·Δ UBiDiDirection::NEUTRAL จࣈྻۭ΍Μ… ฦΓ஋
  122. An apple is called חופת in Hebrew. .תילגנאב תארקנ חופת

    apple ࠨ͔ΒӈͷςΩετͷதʹӈ͔ΒࠨͷςΩετ͕͍ࠞͬͯ͟Δ ӈ͔ΒࠨͷςΩετͷதʹࠨ͔ΒӈͷςΩετ͕͍ࠞͬͯ͟Δ ӈ͔Βࠨʹॻ͘ݴޠͰ΋௨ৗ਺ࣈ͸ࠨ͔Βӈʹॻ͘ͷͰ ӈ͔ΒࠨͷςΩετʹ͸೔ৗతʹࠨ͔ΒӈͷςΩετ͕ࠞ͟Δ
  123. auto bidi = ubidi_open(); if( !bidi ) std::abort(); ubidi_setPara( bidi,

    utf16, utf16_length, base_direction == UBIDI_RTL, nullptr, &error ); if( U_FAILURE( error ) ) std::abort(); const auto run_count = ubidi_countRuns( bidi, &error ); for( unsigned int i = 0u; i != run_count; ++i ) { int32_t start = 0u; int32_t length = 0u; auto bidi_dir = ubidi_getVisualRun( bidi, i, &start, &length ); if( bidi_dir == UBIDI_NEUTRAL ) { std::abort(); } if( bidi_dir == UBIDI_MIXED ) { std::abort(); } ICUͰ ਐߦํ޲͕ಉ͡ൣғͰจࣈྻΛ۠੾Δ ൣғͷ਺Λऔಘ ͜ͷൣғ͸Ͳͬͪ޲͖?
  124. ส วั ส ดี HarfBuzz͕΍ͬͯ͘ΕΔࣄ ࣍ͷจࣈΛ ඳ͘΂͖ҐஔΛٻΊΔ नम स्ते स

    ͲͷάϦϑͰ ඳ͘΂͖͔ΛٻΊΔ ࠓ͙͢μ΢ϯϩʔ υ HarfBuzz͕΍ͬͯ͘Εͳ͍ࣄ ௕͍จࣈྻͷંΓฦ͠ םולש שלום จࣈྻͷਐߦํ޲ͷ൑அ ? ? จࣈͷϨϯμϦϯά םולש ৗʹࠨ͔Β ॲཧͰ͖ΔΑ͏ʹฒ΂ସ͑ ௕͍ςΩετ͸ંΓฦͯ͠දࣔ͠ͳ͚Ε͹ͳΒͳ͍ Unicodeจࣈྻʹ͸ંΓฦͯ͠͸͍͚ͳ͍Օॴ͕͋Δ
  125. English string should not break in the middle of a

    word. ೔ຊޠͷจࣈྻͰ͸ࣺͯԾ໊͸ߦ಄ʹ࣋ͬ ͯ͘Δ΂͖Ͱ͸ͳ͍ ͜͜ͳΒંΓฦͯ͠Α͍ ͜͜ͰંΓฦͯ͠͸͍͚ͳ͍ ͜͜ͳΒંΓฦͯ͠Α͍ ͜͜ͰંΓฦͯ͠͸͍͚ͳ͍ ส วั ส ดี 1จࣈͱݟ၏͞ΕΔγʔέϯεͷ్தͰંΓฦ͢΂͖Ͱ͸ͳ͍
  126. https://unicode.org/reports/tr14/ Unicode Standard Annex #14 Unicodeߦ෼ׂΞϧΰϦζϜ Unicodeʹؚ·ΕΔੈքதͷݴޠͷ ʮͲͷจࣈͱͲͷจࣈͷؒͳΒ վߦΛڬΜͰ΋ྑ͍Ͱ͔͢?ʯ Λ·ͱΊͨ෺

    ΊΜͲ͍͘͞ϧʔϧ͕ ௕ʑͱॻ͔Ε͍ͯΔ
  127. auto iter = ubrk_open( UBRK_LINE, locale, utf16nfc + start, length,

    &error ); para.push_back( run{} ); para.back().is_rtl = is_rtl; std::array< UChar32, 512u > utf32 = { 0 }; int32_t cur = 0u; int32_t next; while( ( next = ubrk_next( iter ) ) != UBRK_DONE ) { std::int32_t utf32_length = 0u; u_strToUTF32( utf32.data(), utf32.size(), &utf32_length, utf16nfc + start + cur, next - cur, &error ); if( U_FAILURE( error ) ) std::abort(); if( utf32_length != 0u ) { auto script_code = uscript_getScript( utf32[ 0 ], &error ); bool newline = is_newline( utf32[ 0 ] ); if( U_FAILURE( error ) ) std::abort(); ubrkΛ࢖͏ͱUnicodeจࣈྻΛ ʮվߦΛڬΜͰ͸͍͚ͳ͍ʯ୯ҐͰ۠੾Δࣄ͕Ͱ͖Δ
  128. ICU ubidi ICU ubrk HarfBuzz จࣈྻ LTRจࣈྻ RTLจࣈྻ LTRจࣈྻ LTRจࣈྻ

    RTLจࣈྻ LTRจࣈྻ ୯ޠ ୯ޠ ୯ޠ ୯ޠ ୯ޠ ୯ޠ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ͜͜ͰંΓฦͯ͠େৎ෉ϚʔΫ
  129. ICU ubrk HarfBuzz LTRจࣈྻ RTLจࣈྻ LTRจࣈྻ ୯ޠ ୯ޠ ୯ޠ ୯ޠ

    ୯ޠ ୯ޠ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ߦͷંΓฦ͠ ը໘্Ͱͷจࣈͷ഑ஔ
  130. 1em HarfbuzzͰ͸௕͞͸ Λ1ͱ͢Δ୯ҐͰ දݱ͞ΕΔ 1 1024 em

  131. typedef struct hb_glyph_position_t { hb_position_t x_advance; hb_position_t y_advance; hb_position_t x_offset;

    hb_position_t y_offset; /*< private >*/ hb_var_int_t var; } hb_glyph_position_t; A B x_offset x_advance ΧʔιϧͷҐஔ͔Β ͲΕ͚ͩͣΕͨ৔ॴʹ จࣈΛඳ͘΂͖͔ จࣈΛඳ͍ͨޙͰ ΧʔιϧΛͲΕ͚ͩ ਐΊΔ΂͖͔
  132. 1pt = 1 72 in ≃ 0.3528mm 1pt = 0.3514mm

    ݩʑͷఆٛ JISن֨Ͱͷఆٛ จࣈͷେ͖͞ͷ୯Ґ ϙΠϯτ(pt) ͲͪΒʹͤΑݴ͑Δ͜ͱ ը໘ͷ෺ཧతͳେ͖͞ͷ৘ใ͕ඞཁ
  133. VESA ENHANCED EXTENDED DISPLAY IDENTIFICATION DATA STANDARD (Defines EDID Structure

    Version 1, Revision 4) 91ϖʔδΑΓ EDID 1.4ʹ͸1cm୯Ґͷ ը໘ͷେ͖͕͞ ؚ·Ε͍ͯΔ VESA DisplayID Standard Version 2.1 47ϖʔδΑΓ DisplayIDʹ͸ 1mm·ͨ͸0.1mm୯Ґͷ ը໘ͷେؚ͖͕͞·Ε͍ͯΔ σΟεϓϨΠ͸ࣗ਎ͷ෺ཧతͳେ͖͞Λ஻͍ͬͯΔ ϓϩδΣΫλ౳αΠζΛఆٛͰ͖ͳ͍ػثͰ͸ ஻͍ͬͯͳ͍ࣄ΋͋Δ
  134. typedef struct VkDisplayPropertiesKHR { VkDisplayKHR display; const char* displayName; VkExtent2D

    physicalDimensions; VkExtent2D physicalResolution; VkSurfaceTransformFlagsKHR supportedTransforms; VkBool32 planeReorderPossible; VkBool32 persistentContent; } VkDisplayPropertiesKHR; VulkanͰσΟεϓϨΠͷ෺ཧతͳେ͖͞ΛऔΕΔ (mm୯Ґ) औΕͳ͍࣌͸ͱΓ͋͑ͣ96dpiͩͱࢥ͏ Windows compatible͙͠͞Λ͓ͯ͜͠͏
  135. จষ͸Wikipediaͷ՘ࢠͷهࣄ͔ΒҾ༻ https://en.wikipedia.org/wiki/Eggplant

  136. None
  137. ࢖͍ͬͯΔϑΥϯτ(NotoSansThai-Regular)ʹ͸਺ࣈ౳͸ؚ·Ε͍ͯͳ͍ ผͷϑΥϯτʹϑΥʔϧόοΫ͢Δ࢓૊ΈΛ༻ҙ͍ͯ͠ͳ͍ҝදࣔͰ͖ͳ͍

  138. άϦϑͷ഑ஔࣗମ͸Chromeͱಉ͡ʹͳ͍ͬͯΔͷͰେৎ෉ͦ͏

  139. λΠޠ͸ஈམͷ࠷ॳʹ େ͖ͳࣈԼ͛ΛೖΕΔ͕ ࣈԼ͛Λ࣮૷͍ͯ͠ͳ͍ͷͰ ࠨ୺͔Βจࣈ͕ฒΜͰ͍Δ

  140. None
  141. ਐߦํ޲͕ࠞࡏ͢ΔςΩετΛਖ਼͍͠ฒͼͰॻ͚͍ͯΔ ՘ࢠ͸ϔϒϥΠޠͰליצח

  142. d7 97 d7 a6 d7 99 d7 9c 20 28

    d7 a9 d7 9d 20 d7 9e d7 93 d7 a2 d7 99 3a ל ח צ י ۭന ( ͜ͷׅހ͸ASCIIίʔυͷׅހͰ ࠨ͔Βӈʹॻ͘ςΩετʹ͓͍ͯ͸ӈΛ޲͍͍ͯΔ͕ ӈ͔Βࠨʹॻ͘ςΩετͰ͋Δͱ͍͏ࢦ͕ࣔͳ͞Ε͍ͯΔҝ HarfBuzz͸UAX #9 §3.4 L4ʹैͬͯάϦϑΛ)ʹஔ͖׵͑Δ
  143. ࢖͍ͬͯΔϑΥϯτ(NotoSansArabic-Regular)ʹ͸Latinܥͷจࣈ͸ؚ·Ε͍ͯͳ͍ ผͷϑΥϯτʹϑΥʔϧόοΫ͢Δ࢓૊ΈΛ༻ҙ͍ͯ͠ͳ͍ҝදࣔͰ͖ͳ͍

  144. ͳΜ͔ҧ͏ؾ͕͢Δ͚ͲɺΞϥϏΞޠ͕ಡΊͳ͗ͯ͢͞Α͘෼͔ΒΜ

  145. தࠃޠͷจࣈ͸߹ମมܗͨ͠Γ͠ͳ͍ͷͰදࣔࣗମ͸೉͘͠ͳ͍

  146. struct GLBHeader { uint32_t magic{}; uint32_t version{}; uint32_t length{}; ChunkHeader

    jsonHeader{}; }; constexpr uint32_t DefaultMaxBufferCount = 8; constexpr uint32_t DefaultMaxMemoryAllocation = 32 * 4 * 1024 * 1024; constexpr std::size_t HeaderSize{ sizeof(GLBHeader) }; constexpr std::size_t ChunkHeaderSize{ sizeof(ChunkHeader) }; constexpr uint32_t GLBHeaderMagic = 0x46546c67u; constexpr uint32_t GLBChunkJSON = 0x4e4f534au; constexpr uint32_t GLBChunkBIN = 0x004e4942u; constexpr char const * const MimetypeApplicationOctet = "data:application/octet-stre constexpr char const * const MimetypeGLTFBuffer = "data:application/gltf-buffer;base constexpr char const * const MimetypeImagePNG = "data:image/png;base64"; constexpr char const * const MimetypeImageJPG = "data:image/jpeg;base64"; fx/gltf.h 43985ϊʔυΛؚΉڊେͳglTFΛ glTFύʔαͷfx-gltf͕ҟৗͳglTFͱ൑அ͢Δ όοϑΝͷ࠷େαΠζΛ4ഒʹ͢ΔͱಡΊΔ
  147. None
  148. GUI දࣔ ೖྗ = +

  149. Ϣʔβۭؒ Ϣʔβۭؒ ΞϓϦέʔγϣϯ xcb αʔό ͓લɺΫϦοΫ͞ΕͯΔͧ Xαʔό͕ډΔ৔߹ ೖྗΠϕϯτΛXαʔό͕௨஌ͯ͘͠Δ

  150. Ϣʔβۭؒ Ϣʔβۭؒ Χʔωϧۭؒ Linux evdev libinput Ϛ΢ε͕ಈ͖·ͨ͠ ΞϓϦέʔγϣϯ xcb αʔό

    ͓લɺΫϦοΫ͞ΕͯΔͧ Xαʔό͸࠷΋ԼͷϨΠϠʔͰ͸ͳ͍ HIDυϥΠό Ϛ΢εͷࠨϘλϯ͕ԡ͞Ε·ͨ͠
  151. Ϣʔβۭؒ Χʔωϧۭؒ Linux evdev libinput ΞϓϦέʔγϣϯ ͜͏͢Ε͹ Xαʔό͕ແͯ͘΋ ೖྗΠϕϯτΛऔΕΔ HIDυϥΠό

    Ϛ΢ε͕ಈ͖·ͨ͠ Ϛ΢εͷࠨϘλϯ͕ԡ͞Ε·ͨ͠
  152. void libinput_t::libinput_internal_t::poll() { auto udev = udev_new(); libinput_interface interface; interface.open_restricted

    = open_restricted; interface.close_restricted = close_restricted; auto li = libinput_udev_create_context(&interface, nullptr, udev); if( li == nullptr ) { std::abort(); } if( libinput_udev_assign_seat(li, "seat0") != 0 ) { std::abort(); } auto libinput_fd = libinput_get_fd( li ); while( 1 ) { int e = 0u; if( ( e = libinput_dispatch( li ) ) != 0 ) { throw std::system_error( std::make_error_code( std::errc( -e ) ), "libinput_t::poll : initial libinput_dispatch failed." ); } libinput_event *raw_event = nullptr; while ((raw_event = libinput_get_event( li )) != nullptr) { std::unique_ptr< libinput_event, libinput_event_deleter > event( raw_event ); const auto type = libinput_event_get_type( raw_event ); if( type == LIBINPUT_EVENT_DEVICE_ADDED ) { udevΛ։͘ libinputͷίϯςΩετΛ࡞Δ γʔτΛ࡞Δ Πϕϯτ͋ͬͨΒ͘Ε
  153. ԿΒ͔ͷೖྗΠϕϯτ͕ى͜Δͱ ͜ͷfdʹ௨஌͕͘ΔͷͰ epollͰ଴ͭ if( ( e = libinput_dispatch( li )

    ) != 0 ) { throw std::system_error( std::make_error_code( std::errc( -e ) ), "libinput_t::poll : initial libinput_dispatch failed." ); } } try { auto event = sched::wait( epoll_notifier->epoll( EPOLLIN, libinput_fd ) | epoll_notifier->epoll( EPOLLIN, set_fd ) ); if( event.data.fd == libinput_fd ) { } else if( event.data.fd == set_fd ) { std::uint64_t temp; if( read( set_fd, &temp, sizeof( temp ) ) < 0 ) { throw std::system_error( std::make_error_code( std::errc( errno ) ), "libinput_t::call : broken event notifier." ); } if( !end ) { } else {
  154. thread_pool->add_co( [ thread_pool=thread_pool, pointer=pointer, &cursor_x, &cursor_y, &cursor_moved ]() { while(

    1 ) { for( auto e: gct::sched::wait( pointer->get_future() ) ) { if( e.event.index() == 0 ) { auto &m = std::get< gct::input::pointer_moved >( e.event ); cursor_moved = true; cursor_x += m.dx; cursor_y += m.dy; } } } } ); Ϛ΢ε͕ಈ͍ͨΠϕϯτΛरͬͯ
  155. if( fb.initial || cursor_moved ) { { auto recorder =

    sync.command_buffer->begin(); if( cursor_moved ) { cursor_moved = false; window.reset(); window.add_rectangle( vk::Rect2D{ vk::Offset2D{ cursor_x, height-cursor_y }, vk::Extent2D{ 64u, 64u } }, gct::srgb_oetf( gct::html_color( 0xFFFFFF ) ), 1u, true, 0u, 0u, 0u ); for( auto &f: framebuffers ) { f.initial = true; } } window( recorder ); recorder.blit( window.get_color(), fb.color ); Χʔιϧ͕ಈ͍͍ͯͨΒ ৽͍͠ҐஔʹΧʔιϧΛ࠶ඳը
  156. None
  157. thread_pool->add_co( [thread_pool=thread_pool,keyboard=keyboard]() { while( 1 ) { for( auto c:

    wait( keyboard->get_future() ) ) { std::cout << c.code; if( c.state == gct::input::key_state::released ) std::cout << " released" << std::endl; else std::cout << " pressed" << std::endl; } } } ); ΩʔϘʔυͷΠϕϯτΛरͬͯ த਎Λදࣔ
  158. $ ./src/example/libinput_keycode/libinput_keycode 36 pressed 36 released 37 pressed 37 released

    38 pressed 38 released 39 pressed 39 released 39 pressed 39 released 39 pressed 38 pressed 39 released 38 released 53 pressed 52 pressed 52 released 50 pressed 51 pressed 50 released ͜ͷ൪߸͸Կ?
  159. #define KEY_Q 16 #define KEY_W 17 #define KEY_E 18 #define

    KEY_R 19 #define KEY_T 20 #define KEY_Y 21 #define KEY_U 22 #define KEY_I 23 #define KEY_O 24 #define KEY_P 25 #define KEY_LEFTBRACE 26 #define KEY_RIGHTBRACE 27 #define KEY_ENTER 28 #define KEY_LEFTCTRL 29 #define KEY_A 30 #define KEY_S 31 #define KEY_D 32 #define KEY_F 33 #define KEY_G 34 #define KEY_H 35 #define KEY_J 36 /usr/include/linux/input-event-codes.h LinuxͷೖྗΠϕϯτͷ൪߸
  160. #define KEY_Q 16 #define KEY_W 17 #define KEY_E 18 #define

    KEY_R 19 #define KEY_T 20 #define KEY_Y 21 #define KEY_U 22 #define KEY_I 23 #define KEY_O 24 #define KEY_P 25 #define KEY_LEFTBRACE 26 #define KEY_RIGHTBRACE 27 #define KEY_ENTER 28 #define KEY_LEFTCTRL 29 #define KEY_A 30 #define KEY_S 31 #define KEY_D 32 #define KEY_F 33 #define KEY_G 34 #define KEY_H 35 #define KEY_J 36 /usr/include/linux/input-event-codes.h Πϕϯτ KEY_A ͕ى͖ͨΒ ΩʔϘʔυͷA͕ԡ͞Εͨͱ͍͏ҙຯ ͱ͸ݶΒͳ͍
  161. 2 3 4 5 6 7 8 9 10 11

    12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 56 57 58 100 97 139 125 126 KEY_A(30) ANSIΩʔϘʔυ(US഑ྻ)ͷA͕͋ΔҐஔͷ Ωʔ͕ԡ͞Εͨͱ͍͏ҙຯ
  162. ANSI ISO ΩϦϧจࣈܥ ؖࠃ2Ϙϧࣜ JIS KEY_A(30) A͔Ͳ͏͔͸஌ΒΜ͕ ͜ͷҐஔͷΩʔ͕ԡ͞Εͨͱ͍͏ҙຯ

  163. KEY_A(30) 1 2 3 4 5 6 7 8 9

    0 A Z E R T Y U I O P Q S D F G H J K L M W X C V B N ISOΩʔϘʔυ ϑϥϯεޠ AZERTY഑ྻͷ৔߹ KEY_A͸Q͕ԡ͞Εͨͱ͍͏ҙຯ
  164. xkbcommon X Window System͔Β X Keyboard Extension(xkb)ͷ ෦෼͚ͩΛ੾Γग़ͨ͠΋ͷ /usr/share/X11/xkb/ ͋ͨΓʹస͕͍ͬͯΔ

    ม׵දΛ࢖ͬͯ ͲͷҐஔͷΩʔ͕ԡ͞Ε͔ͨΛ ԿͷจࣈͷΩʔ͕ೖྗ͞Ε͔ͨʹ ม׵͢Δ https://xkbcommon.org/
  165. libinput͸ʮͲͷҐஔͷΩʔ͕ԡ͞Ε͔ͨʯΛΑͯ͘͜͠Δ xkbcommon͸ΩʔϘʔυϨΠΞ΢τΛࢦఆ͢Δͱ ʮͲͷҐஔͷΩʔ͕ԡ͞Ε͔ͨʯΛ ʮԿͷจࣈ͕ೖྗ͞Ε͔ͨʯʹม׵͢Δ ΩʔϘʔυϨΠΞ΢τͷ৘ใ͕ඞཁ

  166. $ localectl System Locale: LANG=ja_JP.utf8 VC Keymap: jp106 X11 Layout:

    jp X11 Model: jp106 X11 Variant: OADG109A X11 Options: terminate:ctrl_alt_bksp ΩʔϘʔυϨΠΞ΢τͷ৘ใ ͜ΕΞϓϦέʔγϣϯ͔ΒͲ͏΍ͬͯऔΔͷ?
  167. dbus όΠφϦܗࣜͷ γϦΞϥΠθʔγϣϯϑΥʔϚοτ ٴͼͦΕΛ࢖ͬͨ ϓϩηεؒ௨৴ͷن֨ https://www.freedesktop.org/wiki/Software/dbus/

  168. auto dbus_locale = sdbus::createProxy( "org.freedesktop.locale1", "/org/freedesktop/locale1" ); auto [layout,model,variant,options] =

    wait( gct::dbus::call< sdbus::Variant >( dbus_locale->callMethodAsync( "Get" ) .onInterface( "org.freedesktop.DBus.Properties" ) .withArguments( "org.freedesktop.locale1", "X11Layout" ) ) & gct::dbus::call< sdbus::Variant >( dbus_locale->callMethodAsync( "Get" ) .onInterface( "org.freedesktop.DBus.Properties" ) .withArguments( "org.freedesktop.locale1", "X11Model" ) ) & gct::dbus::call< sdbus::Variant >( dbus_locale->callMethodAsync( "Get" ) .onInterface( "org.freedesktop.DBus.Properties" ) .withArguments( "org.freedesktop.locale1", "X11Variant" ) ) & gct::dbus::call< sdbus::Variant >( dbus_locale->callMethodAsync( "Get" ) .onInterface( "org.freedesktop.DBus.Properties" ) .withArguments( "org.freedesktop.locale1", "X11Options" ) ) ); γεςϜόεͷ org.freedesktop.locale1ͷ /org/freedesktop/locale1ʹܨ͍Ͱ ϓϩύςΟΛर͏ͱऔΕΔ
  169. auto raw_ctx = xkb_context_new( XKB_CONTEXT_NO_FLAGS ); if( !raw_ctx ) {

    throw std::system_error( std::make_error_code( std::errc::not_supported ), "xkbcommon_t::xkbcommon_t : xkb_context_new failed." ); } ctx.reset( raw_ctx ); auto raw_keymap = xkb_keymap_new_from_names( ctx.get(), &names, XKB_KEYMAP_COMPILE_NO_FLAGS ); if( !raw_keymap ) { throw std::system_error( std::make_error_code( std::errc::not_supported ), "xkbcommon_t::xkbcommon_t : xkb_keymap_new_from_names failed." ); } keymap.reset( raw_keymap ); auto raw_state = xkb_state_new( keymap.get() ); if( !raw_state ) { throw std::system_error( std::make_error_code( std::errc::not_supported ), "xkbcommon_t::xkbcommon_t : xkb_state_new failed." ); } state.reset( raw_state ); xkbͷίϯςΩετΛ࡞Δ ΩʔϘʔυϨΠΞ΢τͷ໊લ͔Β ΩʔϚοϓΛݟ͚ͭΔ ΩʔϘʔυͷ ঢ়ଶϚγϯΛ࡞Δ
  170. std::shared_ptr< gct::input::input_buffer< gct::input::libinput_event_category::keyboard_event, gct::input::xkb_key_event > > keyboard( new gct::input::input_buffer< gct::input::libinput_event_category::keyboard_event,

    gct::input::xkb_key_event >( libinput, []( auto &dest, auto *event ) { const auto key = libinput_event_keyboard_get_key( event ); const auto time = std::chrono::microseconds( libinput_event_keyboard_get_time_usec( event ) ); const auto state = gct::input::libinput_key_state_to_gct_key_state( libinput_event_keyboard_get_key_state( event ) ); dest.push_back( gct::input::xkb_key_event{ 0u, key + 8u, state, time } ); } ) ); xkbͷΩʔϘʔυΠϕϯτͷ൪߸͸ LinuxͷΩʔϘʔυΠϕϯτͷ൪߸+8 libinput͔ΒདྷͨΠϕϯτΛ xkbcommonʹ౉͢
  171. thread_pool->add_co( [xkb_state=xkb_state,&current_text,&text_changed,&text_guard]() { try { while( 1 ) { for(

    auto c: wait( xkb_state->get_future() ) ) { if( c.state == gct::input::key_state::pressed ) { if( 0x20 <= c.sym && c.sym <= 0x7F ) { std::scoped_lock< std::mutex > lock( text_guard ); current_text += char( c.sym ); std::cout << current_text << std::endl; text_changed = true; } else if( c.sym == 0xFF08 ) { if( !current_text.empty() ) { std::scoped_lock< std::mutex > lock( text_guard ); current_text.pop_back(); std::cout << current_text << std::endl; text_changed = true; } } else if( c.sym == 0xFF0d ) { std::scoped_lock< std::mutex > lock( text_guard ); current_text += '\n'; std::cout << current_text << std::endl; text_changed = true; } xkbcommon͔Β ೖྗ͞ΕͨจࣈΛ΋Β͏
  172. $ ./src/example/libinput_keycode/libinput_keysym H He Hel Hell Hello Hello, Hello, Hello,

    W Hello, Wo Hello, Wor Hello, Worl Hello, World Hello, World! Hello, World!a Hello, World! Hello, World Hello, Worl Hello, Wor Hello, Wo Hello, W ʮԿͷจࣈ͕ೖྗ͞Ε͔ͨʯ ͕औΕΔ
  173. None
  174. None
  175. Nihongo ga utenai...

  176. Ϣʔβۭؒ Ϣʔβۭؒ Χʔωϧۭؒ Linux evdev libinput ΞϓϦέʔγϣϯ xcb αʔό IMMODULE͕ΩʔϘʔυΠϕϯτΛ

    ೖྗϝιουʹ఻͑Δ HIDυϥΠό Qt΍GTK+ IMMODULE Ϣʔβۭؒ ೖྗϝιου ม׵Τϯδϯ
  177. Ϣʔβۭؒ Χʔωϧۭؒ Linux ͜͏͢Ε͹೔ຊޠΛೖྗͰ͖Δ Ϣʔβۭؒ ೖྗϝιου ม׵Τϯδϯ ΞϓϦέʔγϣϯ evdev libinput

    HIDυϥΠό
  178. IMMODULE UIM SCIM IBus fcitx ௚઀ϦϯΫ socket dbus dbus Anthy

    Mozc SKK libhangul Chewing
  179. ΞϓϦέʔγϣϯ UIM SCIM IBus fcitx dbus Anthy Mozc SKK libhangul

    Chewing dbusͰ fcitxͱձ࿩͠Α͏
  180. fcitx5::fcitx5_internal::fcitx5_internal( const std::shared_ptr< sched::thread_pool_t > &tp ) : thread_pool( tp

    ) { conn = sdbus::createDefaultBusConnection(); input_method = sdbus::createProxy( *conn, "org.fcitx.Fcitx5", "/org/freedesktop/portal/inputmethod" ); conn->enterEventLoopAsync(); } void fcitx5::fcitx5_internal::run() { thread_pool->add_co( [self=shared_from_this()]() { self->poll(); } ); } void fcitx5::fcitx5_internal::poll() { auto [version] = sched::wait( gct::dbus::call< std::uint32_t >( input_method->callMethodAsync( "Version" ) .onInterface( "org.fcitx.Fcitx.InputMethod1" ) .withArguments() ) ηογϣϯόεͷ org.fcitx5.Fcitx5ͷ /org/freedesktop/portal/inputmethodʹܨ͙
  181. ); } void fcitx5::fcitx5_internal::poll() { auto [version] = sched::wait( gct::dbus::call<

    std::uint32_t >( input_method->callMethodAsync( "Version" ) .onInterface( "org.fcitx.Fcitx.InputMethod1" ) .withArguments() ) ); std::cout << "fcitx5 version : " << version << std::endl; std::vector< sdbus::Struct< std::string, std::string > > args{ sdbus::make_struct( std::string( "program" ), std::string( "hoge" ) ), sdbus::make_struct( std::string( "display" ), std::string( "x11:" ) ) }; auto [path,wtf] = sched::wait( gct::dbus::call< sdbus::ObjectPath, std::vector< std::uint8_t > >( input_method->callMethodAsync( "CreateInputContext" ) .onInterface( "org.fcitx.Fcitx.InputMethod1" ) .withArguments( args ) ) ); input_context = sdbus::createProxy( *conn, "org.fcitx.Fcitx5", path ); CreateInputContextͰίϯςΩετΛ࡞Δͱ ίϯςΩετͷύε͕໯͑Δ ίϯςΩετͷύεʹܨ͙
  182. thread_pool->add_co( [self=shared_from_this(),e=e,key_state=key_state]() { if( self->input_context ) { auto [result] =

    sched::wait( gct::dbus::call< bool >( self->input_context->callMethodAsync( "ProcessKeyEvent" ) .onInterface( "org.fcitx.Fcitx.InputContext1" ) .withArguments( std::uint32_t( e.sym ), std::uint32_t( e.code ), std::uint32_t( key_state ), bool( e.state == key_state::released ), std::uint32_t( e.relative_time.count() ) ) ) ); if( !result ) { std::cout << "ProcessKeyEvent failed : " << result << std::endl; } } } xkbcommonʹ௨͢લͷΩʔͷҐஔͱ ௨ͨ͠ޙͷΩʔͷจࣈΛfcitx5ʹૹΔ
  183. input_context->uponSignal( "CurrentIM" ) .onInterface( "org.fcitx.Fcitx.InputContext1" ) .call( [self=shared_from_this()]( const std::string

    &a, const std::string &b, const std::string &c ) { std::optional< promise< std::tuple< std::string, std::string, std::string > > > p; { std::scoped_lock< std::mutex > lock( self->guard ); if( self->current_im_p ) { p = std::move( *self->current_im_p ); self->current_im_p = std::nullopt; } else { self->current_im_v = std::make_tuple( a, b, c ); } } if( p ) { ม׵Τϯδϯ͕੾ΓସΘΔͱ CurrentIMγάφϧͰ ม׵Τϯδϯͷ໊લ͕ඈΜͰ͘Δ
  184. input_context->uponSignal( "CommitString" ) .onInterface( "org.fcitx.Fcitx.InputContext1" ) .call( [self=shared_from_this()]( const std::string

    &s ) { std::optional< promise< std::string > > p; { std::scoped_lock< std::mutex > lock( self->guard ); if( self->commit_p ) { p = std::move( *self->commit_p ); self->commit_p = std::nullopt; } else { self->commit_v += s; } } if( p ) { p->set_value( s ); } } ); input_context->uponSignal( "CurrentIM" ) .onInterface( "org.fcitx.Fcitx.InputContext1" ) .call( [self=shared_from_this()]( ม׵͕֬ఆ͢Δͱ CommitStringγάφϧͰ ֬ఆͨ͠಺༰͕ඈΜͰ͘Δ
  185. input_context->uponSignal( "ForwardKey" ) .onInterface( "org.fcitx.Fcitx.InputContext1" ) .call( [self=shared_from_this()]( std::uint32_t a,

    std::uint32_t b, bool c ) { std::optional< promise< std::vector< std::uint32_t > > > p; { std::scoped_lock< std::mutex > lock( self->guard ); if( self->forward_p ) { p = std::move( *self->forward_p ); self->forward_p = std::nullopt; } else { self->forward_v.push_back( a ); } } if( p ) { p->set_value( std::vector< std::uint32_t >{ a } ); } } ); input_context->uponSignal( "UpdateClientSideUI" ) ม׵Τϯδϯ͕੾ΒΕͨঢ়ଶͰ ೖྗ͞Εͨ಺༰͸ ForwardKeyγάφϧͰ ඈΜͰ͘Δ
  186. input_context->uponSignal( "UpdateClientSideUI" ) .onInterface( "org.fcitx.Fcitx.InputContext1" ) .call( [self=shared_from_this()]( const std::vector<

    sdbus::Struct< std::string, std::int32_t > > &a, std::int32_t b, const std::vector< sdbus::Struct< std::string, std::int32_t > > &c, const std::vector< sdbus::Struct< std::string, std::int32_t > > &d, const std::vector< sdbus::Struct< std::string, std::string > > &e, std::int32_t f, std::int32_t g, bool h, bool i ) { fcitx5_client_ui v; v.message = c.empty() ? std::string() : c[ 0 ].get< 0 >(); v.selected = f; v.page = h; for( auto &x: e ) { v.cands.push_back( x.get< 0 >() ); } std::optional< promise< fcitx5_client_ui > > p; ม׵ީิ΢Οϯυ΢ͷ಺༰Λߋ৽͢΂͖࣌͸ UpdateClientSideUIγάφϧ͕ඈΜͰ͘Δ
  187. input_context->uponSignal( "UpdateFormattedPreedit" ) .onInterface( "org.fcitx.Fcitx.InputContext1" ) .call( [self=shared_from_this()]( const std::vector<

    sdbus::Struct< std::string, std::int32_t > > &a, std::int32_t b ) { fcitx5_preedit v; v.selected = b; for( auto &x: a ) { v.cands.push_back( x.get< 0 >() ); } std::optional< promise< fcitx5_preedit > > p; { std::scoped_lock< std::mutex > lock( self->guard ); if( self->preedit_p ) { p = std::move( *self->preedit_p ); self->preedit_p = std::nullopt; } else { self->preedit_v = v; } Preedit(ະ֬ఆͷจࣈྻ)ͷ಺༰Λมߋ͢΂͖࣌͸ UpdateFormattedPreeditγάφϧ͕ඈΜͰ͘Δ
  188. None
  189. ະղܾͷ໰୊

  190. ͜͏͍͏ͷ ൒ಁ໌ͷ௕ํܗ ޙΖͷཁૉ͕ઌʹඳ͔ΕΔࣄΛ อূ͠ͳ͚Ε͹ͳΒͳ͍ Πϯελϯγϯά͔Β ൒ಁ໌ͷཁૉ͚ͩআ֎ͯ͠ Zιʔτ͠ͳ͚Ε͹ͳΒ͍

  191. ॎ ॻ ͖ ɹ ೔ ຊ ޠ ౳ ͷ ͍

    ͘ ͭ ͔ ͷ ݴ ޠ Ͱ ͸ ্ ͔ Β Լ ʹ ς Ω ε τ Λ ਐ Ί Δ ॻ ͖ ํ ͕ ఆ ٛ ͞ Ε ͯ ͍ Δ ɹ HarfBuzz ͸ ॎ ॻ ͖ Λ α ϙ τ ͠ ͯ ͍ Δ ͷ Ͱ ೔ ຊ ޠ ͷ จ ࣈ Λ ॎ ॻ ͖ Ͱ ฒ ΂ Δ ࣄ ࣗ ମ ͸ ೉ ͠ ͘ ͳ ͍ ɹ ͠ ͔ ͠ ɹ ॎ ॻ ͖ ͷ ς Ω ε τ ͷ த ʹ English ͷ Α ͏ ͳ ॎ ॻ ͖ Ͱ ͖ ͳ ͍ ݴ ޠ ͕ ࠞ ͟ ͯ ͖ ͨ Β ά Ϧ ϑ Λ ճ స ͞ ͤ ͳ ͚ Ε ͹ ͳ Β ͳ ͍ ɹ ॎ ॻ ͖ ͷ ς Ω ε τ ͷ த ʹ תי ִ רְבִע ʿ Īvrīt ͷ Α ͏ ͳ ӈ ͔ Β ࠨ ʹ ਐ Ή ݴ ޠ ͕ ࠞ ͟ ͯ ͖ ͨ Β Ͳ ͏ ͠ Α ͏
  192. ΞϯνΤΠϦΞεΛ͔͚͍ͯͳ͍ͷͰ จࣈ͕݁ߏΨλΨλ͍ͯ͠Δ MSAAΛ࢖͑͹៉ྷʹͳΔ͔΋?

  193. OpenType TrueTypeʹ3࣍BézierεϓϥΠϯۂઢͷαϙʔτͳͲ ༷ʑͳػೳ௥ՃΛͨ͠෺ ttf2meshͰ͸ಡΊͳ͍ NotoSansCJK͕OpenTypeͰ ͜Ε͕ಡΊͳ͍ͷ͕݁ߏਏ͍

  194. sdbus gct͸MITϥΠηϯεͰ഑෍͍ͯ͠Δ͕ sdbus͕LGPLͳͷͰ gctΛϦϯΫ͢ΔͱʮϦόʔεΤϯδχΞϦϯάͷڐՄʯΛٻΊΒΕΔ sdbus͸ࣗલͰI/OεϨουΛ͍࣋ͬͯΔ͕ Ͱ͖Ε͹gctͷI/OεϨουͰΠϕϯτΛ଴͍ͨͤͨ কདྷతʹdbusΛࣗલͰ࣮૷͢Δ

  195. RaspberryPiͰ࣮ߦ͢ΔͱΫϥογϡ͢Δ ଟ෼σεΫϦϓλͷ࢖͍͚͗ͩ͢Ͳ ৄ͘͠͸ௐ΂ΒΕ͍ͯͳ͍

  196. ·ͱΊ GUI͢Δͷʹ ͸ཁΒͳ͍ ϓϥΠϚϦϊʔυΛѲͬͯ ը໘ͷίϯτϩʔϧΛखʹೖΕΑ͏ libinputͰೖྗΛड͚෇͚Α͏