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

    View Slide

  2. GPU

    View Slide

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

    View Slide

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


    σʔλ
    ϓϩηε2ͷ


    σʔλ
    ܭࢉͷػೳΛڞ༗͢Δ

    View Slide

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


    σʔλ
    ϓϩηε2ͷ


    σʔλ
    ܭࢉͷػೳΛڞ༗͢Δ

    View Slide

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

    View Slide

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


    GPUͷѻ͍
    Linux
    ϓϥΠϚϦϊʔυ

    View Slide

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


    GPUͷѻ͍
    Linux
    ϓϥΠϚϦϊʔυ
    Ϩϯμʔϊʔυ
    /dev/dri/renderD128ͱ͔
    GPUͷϝϞϦΛ֬อͰ͖Δ


    GPUͰܭࢉͰ͖Δ


    දࣔ͸Ͱ͖ͳ͍
    ͜ͷσόΠεΛհͯ͠
    ෳ਺ͷϓϩηε͕ಉ࣌ʹ


    ͜ͷσόΠεΛ։͚Δ

    View Slide

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


    Uͷѻ͍
    ux
    ϓϥΠϚϦϊʔυ
    ϓϥΠϚϦϊʔυ
    /dev/dri/card0ͱ͔
    GPUͷϝϞϦΛ֬อͰ͖Δ


    GPUͰܭࢉͰ͖Δ


    දࣔ΋Ͱ͖Δ
    ͜ͷσόΠεΛհͯ͠
    ಉ࣌ʹ͜ͷσόΠεΛ


    ։͚Δϓϩηε͸1͚ͭͩ

    View Slide

  10. ϏσΦϝϞϦ
    Χʔωϧۭؒ
    Ϣʔβۭؒ Ϣʔβۭؒ
    Ϣʔβۭؒ


    Linux
    αʔό
    ΫϥΠΞϯτ ΫϥΠΞϯτ

    ͘

    Ή
    Ϩϯμʔϊʔυ

    Ή

    ͘

    ͘
    ϓϥΠϚϦϊʔυ
    ͓લɺڞ༗ϝϞϦʹॻ͘


    Զɺڞ༗ϝϞϦΛಡΉ
    ॻ͍ͨ
    Window System

    View Slide

  11. ϏσΦϝϞϦ
    Χʔωϧۭؒ
    Ϣʔβۭؒ Ϣʔβۭؒ
    Ϣʔβۭؒ


    Linux
    Wayland


    ίϯϙδλ
    Wayland


    ΫϥΠΞϯτ

    ͘

    Ή
    Ϩϯμʔϊʔυ

    Ή

    ͘

    ͘
    ϓϥΠϚϦϊʔυ
    ͓લɺڞ༗ϝϞϦʹॻ͘


    Զɺڞ༗ϝϞϦΛಡΉ
    ॻ͍ͨ
    Wayland
    Wayland


    ΫϥΠΞϯτ

    View Slide

  12. ϏσΦϝϞϦ
    Χʔωϧۭؒ
    Ϣʔβۭؒ
    Linux
    Mesa
    ϋʔυ΢ΣΞඇґଘͷ


    GPUͷૢ࡞
    ಛఆͷGPU޲͚ͷ


    ίϚϯυ
    Ϩϯμʔϊʔυ
    libdrm
    QtɺGTK+౳
    ΞϓϦέʔγϣϯ
    ΢Οδοτͷૢ࡞
    ڞ༗ϝϞϦ
    ϓϥΠϚϦϊʔυ

    View Slide

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


    ͍ͩ͘͞
    Mesa
    libdrm
    ϓϥΠϚϦϊʔυ
    ͜ΕΛ


    ࢖ͬͯ

    View Slide

  14. ϏσΦϝϞϦ
    Χʔωϧۭؒ
    Ϣʔβۭؒ
    Linux
    Mesa
    Ϩϯμʔϊʔυ
    libdrm
    QtɺGTK+౳
    ΞϓϦέʔγϣϯ
    Ϣʔβۭؒ
    αʔό
    xcb
    ඳ͖ऴΘͬͨ
    Mesa
    libdrm
    ϓϥΠϚϦϊʔυ
    ͔͜͜Β


    ͜͜΁


    ίϐʔ
    ඳ͖ऴΘͬͨ
    ϋʔυ΢ΣΞඇґଘͷ


    GPUͷૢ࡞
    ಛఆͷGPU޲͚ͷ


    ίϚϯυ

    View Slide

  15. ϏσΦϝϞϦ
    Χʔωϧۭؒ
    Ϣʔβۭؒ
    Linux
    Mesa
    Ϩϯμʔϊʔυ
    libdrm
    QtɺGTK+౳
    ΞϓϦέʔγϣϯ
    Ϣʔβۭؒ
    αʔό
    xcb
    Mesa
    libdrm
    ϓϥΠϚϦϊʔυ
    ͜ΕΛදࣔͯ͠
    ϋʔυ΢ΣΞඇґଘͷ


    GPUͷૢ࡞
    ಛఆͷGPU޲͚ͷ


    ίϚϯυ

    View Slide

  16. ϏσΦϝϞϦ
    Χʔωϧۭؒ
    Ϣʔβۭؒ
    Linux
    Mesa
    Ϩϯμʔϊʔυ
    libdrm
    ΞϓϦέʔγϣϯ
    Ϣʔβۭؒ
    αʔό
    xcb
    ڞ༗ϝϞϦ
    Mesa
    libdrm
    ϓϥΠϚϦϊʔυ
    ϋʔυ΢ΣΞඇґଘͷ


    GPUͷૢ࡞
    ಛఆͷGPU޲͚ͷ


    ίϚϯυ
    ΞϓϦέʔγϣϯ͕


    Vulkan΍OpenGLΛ


    ௚઀஻Δ৔߹

    View Slide

  17. Ϣʔβۭؒ
    Mesa
    ΞϓϦέʔγϣϯ
    ϋʔυ΢ΣΞඇґଘͷ


    GPUͷૢ࡞
    ಛఆͷGPU޲͚ͷ


    ίϚϯυ
    ϏσΦϝϞϦ
    Χʔωϧۭؒ
    Linux
    Ϩϯμʔϊʔυ
    ϓϥΠϚϦϊʔυ
    libdrm
    ඳ͍ͯ
    ΋͠ը໘ʹද͕ࣔͨ͠Δϓϩηε͕


    ϗετʹ1͔ͭ͠ͳ͍ͳΒ

    View Slide

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


    ϗετʹ1͔ͭ͠ͳ͍ͳΒ
    Q. ͜Ε͕Ͱ͖ΔഺͰ͸
    libdrm
    ͜ΕΛදࣔͯ͠
    ϋʔυ΢ΣΞඇґଘͷ


    GPUͷૢ࡞
    ಛఆͷGPU޲͚ͷ


    ίϚϯυ

    View Slide

  19. VK_KHR_display
    A. Ͱ͖Δ

    View Slide

  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
    ͜Ε

    View Slide

  21. #include
    #include
    #include
    #include
    #include
    int main( int argc, const char *argv[] ) {
    #ifdef VULKAN_HPP_DISPATCH_LOADER_DYNAMIC
    // Vulkan-Hpp͔ΒvkCreateInstanceΛݺ΂ΔΑ͏ʹ͢Δ
    vk::DynamicLoader dl;
    PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr =
    dl.getProcAddress( "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) )
    // Τϯδϯͷ໊લ

    View Slide

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

    View Slide

  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ͱσΟεϓϨΠͷ৘ใΛऔಘ

    View Slide

  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;
    }
    }
    } ར༻ՄೳͳදࣔϞʔυΛऔಘ

    View Slide

  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]
    ରԠϞʔυ : 3840x2160@50Hz
    ରԠϞʔυ : [email protected]
    ରԠϞʔυ : 3840x2160@25Hz
    ରԠϞʔυ : [email protected]
    ରԠϞʔυ : [email protected]
    ରԠϞʔυ : [email protected]

    View Slide

  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 )

    View Slide

  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

    View Slide

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

    View Slide

  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;
    }
    }
    αʔϑΣε্Ͱར༻ՄೳͳΠϝʔδͷ਺Λऔಘ

    View Slide

  30. ϏσΦϝϞϦ
    Χʔωϧۭؒ
    Ϣʔβۭؒ
    Linux
    Mesa
    ΞϓϦέʔγϣϯ
    දࣔ
    ϋʔυ΢ΣΞඇґଘͷ


    GPUͷૢ࡞
    ಛఆͷGPU޲͚ͷ


    ίϚϯυ
    ඳը
    ϓϥΠϚϦϊʔυ
    ੾Γସ͑
    දࣔத
    libdrm
    ඳ͖͔͚͕


    දࣔ͞Εͯ͸͍͚ͳ͍
    αʔϑΣε͸


    2ը໘෼Ҏ্ͷαΠζͰ


    ֬อ͞ΕΔ
    ඳ͖ऴΘͬͨΒ


    දࣔ͢ΔΠϝʔδΛ


    ੾Γସ͑Δ

    View Slide

  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ͰίϚϯυ͕


    ࣮ߦ͞ΕΔ

    View Slide

  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ͷઃఆ
    ࿦ཧσόΠε
    +
    +
    =

    View Slide

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

    View Slide

  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
    αʔϑΣεͷͲͷΠϝʔδ͕දࣔத͔Λอ࣋͢Δ
    දࣔதͩͬͨΠϝʔδ͕දࣔதͰͳ͘ͳͬͨΒڭ͑ͯ͘ΕΔ
    εϫοϓνΣΠϯ

    View Slide

  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 )
    εϫοϓνΣΠϯ͔ΒશͯͷΠϝʔδΛऔಘ͠

    View Slide

  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 )
    }
    );
    ੨ͰృΓͭͿ͢ίϚϯυΛ༻ҙ

    View Slide

  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();
    }
    ը໘ʹදࣔΛ࢝ΊΔલʹ


    αʔϑΣεΛ੨ͰృΓͭͿ͓ͯ͘͠
    ίϚϯυΛΩϡʔʹྲྀͯ͠׬ྃΛ଴ͭ

    View Slide

  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()
    εϫοϓνΣΠϯ͔Β


    ඳ͍ͯ΋େৎ෉ͳαʔϑΣεͷΠϝʔδͷΠϯσοΫεΛ໯͍

    View Slide

  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ϑϨʔϜͷද͕ࣔ࣌ؒܦա͢Δ·Ͱ଴ͭ
    ಺༰Λॻ͖׵͑ͳ͍Ͱ͙͢ʹදࣔʹ·Θ͢

    View Slide

  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 );
    }
    }
    ͜ΕΛσΟεϓϨΠͷϦϑϨογϡϨʔτʹಉظͯ͠܁Γฦ͢

    View Slide

  41. https://speakerdeck.com/fadis/imadokifalsevulkan
    ৄ͍͠Vulkanͷ࢖͍ํ͸


    Ҏલղઆͨ͠΍͕ͭ͋Δ͔Β


    ͦͬͪΛݟͯͶ

    View Slide

  42. View Slide

  43. View Slide

  44. UIΛඳ͖͍ͨ

    View Slide

  45. UIͷத਎͸


    Πϕϯτ͕ى͜Βͳ͍ݶΓ


    มԽ͠ͳ͍
    UIͷ֎͸


    ຖϑϨʔϜಈ͔͘΋͠Εͳ͍͚Ͳ

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  51. GPU
    CPU
    ௕ํܗ
    ͷඳըཁٻΛେྔʹ౤͛Δͱ


    CPUଆͷෛ୲͕όΧʹͳΒͳ͍
    ௕ํܗ
    ͸Α࣍
    GPU͸εϨου਺ͰੑೳΛՔ͙ͷͰ


    খ͍͞ඳըཁٻͰ͸ੑೳ͕Ͱͳ͍

    View Slide

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


    ௕ํܗ1ͷ഑ஔ


    ௕ํܗ2ͷ഑ஔ


    ௕ํܗ3ͷ഑ஔ



    View Slide

  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)

    View Slide

  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)

    View Slide

  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ݸΛ௒͑Δ৔߹͸ෳ਺ͷίϚϯυʹ෼͚Δ
    ௕ํܗ

    View Slide

  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)
    ΛඞཁͳαΠζʹͯ͠
    εΫϦʔϯ࠲ඪܥʹม׵ͯ͠ϥελϥΠβ΁

    View Slide

  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͔ΒಡΉߏ଄ମʹ৘ใΛੵΜͰ͓͘

    View Slide

  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
    );
    } ཚ਺ͰܾΊͨҐஔʹཚ਺ͰܾΊͨ৭ͷ Λඳ͘
    ௕ํܗ

    View Slide

  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ݸ

    View Slide

  60. View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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ʹηοτ͞ΕͨΠϝʔδΛ


    ςΫενϟαϯϓϥʔʹ݁ͼ͚ͭΔ

    View Slide

  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ͷҾ਺ʹΠϝʔδΛ౉͢

    View Slide

  66. View Slide

  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ͷ΢Οϯυ΢Λ࡞ͬͯ

    View Slide

  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()
    )
    }
    );
    ࢖͏ը૾Ληοτͯ͠

    View Slide

  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 } },
    Ϙλϯͱ͔ฒ΂ͯ

    View Slide

  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ͷඳը݁ՌΛ


    ೖྗͱͯ͠౉ͤΔ

    View Slide

  71. View Slide

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

    View Slide

  73. จࣈ͸


    2࣍BézierεϓϥΠϯۂઢͰ


    දݱ͞ΕͨϕΫλը૾
    TrueType

    View Slide

  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ͳΞϓϦέʔγϣϯʹ૊ΈࠐΊͳ͍

    View Slide

  75. ࣄલʹϑΥϯτΛࡾ֯ܗ෼ׂͨ͠σʔλΛ༻ҙͯ͠


    ࣮ߦ࣌͸ͦΕΛͦͷ··GPUͰඳը͠Α͏

    View Slide

  76. https://github.com/fetisov/ttf2mesh
    ttf2mesh
    TrueTypeͷ


    ϑΥϯτΛಡΜͰ


    ࡾ֯ܗ෼ׂͯ͠


    ௖఺ͷϦετΛฦ͢


    ϥΠϒϥϦ
    ͜ΕΛ࢖ͬͯ


    ࣄલʹϑΥϯτΛ


    ௖఺഑ྻʹ͠Α͏

    View Slide

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

    View Slide

  78. Φϑηοτςʔϒϧ
    ςʔϒϧ
    ςʔϒϧ
    ςʔϒϧ

    55'ͷόʔδϣϯ
    ςʔϒϧ਺
    ςʔϒϧͷछྨ
    νΣοΫαϜ
    ςʔϒϧͷઌ಄ͷҐஔ
    ςʔϒϧͷ௕͞
    ςʔϒϧͷछྨ
    νΣοΫαϜ
    ςʔϒϧͷઌ಄ͷҐஔ
    ςʔϒϧͷ௕͞

    TrueTypeͷߏ଄

    View Slide

  79. TrueTypeͷߏ଄
    ςʔϒϧ໊ ಺༰
    head name OS/2 maxp post ϝλσʔλ
    glyf άϦϑΛߏ੒͢Δۂઢͷύϥϝʔλ
    loca
    Կ൪໨ͷάϦϑ͕glyfͷ


    Կཁૉ໨͔Β࢝·Δ͔
    cmap
    locaͷԿ൪໨ͷάϦϑ͕


    จࣈίʔυͰݴ͏ԿͷจࣈʹରԠ͢Δ͔

    View Slide

  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ͷҟࣈମηϨΫλͰબ͹ΕΔจࣈͱάϦϑͷରԠΛ࣋ͭ

    View Slide

  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ΛؚΜͰ͍Δ

    View Slide

  82. Unicodeͷۭؒ
    جຊଟݴޠ໘ #.1

    ௥Ճଟݴޠ໘ 4.1

    ௥Ճ׽ࣈ໘ 4*1

    ୈࡾ׽ࣈ໘ 5*1

    ະׂΓ౰ͯ
    ௥Ճಛघ༻్໘ 441

    ࢲ༻໘
    0x000000
    0x010000
    0x020000
    0x030000
    0x040000
    0x0F0000
    0x110000
    0x0E0000
    16bitͰදݱͰ͖Δൣғ
    ೔ຊޠϑΥϯτͷจࣈ͕


    ࢖͏ൣғ
    ttf2mesh͸


    ௥Ճ׽ࣈ໘ͷ׽ࣈͷάϦϑͷ


    ίʔυϙΠϯτΛऔΕͳ͍

    View Slide

  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੔਺ʹஔ͖׵͑ͯ

    View Slide

  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͕͋ͬͨΒ


    ༏ઌతʹಡΉίʔυΛ௥Ճ

    View Slide

  85. Format 12ରԠ൛ttf2mesh
    https://github.com/Fadis/ttf2mesh

    View Slide

  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จࣈ෼

    View Slide

  87. glTF
    3DϞσϧσʔλͷϑΥʔϚοτ
    JSONܗࣜͷϔομͰ


    GPUʹૹΔόΠφϦΛ


    ͲͷΑ͏ʹղऍ͢΂͖͔Λهड़
    ͜ͷϔομʹόΠφϦຊମΛ


    σʔλͱ͚ͯͬͭͨ͘͠෺
    https://www.khronos.org/gltf/

    View Slide

  88. TrueType glTF
    ttf2mesh

    View Slide

  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ͰϑΥϯτΛಡΉ

    View Slide

  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(
    άϦϑͷ৘ใ͕ਖ਼͘͠औΕ͍ͯͯ


    ཉ͍͠ൣғͷจࣈͷάϦϑͩͬͨΒ

    View Slide

  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 ) );
    ࡾ֯ܗ෼ׂ
    ௖఺഑ྻʹಉ͡࠲ඪͷ௖఺͕طʹ͋ͬͨΒͦͷΠϯσοΫεΛฦ͢
    ແ͔ͬͨΒ௖఺഑ྻʹ௖఺Λ௥Ճͯͦ͠ͷΠϯσοΫεΛฦ͢

    View Slide

  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ͷάϦϑͰ͢

    View Slide

  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ΤϯίʔμͰ࡞ΕΔ

    View Slide

  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 ] );
    ֤άϦϑͷઌ಄Λࢦ͢


    όοϑΝϏϡʔΛ࡞Δ
    ࠷ޙʹ௖఺഑ྻͷ


    όοϑΝϏϡʔΛ࡞Δ

    View Slide

  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
    })}
    }) );
    ֤όοϑΝϏϡʔΛࢦ͢


    ΞΫηαΛ࡞Δ
    ௖఺഑ྻΛࢦ͢ΞΫηαΛ࡞Δ

    View Slide

  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ʹ͓ͯ͘͠

    View Slide

  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Λ


    ϑΝΠϧʹు͘

    View Slide

  98. ී௨ͷglTF͔ͩΒ


    BlenderͰಡΊΔ
    1ͭͷάϦϑ͕


    1ͭͷϊʔυʹͳ͍ͬͯΔ
    ΛϕʔεϥΠϯͱͯ͠


    άϦϑ͕ฒΜͰ͍Δ
    y = 0

    View Slide

  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ʹૹΔ

    View Slide

  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

    View Slide

  101. View Slide

  102. จࣈΛGPUͰେྔʹඳ͖͍ͨ

    View Slide

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


    ௕ํܗ1ͷ഑ஔ


    ௕ํܗ2ͷ഑ஔ


    ௕ํܗ3ͷ഑ஔ



    ݸʑͷจࣈ͸τϙϩδ͕ҟͳΔҝ


    ΠϯελϯγϯάͰ·ͱΊͯඳ͚ͳ͍

    View Slide

  104. CPU͔Β΋GPU͔Β΋ݟ͑ΔϝϞϦ
    GPU
    CPU
    Indirect


    Draw
    1ίϚϯυͰҟͳΔτϙϩδͷਤܗΛେྔʹඳ͘
    άϦϑ0ͷ഑ஔ


    άϦϑ1ͷ഑ஔ


    άϦϑ2ͷ഑ஔ


    άϦϑ3ͷ഑ஔ



    άϦϑ0ͷ௖఺ͷಡΈํ


    άϦϑ1ͷ௖఺ͷಡΈํ


    άϦϑ2ͷ௖఺ͷಡΈํ
    άϦϑ3ͷ௖఺ͷಡΈํ


    ͜͜ʹྻڍͨ͠΍ͭඳ͍ͯ

    View Slide

  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ʹૹͬͨσʔλͷதʹ͋Δ͔Λௐ΂ͯ

    View Slide

  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;
    }
    }
    άϦϑͷ৭ͱ഑ஔ
    άϦϑͷ௖఺ΛͲͷҐஔ͔ΒಡΉ͔

    View Slide

  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 )
    );
    }
    ϑΥϯτͷ௖఺഑ྻΛ࢖͏
    ϑΥϯτͷ௖఺ΠϯσοΫεΛ࢖͏
    ͜͜ʹஔ͍ͯ͋Δ௖఺ͷಡΈํʹैͬͯ
    ͜ͷ਺ͷάϦϑΛඳը

    View Slide

  108. View Slide

  109. จࣈαΠζ෼ͮͭӈʹਐΉ
    ຊ౰ʹͦΕͰେৎ෉?

    View Slide

  110. όΠτྻ
    ୯७ʹฒ΂Δͱ
    ਖ਼͍͠දࣔ
    H e l l o
    48 65 6c 6c 6f
    Hello
    Hello
    ӳޠ
    จࣈαΠζ෼ͮͭӈʹਐΉ

    View Slide

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

    View Slide

  112. όΠτྻ
    ୯७ʹฒ΂Δͱ
    ਖ਼͍͠දࣔ
    ส ว ั ส ด ี
    e0 b8 aa e0 b8 a7 e0 b8 b1 e0 b8 aa e0 b8 94 e0 b8 b5

    วั

    ดี



    วั

    ดี
    ্ʹ৐͔ͬͬͯΔ



    ส ั ี
    λΠޠ
    ෳ਺ͷίʔυϙΠϯτͷฒͼͰάϦϑΛඳ͘΂͖Ґஔ͕มΘΔ

    View Slide

  113. όΠτྻ
    ୯७ʹฒ΂Δͱ
    ਖ਼͍͠දࣔ
    न म स



    e0 a4 a8 e0 a4 ae e0 a4 b8 e0 a5 8d e0 a4 a4 e0 a5 87
    नम
    स्त

    नमस



    नम
    स्ते
    ࣍ͷίʔυϙΠϯτͱ߹ମͯ͠ܗ͕มΘ͍ͬͯΔ
    ώϯσΟʔޠ
    ෳ਺ͷίʔυϙΠϯτͷฒͼͰඳ͘΂͖άϦϑ͕มΘΔ

    View Slide

  114. όΠτྻ
    ୯७ʹฒ΂Δͱ
    ਖ਼͍͠දࣔ
    ש ל ו ם
    d7 a9 d7 9c d7 95 d7 9d
    םולש


    שלום
    םולש
    ӈ͔Βࠨ
    ϔϒϥΠޠ
    1จࣈඳ͍ͨΒࠨʹਐΉ

    View Slide

  115. όΠτྻ
    ୯७ʹฒ΂Δͱ
    ਖ਼͍͠දࣔ
    م ر ح ب ً ط
    d9 85 d8 b1 d8 ad d8 a8 d9 8b d8 a7
    ﺎًﺑﺣرﻣ
    مرحبً
    ط
    ﺎًﺑﺣرﻣ
    ӈ͔Βࠨ͔ͭ࣍ͷίʔυϙΠϯτͱ߹ମͯ͠ผͷάϦϑʹมԽ
    ΞϥϏΞޠ

    View Slide

  116. HarfBuzz
    Φʔϓϯιʔεͳ


    ςΩετγΣΠϐϯάϥΠϒϥϦ
    ༩͑ΒΕͨจࣈྻ͔Β
    ϑΥϯτʹ͋ΔͲͷάϦϑΛ
    ͲͷҐஔʹ
    ඳ͘΂͖͔ΛٻΊΔ
    https://harfbuzz.github.io/

    View Slide


  117. วั

    ดี
    HarfBuzz͕΍ͬͯ͘ΕΔࣄ
    ࣍ͷจࣈΛ


    ඳ͘΂͖ҐஔΛٻΊΔ
    नम
    स्ते

    ͲͷάϦϑͰ


    ඳ͘΂͖͔ΛٻΊΔ
    ࠓ͙͢μ΢ϯϩʔ
    υ
    HarfBuzz͕΍ͬͯ͘Εͳ͍ࣄ
    ௕͍จࣈྻͷંΓฦ͠
    םולש


    שלום
    จࣈྻͷਐߦํ޲ͷ൑அ
    ?
    ?
    จࣈͷϨϯμϦϯά
    םולש
    ৗʹࠨ͔Β


    ॲཧͰ͖ΔΑ͏ʹฒ΂ସ͑

    View Slide


  118. วั

    ดี
    HarfBuzz͕΍ͬͯ͘ΕΔࣄ
    ࣍ͷจࣈΛ


    ඳ͘΂͖ҐஔΛٻΊΔ
    नम
    स्ते

    ͲͷάϦϑͰ


    ඳ͘΂͖͔ΛٻΊΔ
    ࠓ͙͢μ΢ϯϩʔ
    υ
    HarfBuzz͕΍ͬͯ͘Εͳ͍ࣄ
    ௕͍จࣈྻͷંΓฦ͠
    םולש


    שלום
    จࣈྻͷਐߦํ޲ͷ൑அ
    ?
    ?
    จࣈͷϨϯμϦϯά
    םולש
    ৗʹࠨ͔Β


    ॲཧͰ͖ΔΑ͏ʹฒ΂ସ͑
    ࠨ͔Βӈʹॻ͘ݴޠͳͷ͔


    ӈ͔Βࠨʹॻ͘ݴޠͳͷ͔͸


    ΞϓϦέʔγϣϯ͕൑அ


    HarfBuzz͸ࢦఆ͞Εͨ޲͖ʹจࣈΛฒ΂Δॲཧ͚ͩΛ͢Δ

    View Slide

  119. https://unicode.org/reports/tr9/
    Unicode Standard Annex #9
    Unicode૒ํ޲ΞϧΰϦζϜ
    ӈ͔ΒࠨʹਐΉݴޠͷจࣈ͕


    ؚ·Ε͍ͯΔ৔߹ͷ


    จࣈͷ഑ஔํ๏Λఆٛ͢Δ
    ΊΜͲ͍͘͞ϧʔϧ͕


    ௕ʑͱॻ͔Ε͍ͯΔ

    View Slide

  120. ICU
    Unicodeʹ·ͭΘΔ


    ΊΜͲ͍͘͞ॲཧΛ࣮૷ͨ͠


    ϥΠϒϥϦ
    https://icu.unicode.org/

    View Slide

  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 จࣈྻۭ΍Μ…
    ฦΓ஋

    View Slide

  122. An apple is called חופת in Hebrew.
    .תילגנאב תארקנ חופת
    apple
    ࠨ͔ΒӈͷςΩετͷதʹӈ͔ΒࠨͷςΩετ͕͍ࠞͬͯ͟Δ
    ӈ͔ΒࠨͷςΩετͷதʹࠨ͔ΒӈͷςΩετ͕͍ࠞͬͯ͟Δ
    ӈ͔Βࠨʹॻ͘ݴޠͰ΋௨ৗ਺ࣈ͸ࠨ͔Βӈʹॻ͘ͷͰ


    ӈ͔ΒࠨͷςΩετʹ͸೔ৗతʹࠨ͔ΒӈͷςΩετ͕ࠞ͟Δ

    View Slide

  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Ͱ


    ਐߦํ޲͕ಉ͡ൣғͰจࣈྻΛ۠੾Δ
    ൣғͷ਺Λऔಘ
    ͜ͷൣғ͸Ͳͬͪ޲͖?

    View Slide


  124. วั

    ดี
    HarfBuzz͕΍ͬͯ͘ΕΔࣄ
    ࣍ͷจࣈΛ


    ඳ͘΂͖ҐஔΛٻΊΔ
    नम
    स्ते

    ͲͷάϦϑͰ


    ඳ͘΂͖͔ΛٻΊΔ
    ࠓ͙͢μ΢ϯϩʔ
    υ
    HarfBuzz͕΍ͬͯ͘Εͳ͍ࣄ
    ௕͍จࣈྻͷંΓฦ͠
    םולש


    שלום
    จࣈྻͷਐߦํ޲ͷ൑அ
    ?
    ?
    จࣈͷϨϯμϦϯά
    םולש
    ৗʹࠨ͔Β


    ॲཧͰ͖ΔΑ͏ʹฒ΂ସ͑
    ௕͍ςΩετ͸ંΓฦͯ͠දࣔ͠ͳ͚Ε͹ͳΒͳ͍
    Unicodeจࣈྻʹ͸ંΓฦͯ͠͸͍͚ͳ͍Օॴ͕͋Δ

    View Slide

  125. English string should not break in the middle
    of a word.
    ೔ຊޠͷจࣈྻͰ͸ࣺͯԾ໊͸ߦ಄ʹ࣋ͬ
    ͯ͘Δ΂͖Ͱ͸ͳ͍
    ͜͜ͳΒંΓฦͯ͠Α͍
    ͜͜ͰંΓฦͯ͠͸͍͚ͳ͍
    ͜͜ͳΒંΓฦͯ͠Α͍
    ͜͜ͰંΓฦͯ͠͸͍͚ͳ͍

    วั

    ดี
    1จࣈͱݟ၏͞ΕΔγʔέϯεͷ్தͰંΓฦ͢΂͖Ͱ͸ͳ͍

    View Slide

  126. https://unicode.org/reports/tr14/
    Unicode Standard Annex #14
    Unicodeߦ෼ׂΞϧΰϦζϜ
    Unicodeʹؚ·ΕΔੈքதͷݴޠͷ


    ʮͲͷจࣈͱͲͷจࣈͷؒͳΒ


    վߦΛڬΜͰ΋ྑ͍Ͱ͔͢?ʯ


    Λ·ͱΊͨ෺
    ΊΜͲ͍͘͞ϧʔϧ͕


    ௕ʑͱॻ͔Ε͍ͯΔ

    View Slide

  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จࣈྻΛ


    ʮվߦΛڬΜͰ͸͍͚ͳ͍ʯ୯ҐͰ۠੾Δࣄ͕Ͱ͖Δ

    View Slide

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

    View Slide

  129. ICU ubrk
    HarfBuzz
    LTRจࣈྻ RTLจࣈྻ LTRจࣈྻ
    ୯ޠ
    ୯ޠ ୯ޠ ୯ޠ ୯ޠ
    ୯ޠ
    ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ
    ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ
    ࣈ ࣈ
    ߦͷંΓฦ͠
    ը໘্Ͱͷจࣈͷ഑ஔ

    View Slide

  130. 1em
    HarfbuzzͰ͸௕͞͸


    Λ1ͱ͢Δ୯ҐͰ


    දݱ͞ΕΔ
    1
    1024
    em

    View Slide

  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
    ΧʔιϧͷҐஔ͔Β


    ͲΕ͚ͩͣΕͨ৔ॴʹ


    จࣈΛඳ͘΂͖͔
    จࣈΛඳ͍ͨޙͰ


    ΧʔιϧΛͲΕ͚ͩ


    ਐΊΔ΂͖͔

    View Slide

  132. 1pt =
    1
    72
    in ≃ 0.3528mm
    1pt = 0.3514mm
    ݩʑͷఆٛ
    JISن֨Ͱͷఆٛ
    จࣈͷେ͖͞ͷ୯Ґ ϙΠϯτ(pt)
    ͲͪΒʹͤΑݴ͑Δ͜ͱ


    ը໘ͷ෺ཧతͳେ͖͞ͷ৘ใ͕ඞཁ

    View Slide

  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୯Ґͷ


    ը໘ͷେؚ͖͕͞·Ε͍ͯΔ
    σΟεϓϨΠ͸ࣗ਎ͷ෺ཧతͳେ͖͞Λ஻͍ͬͯΔ
    ϓϩδΣΫλ౳αΠζΛఆٛͰ͖ͳ͍ػثͰ͸


    ஻͍ͬͯͳ͍ࣄ΋͋Δ

    View Slide

  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͙͠͞Λ͓ͯ͜͠͏

    View Slide

  135. จষ͸Wikipediaͷ՘ࢠͷهࣄ͔ΒҾ༻ https://en.wikipedia.org/wiki/Eggplant

    View Slide

  136. View Slide

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

    View Slide

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

    View Slide

  139. λΠޠ͸ஈམͷ࠷ॳʹ


    େ͖ͳࣈԼ͛ΛೖΕΔ͕
    ࣈԼ͛Λ࣮૷͍ͯ͠ͳ͍ͷͰ


    ࠨ୺͔Βจࣈ͕ฒΜͰ͍Δ

    View Slide

  140. View Slide

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

    View Slide

  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ʹैͬͯάϦϑΛ)ʹஔ͖׵͑Δ

    View Slide

  143. ࢖͍ͬͯΔϑΥϯτ(NotoSansArabic-Regular)ʹ͸Latinܥͷจࣈ͸ؚ·Ε͍ͯͳ͍
    ผͷϑΥϯτʹϑΥʔϧόοΫ͢Δ࢓૊ΈΛ༻ҙ͍ͯ͠ͳ͍ҝදࣔͰ͖ͳ͍

    View Slide

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

    View Slide

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

    View Slide

  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ഒʹ͢ΔͱಡΊΔ

    View Slide

  147. View Slide

  148. GUI දࣔ ೖྗ
    = +

    View Slide

  149. Ϣʔβۭؒ
    Ϣʔβۭؒ
    ΞϓϦέʔγϣϯ
    xcb
    αʔό
    ͓લɺΫϦοΫ͞ΕͯΔͧ
    Xαʔό͕ډΔ৔߹


    ೖྗΠϕϯτΛXαʔό͕௨஌ͯ͘͠Δ

    View Slide

  150. Ϣʔβۭؒ
    Ϣʔβۭؒ
    Χʔωϧۭؒ
    Linux
    evdev
    libinput
    Ϛ΢ε͕ಈ͖·ͨ͠
    ΞϓϦέʔγϣϯ
    xcb
    αʔό
    ͓લɺΫϦοΫ͞ΕͯΔͧ
    Xαʔό͸࠷΋ԼͷϨΠϠʔͰ͸ͳ͍
    HIDυϥΠό Ϛ΢εͷࠨϘλϯ͕ԡ͞Ε·ͨ͠

    View Slide

  151. Ϣʔβۭؒ
    Χʔωϧۭؒ
    Linux
    evdev
    libinput
    ΞϓϦέʔγϣϯ
    ͜͏͢Ε͹


    Xαʔό͕ແͯ͘΋


    ೖྗΠϕϯτΛऔΕΔ
    HIDυϥΠό
    Ϛ΢ε͕ಈ͖·ͨ͠
    Ϛ΢εͷࠨϘλϯ͕ԡ͞Ε·ͨ͠

    View Slide

  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ͷίϯςΩετΛ࡞Δ
    γʔτΛ࡞Δ
    Πϕϯτ͋ͬͨΒ͘Ε

    View Slide

  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 {

    View Slide

  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;
    }
    }
    }
    }
    );
    Ϛ΢ε͕ಈ͍ͨΠϕϯτΛरͬͯ

    View Slide

  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
    );
    Χʔιϧ͕ಈ͍͍ͯͨΒ


    ৽͍͠ҐஔʹΧʔιϧΛ࠶ඳը

    View Slide

  156. View Slide

  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;
    }
    }
    }
    );
    ΩʔϘʔυͷΠϕϯτΛरͬͯ
    த਎Λදࣔ

    View Slide

  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
    ͜ͷ൪߸͸Կ?

    View Slide

  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ͷೖྗΠϕϯτͷ൪߸

    View Slide

  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͕ԡ͞Εͨͱ͍͏ҙຯ
    ͱ͸ݶΒͳ͍

    View Slide

  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͕͋ΔҐஔͷ


    Ωʔ͕ԡ͞Εͨͱ͍͏ҙຯ

    View Slide

  162. ANSI
    ISO
    ΩϦϧจࣈܥ ؖࠃ2Ϙϧࣜ
    JIS
    KEY_A(30)
    A͔Ͳ͏͔͸஌ΒΜ͕


    ͜ͷҐஔͷΩʔ͕ԡ͞Εͨͱ͍͏ҙຯ

    View Slide

  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͕ԡ͞Εͨͱ͍͏ҙຯ

    View Slide

  164. xkbcommon
    X Window System͔Β


    X Keyboard Extension(xkb)ͷ


    ෦෼͚ͩΛ੾Γग़ͨ͠΋ͷ
    /usr/share/X11/xkb/


    ͋ͨΓʹస͕͍ͬͯΔ


    ม׵දΛ࢖ͬͯ


    ͲͷҐஔͷΩʔ͕ԡ͞Ε͔ͨΛ


    ԿͷจࣈͷΩʔ͕ೖྗ͞Ε͔ͨʹ


    ม׵͢Δ
    https://xkbcommon.org/

    View Slide

  165. libinput͸ʮͲͷҐஔͷΩʔ͕ԡ͞Ε͔ͨʯΛΑͯ͘͜͠Δ
    xkbcommon͸ΩʔϘʔυϨΠΞ΢τΛࢦఆ͢Δͱ


    ʮͲͷҐஔͷΩʔ͕ԡ͞Ε͔ͨʯΛ


    ʮԿͷจࣈ͕ೖྗ͞Ε͔ͨʯʹม׵͢Δ
    ΩʔϘʔυϨΠΞ΢τͷ৘ใ͕ඞཁ

    View Slide

  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
    ΩʔϘʔυϨΠΞ΢τͷ৘ใ
    ͜ΕΞϓϦέʔγϣϯ͔ΒͲ͏΍ͬͯऔΔͷ?

    View Slide

  167. dbus
    όΠφϦܗࣜͷ


    γϦΞϥΠθʔγϣϯϑΥʔϚοτ


    ٴͼͦΕΛ࢖ͬͨ


    ϓϩηεؒ௨৴ͷن֨
    https://www.freedesktop.org/wiki/Software/dbus/

    View Slide

  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ʹܨ͍Ͱ
    ϓϩύςΟΛर͏ͱऔΕΔ

    View Slide

  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ͷίϯςΩετΛ࡞Δ
    ΩʔϘʔυϨΠΞ΢τͷ໊લ͔Β
    ΩʔϚοϓΛݟ͚ͭΔ
    ΩʔϘʔυͷ
    ঢ়ଶϚγϯΛ࡞Δ

    View Slide

  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ʹ౉͢

    View Slide

  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͔Β


    ೖྗ͞ΕͨจࣈΛ΋Β͏

    View Slide

  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
    ʮԿͷจࣈ͕ೖྗ͞Ε͔ͨʯ


    ͕औΕΔ

    View Slide

  173. View Slide

  174. View Slide

  175. Nihongo ga utenai...

    View Slide

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


    ೖྗϝιουʹ఻͑Δ
    HIDυϥΠό
    Qt΍GTK+
    IMMODULE
    Ϣʔβۭؒ
    ೖྗϝιου
    ม׵Τϯδϯ

    View Slide

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

    View Slide

  178. IMMODULE
    UIM
    SCIM
    IBus
    fcitx
    ௚઀ϦϯΫ
    socket
    dbus
    dbus
    Anthy
    Mozc
    SKK
    libhangul
    Chewing

    View Slide

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


    fcitxͱձ࿩͠Α͏

    View Slide

  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ʹܨ͙

    View Slide

  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ͰίϯςΩετΛ࡞Δͱ


    ίϯςΩετͷύε͕໯͑Δ
    ίϯςΩετͷύεʹܨ͙

    View Slide

  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ʹૹΔ

    View Slide

  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γάφϧͰ


    ม׵Τϯδϯͷ໊લ͕ඈΜͰ͘Δ

    View Slide

  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γάφϧͰ


    ֬ఆͨ͠಺༰͕ඈΜͰ͘Δ

    View Slide

  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γάφϧͰ


    ඈΜͰ͘Δ

    View Slide

  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γάφϧ͕ඈΜͰ͘Δ

    View Slide

  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γάφϧ͕ඈΜͰ͘Δ

    View Slide

  188. View Slide

  189. ະղܾͷ໰୊

    View Slide

  190. ͜͏͍͏ͷ
    ൒ಁ໌ͷ௕ํܗ
    ޙΖͷཁૉ͕ઌʹඳ͔ΕΔࣄΛ


    อূ͠ͳ͚Ε͹ͳΒͳ͍
    Πϯελϯγϯά͔Β


    ൒ಁ໌ͷཁૉ͚ͩআ֎ͯ͠


    Zιʔτ͠ͳ͚Ε͹ͳΒ͍

    View Slide



  191. ͖
    ɹ


    ޠ

    ͷ
    ͍
    ͘
    ͭ
    ͔
    ͷ
    ݴ
    ޠ
    Ͱ
    ͸

    ͔
    Β
    Լ
    ʹ
    ς
    Ω
    ε
    τ
    Λ

    Ί
    Δ

    ͖

    ͕

    ٛ
    ͞
    Ε
    ͯ
    ͍
    Δ
    ɹ
    HarfBuzz
    ͸


    ͖
    Λ
    α
    ϙ
    τ
    ͠
    ͯ
    ͍
    Δ
    ͷ
    Ͱ


    ޠ
    ͷ


    Λ


    ͖
    Ͱ

    ΂
    Δ



    ͸

    ͠
    ͘
    ͳ
    ͍
    ɹ
    ͠
    ͔
    ͠
    ɹ


    ͖
    ͷ
    ς
    Ω
    ε
    τ
    ͷ

    ʹ
    English
    ͷ
    Α
    ͏
    ͳ


    ͖
    Ͱ
    ͖
    ͳ
    ͍
    ݴ
    ޠ
    ͕

    ͟
    ͯ
    ͖
    ͨ
    Β
    ά
    Ϧ
    ϑ
    Λ
    ճ

    ͞
    ͤ
    ͳ
    ͚
    Ε
    ͹
    ͳ
    Β
    ͳ
    ͍
    ɹ


    ͖
    ͷ
    ς
    Ω
    ε
    τ
    ͷ

    ʹ
    תי ִ
    רְבִע ʿ
    Īvrīt
    ͷ
    Α
    ͏
    ͳ
    ӈ
    ͔
    Β

    ʹ

    Ή
    ݴ
    ޠ
    ͕

    ͟
    ͯ
    ͖
    ͨ
    Β
    Ͳ
    ͏
    ͠
    Α
    ͏

    View Slide

  192. ΞϯνΤΠϦΞεΛ͔͚͍ͯͳ͍ͷͰ


    จࣈ͕݁ߏΨλΨλ͍ͯ͠Δ
    MSAAΛ࢖͑͹៉ྷʹͳΔ͔΋?

    View Slide

  193. OpenType
    TrueTypeʹ3࣍BézierεϓϥΠϯۂઢͷαϙʔτͳͲ


    ༷ʑͳػೳ௥ՃΛͨ͠෺
    ttf2meshͰ͸ಡΊͳ͍
    NotoSansCJK͕OpenTypeͰ


    ͜Ε͕ಡΊͳ͍ͷ͕݁ߏਏ͍

    View Slide

  194. sdbus
    gct͸MITϥΠηϯεͰ഑෍͍ͯ͠Δ͕


    sdbus͕LGPLͳͷͰ


    gctΛϦϯΫ͢ΔͱʮϦόʔεΤϯδχΞϦϯάͷڐՄʯΛٻΊΒΕΔ
    sdbus͸ࣗલͰI/OεϨουΛ͍࣋ͬͯΔ͕


    Ͱ͖Ε͹gctͷI/OεϨουͰΠϕϯτΛ଴͍ͨͤͨ
    কདྷతʹdbusΛࣗલͰ࣮૷͢Δ

    View Slide

  195. RaspberryPiͰ࣮ߦ͢ΔͱΫϥογϡ͢Δ
    ଟ෼σεΫϦϓλͷ࢖͍͚͗ͩ͢Ͳ


    ৄ͘͠͸ௐ΂ΒΕ͍ͯͳ͍

    View Slide

  196. ·ͱΊ
    GUI͢Δͷʹ ͸ཁΒͳ͍
    ϓϥΠϚϦϊʔυΛѲͬͯ


    ը໘ͷίϯτϩʔϧΛखʹೖΕΑ͏
    libinputͰೖྗΛड͚෇͚Α͏

    View Slide