Share
Embed
低レイヤーから始める 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
Transcript
196 slides
-
GPU͍͍ͨ GPU͍͍ͨ ϓϩηε1 ϓϩηε2 εέδϡʔϥ ࠓϓϩηε1ʹGPUΛΘͤΑ͏ ػ ϏσΦϝϞϦ ϓϩηε1ͷ σʔλ
ϓϩηε2ͷ σʔλ ܭࢉͷػೳΛڞ༗͢Δ -
GPU͍͍ͨ GPU͍͍ͨ ϓϩηε1 ϓϩηε2 εέδϡʔϥ ࠓϓϩηε2ʹGPUΛΘͤΑ͏ ػ ϏσΦϝϞϦ ϓϩηε1ͷ σʔλ
ϓϩηε2ͷ σʔλ ܭࢉͷػೳΛڞ༗͢Δ -
Χʔωϧۭؒ DRM/KMS Ϣʔβۭؒ Ϣʔβۭؒ Ϣʔβۭؒ εέδϡʔϥ Ϩϯμʔϊʔυ ܭࢉ දࣔ LinuxͰͷ
GPUͷѻ͍ Linux ϓϥΠϚϦϊʔυ -
Χʔωϧۭؒ DRM/KMS Ϣʔβۭؒ Ϣʔβۭؒ Ϣʔβۭؒ εέδϡʔϥ Ϩϯμʔϊʔυ ܭࢉ දࣔ LinuxͰͷ
GPUͷѻ͍ Linux ϓϥΠϚϦϊʔυ Ϩϯμʔϊʔυ /dev/dri/renderD128ͱ͔ GPUͷϝϞϦΛ֬อͰ͖Δ GPUͰܭࢉͰ͖Δ දࣔͰ͖ͳ͍ ͜ͷσόΠεΛհͯ͠ ෳͷϓϩηε͕ಉ࣌ʹ ͜ͷσόΠεΛ։͚Δ -
ϧۭؒ DRM/KMS Ϣʔβۭؒ Ϣʔβۭؒ Ϣʔβۭؒ εέδϡʔϥ Ϩϯμʔϊʔυ ܭࢉ දࣔ uxͰͷ
Uͷѻ͍ ux ϓϥΠϚϦϊʔυ ϓϥΠϚϦϊʔυ /dev/dri/card0ͱ͔ GPUͷϝϞϦΛ֬อͰ͖Δ GPUͰܭࢉͰ͖Δ දࣔͰ͖Δ ͜ͷσόΠεΛհͯ͠ ಉ࣌ʹ͜ͷσόΠεΛ ։͚Δϓϩηε1͚ͭͩ -
ϏσΦϝϞϦ Χʔωϧۭؒ Ϣʔβۭؒ Ϣʔβۭؒ Ϣʔβۭؒ ද ࣔ Linux αʔό ΫϥΠΞϯτ
ΫϥΠΞϯτ ॻ ͘ ಡ Ή Ϩϯμʔϊʔυ ಡ Ή ॻ ͘ ॻ ͘ ϓϥΠϚϦϊʔυ ͓લɺڞ༗ϝϞϦʹॻ͘ Զɺڞ༗ϝϞϦΛಡΉ ॻ͍ͨ Window System -
ϏσΦϝϞϦ Χʔωϧۭؒ Ϣʔβۭؒ Ϣʔβۭؒ Ϣʔβۭؒ ද ࣔ Linux Wayland ίϯϙδλ
Wayland ΫϥΠΞϯτ ॻ ͘ ಡ Ή Ϩϯμʔϊʔυ ಡ Ή ॻ ͘ ॻ ͘ ϓϥΠϚϦϊʔυ ͓લɺڞ༗ϝϞϦʹॻ͘ Զɺڞ༗ϝϞϦΛಡΉ ॻ͍ͨ Wayland Wayland ΫϥΠΞϯτ -
ϏσΦϝϞϦ Χʔωϧۭؒ Ϣʔβۭؒ Linux Mesa ϋʔυΣΞඇґଘͷ GPUͷૢ࡞ ಛఆͷGPU͚ͷ ίϚϯυ Ϩϯμʔϊʔυ
libdrm QtɺGTK+ ΞϓϦέʔγϣϯ Οδοτͷૢ࡞ ڞ༗ϝϞϦ ϓϥΠϚϦϊʔυ -
ϏσΦϝϞϦ Χʔωϧۭؒ Ϣʔβۭؒ Linux Mesa Ϩϯμʔϊʔυ libdrm QtɺGTK+ ΞϓϦέʔγϣϯ Ϣʔβۭؒ
αʔό xcb ڞ༗ϝϞϦ͍ͩ͘͞ ڞ༗ϝϞϦ ͜ΕΛͬͯ ͜͜ʹඳ͍ͯ ڞ༗ϝϞϦ ͍ͩ͘͞ Mesa libdrm ϓϥΠϚϦϊʔυ ͜ΕΛ ͬͯ -
ϏσΦϝϞϦ Χʔωϧۭؒ Ϣʔβۭؒ Linux Mesa Ϩϯμʔϊʔυ libdrm QtɺGTK+ ΞϓϦέʔγϣϯ Ϣʔβۭؒ
αʔό xcb ඳ͖ऴΘͬͨ Mesa libdrm ϓϥΠϚϦϊʔυ ͔͜͜Β ͜͜ ίϐʔ ඳ͖ऴΘͬͨ ϋʔυΣΞඇґଘͷ GPUͷૢ࡞ ಛఆͷGPU͚ͷ ίϚϯυ -
ϏσΦϝϞϦ Χʔωϧۭؒ Ϣʔβۭؒ Linux Mesa Ϩϯμʔϊʔυ libdrm QtɺGTK+ ΞϓϦέʔγϣϯ Ϣʔβۭؒ
αʔό xcb Mesa libdrm ϓϥΠϚϦϊʔυ ͜ΕΛදࣔͯ͠ ϋʔυΣΞඇґଘͷ GPUͷૢ࡞ ಛఆͷGPU͚ͷ ίϚϯυ -
ϏσΦϝϞϦ Χʔωϧۭؒ Ϣʔβۭؒ Linux Mesa Ϩϯμʔϊʔυ libdrm ΞϓϦέʔγϣϯ Ϣʔβۭؒ αʔό
xcb ڞ༗ϝϞϦ Mesa libdrm ϓϥΠϚϦϊʔυ ϋʔυΣΞඇґଘͷ GPUͷૢ࡞ ಛఆͷGPU͚ͷ ίϚϯυ ΞϓϦέʔγϣϯ͕ VulkanOpenGLΛ Δ߹ -
Ϣʔβۭؒ Mesa ΞϓϦέʔγϣϯ ϋʔυΣΞඇґଘͷ GPUͷૢ࡞ ಛఆͷGPU͚ͷ ίϚϯυ ϏσΦϝϞϦ Χʔωϧۭؒ Linux
Ϩϯμʔϊʔυ ϓϥΠϚϦϊʔυ libdrm ඳ͍ͯ ͠ը໘ʹද͕ࣔͨ͠Δϓϩηε͕ ϗετʹ1͔ͭ͠ͳ͍ͳΒ -
ϏσΦϝϞϦ Χʔωϧۭؒ Ϣʔβۭؒ Linux Mesa Ϩϯμʔϊʔυ ΞϓϦέʔγϣϯ ϓϥΠϚϦϊʔυ ͠ը໘ʹද͕ࣔͨ͠Δϓϩηε͕ ϗετʹ1͔ͭ͠ͳ͍ͳΒ
Q. ͜Ε͕Ͱ͖ΔഺͰ libdrm ͜ΕΛදࣔͯ͠ ϋʔυΣΞඇґଘͷ GPUͷૢ࡞ ಛఆͷGPU͚ͷ ίϚϯυ -
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 ͜Ε -
#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) ) // Τϯδϯͷ໊લ -
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 ) ); -
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ͱσΟεϓϨΠͷใΛऔಘ -
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; } } } ར༻ՄೳͳදࣔϞʔυΛऔಘ -
$ ./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] -
// Πϯελϯε͕αϙʔτ͢Δ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 ) -
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 -
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; } } -
// αʔϑΣεͷใΛऔಘ 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; } } αʔϑΣε্Ͱར༻ՄೳͳΠϝʔδͷΛऔಘ -
ϏσΦϝϞϦ Χʔωϧۭؒ Ϣʔβۭؒ Linux Mesa ΞϓϦέʔγϣϯ දࣔ ϋʔυΣΞඇґଘͷ GPUͷૢ࡞ ಛఆͷGPU͚ͷ
ίϚϯυ ඳը ϓϥΠϚϦϊʔυ Γସ͑ දࣔத libdrm ඳ͖͔͚͕ දࣔ͞Ε͍͚ͯͳ͍ αʔϑΣε 2ը໘Ҏ্ͷαΠζͰ ֬อ͞ΕΔ ඳ͖ऴΘͬͨΒ දࣔ͢ΔΠϝʔδΛ Γସ͑Δ -
// σόΠεʹඋΘ͍ͬͯΔΩϡʔΛऔಘ 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ͰίϚϯυ͕ ࣮ߦ͞ΕΔ -
// σόΠεʹඋΘ͍ͬͯΔΩϡʔΛऔಘ 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ͷઃఆ ཧσόΠε + + = -
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 ); -
// εϫοϓνΣʔϯΛ࡞Δ 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 αʔϑΣεͷͲͷΠϝʔδ͕දࣔத͔Λอ࣋͢Δ දࣔதͩͬͨΠϝʔδ͕දࣔதͰͳ͘ͳͬͨΒڭ͑ͯ͘ΕΔ εϫοϓνΣΠϯ -
// εϫοϓνΣʔϯͷΠϝʔδΛऔಘ͢Δ 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 ) εϫοϓνΣΠϯ͔ΒશͯͷΠϝʔδΛऔಘ͠ -
// εϫοϓνΣΠϯͷΠϝʔδΛ੨ͰృΓͭͿ͢ 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 ) } ); ੨ͰృΓͭͿ͢ίϚϯυΛ༻ҙ -
.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(); } ը໘ʹදࣔΛ࢝ΊΔલʹ αʔϑΣεΛ੨ͰృΓͭͿ͓ͯ͘͠ ίϚϯυΛΩϡʔʹྲྀͯྃ͠Λͭ -
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() εϫοϓνΣΠϯ͔Β ඳ͍ͯେৎͳαʔϑΣεͷΠϝʔδͷΠϯσοΫεΛ͍ -
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ϑϨʔϜͷද͕ࣔ࣌ؒܦա͢Δ·Ͱͭ ༰Λॻ͖͑ͳ͍Ͱ͙͢ʹදࣔʹ·Θ͢ -
// ͜ͷηϑΥʹ௨͕དྷΔ·Ͱ͔ͬͯΒ .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 ); } } ͜ΕΛσΟεϓϨΠͷϦϑϨογϡϨʔτʹಉظͯ͠܁Γฦ͢ -
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) -
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) -
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ݸΛ͑Δ߹ෳͷίϚϯυʹ͚Δ ํܗ -
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) ΛඞཁͳαΠζʹͯ͠ εΫϦʔϯ࠲ඪܥʹมͯ͠ϥελϥΠβ -
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͔ΒಡΉߏମʹใΛੵΜͰ͓͘ -
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 ); } ཚͰܾΊͨҐஔʹཚͰܾΊͨ৭ͷ Λඳ͘ ํܗ -
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ݸ -
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 -
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ʹηοτ͞ΕͨΠϝʔδΛ ςΫενϟαϯϓϥʔʹ݁ͼ͚ͭΔ -
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ͷҾʹΠϝʔδΛ͢ -
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ͷΟϯυΛ࡞ͬͯ -
{ 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() ) } ); ͏ը૾Ληοτͯ͠ -
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 } }, Ϙλϯͱ͔ฒͯ -
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ͷඳը݁ՌΛ ೖྗͱͯͤ͠Δ -
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ͳΞϓϦέʔγϣϯʹΈࠐΊͳ͍ -
Φϑηοτςʔϒϧ ςʔϒϧ ςʔϒϧ ςʔϒϧ ⋮ 55'ͷόʔδϣϯ ςʔϒϧ ςʔϒϧͷछྨ νΣοΫαϜ ςʔϒϧͷઌ಄ͷҐஔ
ςʔϒϧͷ͞ ςʔϒϧͷछྨ νΣοΫαϜ ςʔϒϧͷઌ಄ͷҐஔ ςʔϒϧͷ͞ ⋮ TrueTypeͷߏ -
TrueTypeͷߏ ςʔϒϧ໊ ༰ head name OS/2 maxp post ϝλσʔλ glyf
άϦϑΛߏ͢Δۂઢͷύϥϝʔλ loca Կ൪ͷάϦϑ͕glyfͷ Կཁૉ͔Β࢝·Δ͔ cmap locaͷԿ൪ͷάϦϑ͕ จࣈίʔυͰݴ͏ԿͷจࣈʹରԠ͢Δ͔ -
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ͷҟࣈମηϨΫλͰબΕΔจࣈͱάϦϑͷରԠΛ࣋ͭ -
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ΛؚΜͰ͍Δ -
Unicodeͷۭؒ جຊଟݴޠ໘ #.1 Ճଟݴޠ໘ 4.1 Ճࣈ໘ 4*1 ୈࡾࣈ໘ 5*1 ະׂΓͯ
Ճಛघ༻్໘ 441 ࢲ༻໘ 0x000000 0x010000 0x020000 0x030000 0x040000 0x0F0000 0x110000 0x0E0000 16bitͰදݱͰ͖Δൣғ ຊޠϑΥϯτͷจࣈ͕ ͏ൣғ ttf2mesh Ճࣈ໘ͷࣈͷάϦϑͷ ίʔυϙΠϯτΛऔΕͳ͍ -
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ʹஔ͖͑ͯ -
@@ -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͕͋ͬͨΒ ༏ઌతʹಡΉίʔυΛՃ -
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จࣈ -
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ͰϑΥϯτΛಡΉ -
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( άϦϑͷใ͕ਖ਼͘͠औΕ͍ͯͯ ཉ͍͠ൣғͷจࣈͷάϦϑͩͬͨΒ -
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 ) ); ࡾ֯ܗׂ ྻʹಉ͡࠲ඪͷ͕طʹ͋ͬͨΒͦͷΠϯσοΫεΛฦ͢ ແ͔ͬͨΒྻʹΛՃͯͦ͠ͷΠϯσοΫεΛฦ͢ -
); 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ͷάϦϑͰ͢ -
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ΤϯίʔμͰ࡞ΕΔ -
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 ] ); ֤άϦϑͷઌ಄Λࢦ͢ όοϑΝϏϡʔΛ࡞Δ ࠷ޙʹྻͷ όοϑΝϏϡʔΛ࡞Δ -
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 })} }) ); ֤όοϑΝϏϡʔΛࢦ͢ ΞΫηαΛ࡞Δ ྻΛࢦ͢ΞΫηαΛ࡞Δ -
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ʹ͓ͯ͘͠ -
{ 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Λ ϑΝΠϧʹు͘ -
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ʹૹΔ -
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 -
CPU͔ΒGPU͔Βݟ͑ΔϝϞϦ GPU CPU ํܗ × 500 Πϯελϯγϯά 1ίϚϯυͰಉ͡τϙϩδͷਤܗΛେྔʹඳ͘ ํܗ0ͷஔ ํܗ1ͷஔ
ํܗ2ͷஔ ํܗ3ͷஔ ⋮ ݸʑͷจࣈτϙϩδ͕ҟͳΔҝ ΠϯελϯγϯάͰ·ͱΊͯඳ͚ͳ͍ -
CPU͔ΒGPU͔Βݟ͑ΔϝϞϦ GPU CPU Indirect Draw 1ίϚϯυͰҟͳΔτϙϩδͷਤܗΛେྔʹඳ͘ άϦϑ0ͷஔ άϦϑ1ͷஔ άϦϑ2ͷஔ άϦϑ3ͷஔ
⋮ άϦϑ0ͷͷಡΈํ άϦϑ1ͷͷಡΈํ άϦϑ2ͷͷಡΈํ άϦϑ3ͷͷಡΈํ ⋮ ͜͜ʹྻڍͨͭ͠ඳ͍ͯ -
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ʹૹͬͨσʔλͷதʹ͋Δ͔Λௐͯ -
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; } } άϦϑͷ৭ͱஔ άϦϑͷΛͲͷҐஔ͔ΒಡΉ͔ -
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 ) ); } ϑΥϯτͷྻΛ͏ ϑΥϯτͷΠϯσοΫεΛ͏ ͜͜ʹஔ͍ͯ͋ΔͷಡΈํʹैͬͯ ͜ͷͷάϦϑΛඳը -
όΠτྻ ୯७ʹฒΔͱ ਖ਼͍͠දࣔ H e l l o 48 65
6c 6c 6f Hello Hello ӳޠ จࣈαΠζͮͭӈʹਐΉ -
όΠτྻ ୯७ʹฒΔͱ ਖ਼͍͠දࣔ ͜ Μ ʹ ͪ e3 81
93 e3 82 93 e3 81 ab e3 81 a1 e3 81 af ͜Μʹͪ ͜Μʹͪ ຊޠ จࣈαΠζͮͭӈʹਐΉ -
όΠτྻ ୯७ʹฒΔͱ ਖ਼͍͠දࣔ ส ว ั ส ด ี e0
b8 aa e0 b8 a7 e0 b8 b1 e0 b8 aa e0 b8 94 e0 b8 b5 ส วั ส ดี ส วั ส ดี ্ʹ͔ͬͬͯΔ ด ส ว ส ั ี λΠޠ ෳͷίʔυϙΠϯτͷฒͼͰάϦϑΛඳ͖͘Ґஔ͕มΘΔ -
όΠτྻ ୯७ʹฒΔͱ ਖ਼͍͠දࣔ न म स ् त े e0
a4 a8 e0 a4 ae e0 a4 b8 e0 a5 8d e0 a4 a4 e0 a5 87 नम स्त े नमस ् त े नम स्ते ࣍ͷίʔυϙΠϯτͱ߹ମͯ͠ܗ͕มΘ͍ͬͯΔ ώϯσΟʔޠ ෳͷίʔυϙΠϯτͷฒͼͰඳ͖͘άϦϑ͕มΘΔ -
όΠτྻ ୯७ʹฒΔͱ ਖ਼͍͠දࣔ ש ל ו ם d7 a9 d7
9c d7 95 d7 9d םולש שלום םולש ӈ͔Βࠨ ϔϒϥΠޠ 1จࣈඳ͍ͨΒࠨʹਐΉ -
όΠτྻ ୯७ʹฒΔͱ ਖ਼͍͠දࣔ م ر ح ب ً ط d9
85 d8 b1 d8 ad d8 a8 d9 8b d8 a7 ﺎًﺑﺣرﻣ مرحبً ط ﺎًﺑﺣرﻣ ӈ͔Βࠨ͔ͭ࣍ͷίʔυϙΠϯτͱ߹ମͯ͠ผͷάϦϑʹมԽ ΞϥϏΞޠ -
ส วั ส ดี HarfBuzz͕ͬͯ͘ΕΔࣄ ࣍ͷจࣈΛ ඳ͖͘ҐஔΛٻΊΔ नम स्ते स
ͲͷάϦϑͰ ඳ͖͔͘ΛٻΊΔ ࠓ͙͢μϯϩʔ υ HarfBuzz͕ͬͯ͘Εͳ͍ࣄ ͍จࣈྻͷંΓฦ͠ םולש שלום จࣈྻͷਐߦํͷஅ ? ? จࣈͷϨϯμϦϯά םולש ৗʹࠨ͔Β ॲཧͰ͖ΔΑ͏ʹฒସ͑ -
ส วั ส ดี HarfBuzz͕ͬͯ͘ΕΔࣄ ࣍ͷจࣈΛ ඳ͖͘ҐஔΛٻΊΔ नम स्ते स
ͲͷάϦϑͰ ඳ͖͔͘ΛٻΊΔ ࠓ͙͢μϯϩʔ υ HarfBuzz͕ͬͯ͘Εͳ͍ࣄ ͍จࣈྻͷંΓฦ͠ םולש שלום จࣈྻͷਐߦํͷஅ ? ? จࣈͷϨϯμϦϯά םולש ৗʹࠨ͔Β ॲཧͰ͖ΔΑ͏ʹฒସ͑ ࠨ͔Βӈʹॻ͘ݴޠͳͷ͔ ӈ͔Βࠨʹॻ͘ݴޠͳͷ͔ ΞϓϦέʔγϣϯ͕அ HarfBuzzࢦఆ͞Ε͖ͨʹจࣈΛฒΔॲཧ͚ͩΛ͢Δ -
U_CAPI UBiDiDirection U_EXPORT2 ubidi_getBaseDirection(const UChar *text, int32_t length ); unicode/ubidi.h
͜ͷจࣈྻͬͯͲ͖ͬͪ? UBiDiDirection::UBIDI_LTR ࠨ͔ΒӈͷςΩετͰ࢝·Δ UBiDiDirection::UBIDI_RTL ӈ͔ΒࠨͷςΩετͰ࢝·Δ UBiDiDirection::NEUTRAL จࣈྻۭΜ… ฦΓ -
An apple is called חופת in Hebrew. .תילגנאב תארקנ חופת
apple ࠨ͔ΒӈͷςΩετͷதʹӈ͔ΒࠨͷςΩετ͕͍ࠞͬͯ͟Δ ӈ͔ΒࠨͷςΩετͷதʹࠨ͔ΒӈͷςΩετ͕͍ࠞͬͯ͟Δ ӈ͔Βࠨʹॻ͘ݴޠͰ௨ৗࣈࠨ͔Βӈʹॻ͘ͷͰ ӈ͔ΒࠨͷςΩετʹৗతʹࠨ͔ΒӈͷςΩετ͕ࠞ͟Δ -
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Ͱ ਐߦํ͕ಉ͡ൣғͰจࣈྻΛ۠Δ ൣғͷΛऔಘ ͜ͷൣғͲ͖ͬͪ? -
ส วั ส ดี HarfBuzz͕ͬͯ͘ΕΔࣄ ࣍ͷจࣈΛ ඳ͖͘ҐஔΛٻΊΔ नम स्ते स
ͲͷάϦϑͰ ඳ͖͔͘ΛٻΊΔ ࠓ͙͢μϯϩʔ υ HarfBuzz͕ͬͯ͘Εͳ͍ࣄ ͍จࣈྻͷંΓฦ͠ םולש שלום จࣈྻͷਐߦํͷஅ ? ? จࣈͷϨϯμϦϯά םולש ৗʹࠨ͔Β ॲཧͰ͖ΔΑ͏ʹฒସ͑ ͍ςΩετંΓฦͯ͠දࣔ͠ͳ͚ΕͳΒͳ͍ UnicodeจࣈྻʹંΓฦ͍͚ͯ͠ͳ͍Օॴ͕͋Δ -
English string should not break in the middle of a
word. ຊޠͷจࣈྻͰࣺͯԾ໊ߦ಄ʹ࣋ͬ ͯ͘Δ͖Ͱͳ͍ ͜͜ͳΒંΓฦͯ͠Α͍ ͜͜ͰંΓฦ͍͚ͯ͠ͳ͍ ͜͜ͳΒંΓฦͯ͠Α͍ ͜͜ͰંΓฦ͍͚ͯ͠ͳ͍ ส วั ส ดี 1จࣈͱݟ၏͞ΕΔγʔέϯεͷ్தͰંΓฦ͖͢Ͱͳ͍ -
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จࣈྻΛ ʮվߦΛڬΜͰ͍͚ͳ͍ʯ୯ҐͰ۠Δࣄ͕Ͱ͖Δ -
ICU ubidi ICU ubrk HarfBuzz จࣈྻ LTRจࣈྻ RTLจࣈྻ LTRจࣈྻ LTRจࣈྻ
RTLจࣈྻ LTRจࣈྻ ୯ޠ ୯ޠ ୯ޠ ୯ޠ ୯ޠ ୯ޠ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ͜͜ͰંΓฦͯ͠େৎϚʔΫ -
ICU ubrk HarfBuzz LTRจࣈྻ RTLจࣈྻ LTRจࣈྻ ୯ޠ ୯ޠ ୯ޠ ୯ޠ
୯ޠ ୯ޠ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ࣈ ߦͷંΓฦ͠ ը໘্Ͱͷจࣈͷஔ -
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 ΧʔιϧͷҐஔ͔Β ͲΕ͚ͩͣΕͨॴʹ จࣈΛඳ͖͔͘ จࣈΛඳ͍ͨޙͰ ΧʔιϧΛͲΕ͚ͩ ਐΊΔ͖͔ -
1pt = 1 72 in ≃ 0.3528mm 1pt = 0.3514mm
ݩʑͷఆٛ JISن֨Ͱͷఆٛ จࣈͷେ͖͞ͷ୯Ґ ϙΠϯτ(pt) ͲͪΒʹͤΑݴ͑Δ͜ͱ ը໘ͷཧతͳେ͖͞ͷใ͕ඞཁ -
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୯Ґͷ ը໘ͷେؚ͖͕͞·Ε͍ͯΔ σΟεϓϨΠࣗͷཧతͳେ͖͞Λ͍ͬͯΔ ϓϩδΣΫλαΠζΛఆٛͰ͖ͳ͍ػثͰ ͍ͬͯͳ͍ࣄ͋Δ -
typedef struct VkDisplayPropertiesKHR { VkDisplayKHR display; const char* displayName; VkExtent2D
physicalDimensions; VkExtent2D physicalResolution; VkSurfaceTransformFlagsKHR supportedTransforms; VkBool32 planeReorderPossible; VkBool32 persistentContent; } VkDisplayPropertiesKHR; VulkanͰσΟεϓϨΠͷཧతͳେ͖͞ΛऔΕΔ (mm୯Ґ) औΕͳ͍࣌ͱΓ͋͑ͣ96dpiͩͱࢥ͏ Windows compatible͙͠͞Λ͓ͯ͜͠͏ -
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ίʔυͷׅހͰ ࠨ͔Βӈʹॻ͘ςΩετʹ͓͍ͯӈΛ͍͍ͯΔ͕ ӈ͔Βࠨʹॻ͘ςΩετͰ͋Δͱ͍͏ࢦ͕ࣔͳ͞Ε͍ͯΔҝ HarfBuzzUAX #9 §3.4 L4ʹैͬͯάϦϑΛ)ʹஔ͖͑Δ -
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ഒʹ͢ΔͱಡΊΔ -
Ϣʔβۭؒ Ϣʔβۭؒ Χʔωϧۭؒ Linux evdev libinput Ϛε͕ಈ͖·ͨ͠ ΞϓϦέʔγϣϯ xcb αʔό
͓લɺΫϦοΫ͞ΕͯΔͧ Xαʔό࠷ԼͷϨΠϠʔͰͳ͍ HIDυϥΠό ϚεͷࠨϘλϯ͕ԡ͞Ε·ͨ͠ -
Ϣʔβۭؒ Χʔωϧۭؒ Linux evdev libinput ΞϓϦέʔγϣϯ ͜͏͢Ε Xαʔό͕ແͯ͘ ೖྗΠϕϯτΛऔΕΔ HIDυϥΠό
Ϛε͕ಈ͖·ͨ͠ ϚεͷࠨϘλϯ͕ԡ͞Ε·ͨ͠ -
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ͷίϯςΩετΛ࡞Δ γʔτΛ࡞Δ Πϕϯτ͋ͬͨΒ͘Ε -
ԿΒ͔ͷೖྗΠϕϯτ͕ى͜Δͱ ͜ͷ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 { -
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; } } } } ); Ϛε͕ಈ͍ͨΠϕϯτΛरͬͯ -
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 ); Χʔιϧ͕ಈ͍͍ͯͨΒ ৽͍͠ҐஔʹΧʔιϧΛ࠶ඳը -
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; } } } ); ΩʔϘʔυͷΠϕϯτΛरͬͯ தΛදࣔ -
$ ./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 ͜ͷ൪߸Կ? -
#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ͷೖྗΠϕϯτͷ൪߸ -
#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͕ԡ͞Εͨͱ͍͏ҙຯ ͱݶΒͳ͍ -
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͕͋ΔҐஔͷ Ωʔ͕ԡ͞Εͨͱ͍͏ҙຯ -
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_AQ͕ԡ͞Εͨͱ͍͏ҙຯ -
xkbcommon X Window System͔Β X Keyboard Extension(xkb)ͷ ෦͚ͩΛΓग़ͨ͠ͷ /usr/share/X11/xkb/ ͋ͨΓʹస͕͍ͬͯΔ
มදΛͬͯ ͲͷҐஔͷΩʔ͕ԡ͞Ε͔ͨΛ ԿͷจࣈͷΩʔ͕ೖྗ͞Ε͔ͨʹ ม͢Δ https://xkbcommon.org/ -
$ 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 ΩʔϘʔυϨΠΞτͷใ ͜ΕΞϓϦέʔγϣϯ͔ΒͲ͏ͬͯऔΔͷ? -
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ʹܨ͍Ͱ ϓϩύςΟΛर͏ͱऔΕΔ -
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ͷίϯςΩετΛ࡞Δ ΩʔϘʔυϨΠΞτͷ໊લ͔Β ΩʔϚοϓΛݟ͚ͭΔ ΩʔϘʔυͷ ঢ়ଶϚγϯΛ࡞Δ -
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ʹ͢ -
thread_pool->add_co( [xkb_state=xkb_state,¤t_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͔Β ೖྗ͞ΕͨจࣈΛΒ͏ -
$ ./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 ʮԿͷจࣈ͕ೖྗ͞Ε͔ͨʯ ͕औΕΔ -
Ϣʔβۭؒ Ϣʔβۭؒ Χʔωϧۭؒ Linux evdev libinput ΞϓϦέʔγϣϯ xcb αʔό IMMODULE͕ΩʔϘʔυΠϕϯτΛ
ೖྗϝιουʹ͑Δ HIDυϥΠό QtGTK+ IMMODULE Ϣʔβۭؒ ೖྗϝιου มΤϯδϯ -
IMMODULE UIM SCIM IBus fcitx ϦϯΫ socket dbus dbus Anthy
Mozc SKK libhangul Chewing -
ΞϓϦέʔγϣϯ UIM SCIM IBus fcitx dbus Anthy Mozc SKK libhangul
Chewing dbusͰ fcitxͱձ͠Α͏ -
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ʹܨ͙ -
); } 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ͰίϯςΩετΛ࡞Δͱ ίϯςΩετͷύε͕͑Δ ίϯςΩετͷύεʹܨ͙ -
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ʹૹΔ -
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γάφϧͰ มΤϯδϯͷ໊લ͕ඈΜͰ͘Δ -
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γάφϧͰ ֬ఆͨ͠༰͕ඈΜͰ͘Δ -
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γάφϧͰ ඈΜͰ͘Δ -
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γάφϧ͕ඈΜͰ͘Δ -
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γάφϧ͕ඈΜͰ͘Δ -
ॎ ॻ ͖ ɹ ຊ ޠ ͷ ͍
͘ ͭ ͔ ͷ ݴ ޠ Ͱ ্ ͔ Β Լ ʹ ς Ω ε τ Λ ਐ Ί Δ ॻ ͖ ํ ͕ ఆ ٛ ͞ Ε ͯ ͍ Δ ɹ HarfBuzz ॎ ॻ ͖ Λ α ϙ τ ͠ ͯ ͍ Δ ͷ Ͱ ຊ ޠ ͷ จ ࣈ Λ ॎ ॻ ͖ Ͱ ฒ Δ ࣄ ࣗ ମ ͠ ͘ ͳ ͍ ɹ ͠ ͔ ͠ ɹ ॎ ॻ ͖ ͷ ς Ω ε τ ͷ த ʹ English ͷ Α ͏ ͳ ॎ ॻ ͖ Ͱ ͖ ͳ ͍ ݴ ޠ ͕ ࠞ ͟ ͯ ͖ ͨ Β ά Ϧ ϑ Λ ճ స ͞ ͤ ͳ ͚ Ε ͳ Β ͳ ͍ ɹ ॎ ॻ ͖ ͷ ς Ω ε τ ͷ த ʹ תי ִ רְבִע ʿ Īvrīt ͷ Α ͏ ͳ ӈ ͔ Β ࠨ ʹ ਐ Ή ݴ ޠ ͕ ࠞ ͟ ͯ ͖ ͨ Β Ͳ ͏ ͠ Α ͏