Git Product home page Git Product logo

wgpu_glyph's Introduction

wgpu_glyph

Warning

This crate has been superseded by glyphon.

glyphon has a better design that fits better with wgpu. Furthermore, it is built on top of cosmic-text, which supports many more advanced text use cases.

Test Status crates.io Documentation License

A fast text renderer for wgpu, powered by glyph_brush.

Examples

Have a look at

wgpu_glyph's People

Contributors

antonok-edm avatar bobo1239 avatar calcoph avatar cupnfish avatar davidster avatar dispersia avatar expenses avatar fintelia avatar foltik avatar genusistimelord avatar hecrj avatar heroesgrave avatar lukaskalbertodt avatar nico-abram avatar paulkernfeld avatar polymeilex avatar rukai avatar sconybeare avatar sotrh avatar terrarier2111 avatar wohe avatar wyatt-herkamp avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

wgpu_glyph's Issues

Add examples for text-selection, underlines, etc.

I'm working on using wgpu_glyph in the context of a code editor, in particular the ability to show that text has been selected. This is proving more difficult than I anticipated, but seems like a common use case, so once I figure it out I intend to make a PR to add examples for how to go about this. If anyone has already figured this out, I would love to hear how they went about it.

Crash when resizing window with text positioned relatively

wgpu_glyph causes my application to crash with exit code 0xc000041d on Windows 10, with no error or panic message, seemingly randomly while resizing the window. You can see an adaptation of the hello example that reproduces the problem, at least for me, here.

Support for depth testing?

In certain cases, it might be nice to be able to render text that is behind objects that have already been rendered, i.e. by using depth testing.

I'm not 100% clear on how wgpu_glyph's rendering pipeline should interact with the user's rendering pipeline, but I think it might be possible to have the user pass in a DepthStencilStateDescriptor and a RenderPassDepthStencilAttachmentDescriptor.

Monospaced / fixed width font spacing

It appears that the choice of anchor point for text positioning depends on the actual width of the text in pixels rather than the width of the glyphs. In general this is hard to notice, but it definitely causes issues with monospaced/fixed width fonts that are dynamically updated. For instance, consider a simple counter app, where the text of the current count is positioned with the following Layout:

wgpu_glyph::Layout::default_single_line()
    .h_align(wgpu_glyph::HorizontalAlign::Right)
    .v_align(wgpu_glyph::VerticalAlign::Center)

When the rightmost character of the text updates from a wide character (e.g. 0) to a narrow character (e.g. 1) or vice versa, there is a noticeable horizontal shift in the rest of the characters on the left.

Background color

Do you have any started work on background color support ?
It can be great to have in on Text and Section.

Do you have any advice to start to implement it ?

Thanks,
Marc-Antoine

Comparison with glyphon crate.

Given that the main user of the crate (iced) seems to have moved from wgpu_glyph to glyphon (as per this pr). It may be helpful to people browsing crates to have a comparison between the two?

update to latest api

There's been quite a few changes since 0.2 crates and master wgpu-rs; are there any plans to upgrade this to the api that's on master any time soonish?

Multi line text can cause validation error if scale factor != 1

EDIT: Looking at the backtrace more carefully, I actually realize that this is probably not a problem in wgpu_glyph but a problem in iced which is the same as reported by the two issue mentioned below, since I think it is in iced code that the Scissor_rect region is set. Sorry for the duplicate.

When a text is displayed on several line, a validation error similar to iced-rs/iced#816 and iced-rs/iced#775 happens.

I believe a fix similar to iced-rs/iced#818 could work

Here is a validation error + backtrace

wgpu error: Validation Error

Caused by:
    In a RenderPass
      note: encoder = `<CommandBuffer-(0, 245, Vulkan)>`
    In a set_scissor_rect command
    Invalid ScissorRect parameters


thread 'main' panicked at 'Handling wgpu errors as fatal by default', /home/nlevy/.cargo/registry/src/github.com-1ecc6299db9ec823/wgpu-0.7.0/src/backend/direct.rs:1896:5
stack backtrace:
   0: std::panicking::begin_panic
   1: wgpu::backend::direct::default_error_handler
   2: core::ops::function::Fn::call
   3: <wgpu::backend::direct::Context as wgpu::Context>::command_encoder_end_render_pass
   4: wgpu_glyph::pipeline::Pipeline<()>::draw
   5: iced_wgpu::backend::Backend::flush
   6: iced_wgpu::backend::Backend::draw
   7: ensnano::gui::Gui::render
   8: ensnano::main::{{closure}}
   9: winit::platform_impl::platform::x11::EventLoop<T>::run
  10: winit::platform_impl::platform::EventLoop<T>::run
  11: winit::event_loop::EventLoop<T>::run
  12: ensnano::main

I'll try to make a minimal example to reproduce the bug tomorrow

No support for resolve_target?

Although you can provide a multisample_state for your Font, I don't see any use of this when you are unable to pass a resolve_target to the wgpu::RenderPass created by wgpu_glyph. The resolve_target is needed to apply the changes from the multisample view to the actual texture, as just parsing the multisample view alone does practically nothing.

Slight inconsistency between generated images

While setting up a test suite to compare images rendered and captured using wgpu_glyph, I discovdred a very slight difference between the image generated on one run vs. another. The pink areas in the attachment are where the pixels don't match exactly.

As far as I can tell, my code has nothing non-deterministic going on that could cause this behavior, so I wanted to check in here just to see if there are any potential obvious reasons why wgpu_glyph might produce a different result from one run to the next.

The difference is unnoticeable to the human eye, I only spotted it because of my testing work, which can easily be modified to use a more lenient comparison algorithm. Just wanted to deepen my understanding about whether the issue might be on my end or not.

image

Latest release fails with validation error using wgpu 0.11.1

After upgrading to wgpu 0.11.1, I'm getting the following error:

thread 'main' panicked at 'wgpu error: Validation Error

Caused by:
    In Device::create_render_pipeline
    error matching FRAGMENT shader requirements against the pipeline
    unable to filter the texture (ResourceBinding { group: 0, binding: 2 }) by the sampler (ResourceBinding { group: 0, binding: 1 })
    non-filterable float texture

', /home/hanno/.cargo/registry/src/github.com-1ecc6299db9ec823/wgpu-0.11.1/src/backend/direct.rs:2195:5
stack backtrace:
   0: rust_begin_unwind
             at /rustc/59eed8a2aac0230a8b53e89d4e99d55912ba6b35/library/std/src/panicking.rs:517:5
   1: std::panicking::begin_panic_fmt
             at /rustc/59eed8a2aac0230a8b53e89d4e99d55912ba6b35/library/std/src/panicking.rs:460:5
   2: wgpu::backend::direct::default_error_handler
             at /home/hanno/.cargo/registry/src/github.com-1ecc6299db9ec823/wgpu-0.11.1/src/backend/direct.rs:2195:5
   3: core::ops::function::Fn::call
             at /rustc/59eed8a2aac0230a8b53e89d4e99d55912ba6b35/library/core/src/ops/function.rs:70:5
   4: <alloc::boxed::Box<F,A> as core::ops::function::Fn<Args>>::call
             at /rustc/59eed8a2aac0230a8b53e89d4e99d55912ba6b35/library/alloc/src/boxed.rs:1650:9
   5: wgpu::backend::direct::ErrorSinkRaw::handle_error
             at /home/hanno/.cargo/registry/src/github.com-1ecc6299db9ec823/wgpu-0.11.1/src/backend/direct.rs:2183:9
   6: wgpu::backend::direct::Context::handle_error
             at /home/hanno/.cargo/registry/src/github.com-1ecc6299db9ec823/wgpu-0.11.1/src/backend/direct.rs:184:9
   7: <wgpu::backend::direct::Context as wgpu::Context>::device_create_render_pipeline
             at /home/hanno/.cargo/registry/src/github.com-1ecc6299db9ec823/wgpu-0.11.1/src/backend/direct.rs:1276:13
   8: wgpu::Device::create_render_pipeline
             at /home/hanno/.cargo/registry/src/github.com-1ecc6299db9ec823/wgpu-0.11.1/src/lib.rs:1770:17
   9: wgpu_glyph::pipeline::build
             at /home/hanno/.cargo/registry/src/github.com-1ecc6299db9ec823/wgpu_glyph-0.15.1/src/pipeline.rs:290:15
  10: wgpu_glyph::pipeline::Pipeline<()>::new
             at /home/hanno/.cargo/registry/src/github.com-1ecc6299db9ec823/wgpu_glyph-0.15.1/src/pipeline.rs:34:9
  11: wgpu_glyph::GlyphBrush<(),F,H>::new
             at /home/hanno/.cargo/registry/src/github.com-1ecc6299db9ec823/wgpu_glyph-0.15.1/src/lib.rs:221:23
  12: wgpu_glyph::builder::GlyphBrushBuilder<(),F,H>::build
             at /home/hanno/.cargo/registry/src/github.com-1ecc6299db9ec823/wgpu_glyph-0.15.1/src/builder.rs:113:9
  13: fj_host::graphics::config_ui::ConfigUi::new
             at ./src/graphics/config_ui.rs:22:13
  14: fj_host::graphics::renderer::Renderer::new::{{closure}}
             at ./src/graphics/renderer.rs:121:25
  15: <core::future::from_generator::GenFuture<T> as core::future::future::Future>::poll
             at /rustc/59eed8a2aac0230a8b53e89d4e99d55912ba6b35/library/core/src/future/mod.rs:80:19
  16: futures_executor::local_pool::block_on::{{closure}}
             at /home/hanno/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-executor-0.3.18/src/local_pool.rs:315:23
  17: futures_executor::local_pool::run_executor::{{closure}}
             at /home/hanno/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-executor-0.3.18/src/local_pool.rs:90:37
  18: std::thread::local::LocalKey<T>::try_with
             at /rustc/59eed8a2aac0230a8b53e89d4e99d55912ba6b35/library/std/src/thread/local.rs:399:16
  19: std::thread::local::LocalKey<T>::with
             at /rustc/59eed8a2aac0230a8b53e89d4e99d55912ba6b35/library/std/src/thread/local.rs:375:9
  20: futures_executor::local_pool::run_executor
             at /home/hanno/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-executor-0.3.18/src/local_pool.rs:86:5
  21: futures_executor::local_pool::block_on
             at /home/hanno/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-executor-0.3.18/src/local_pool.rs:315:5
  22: fj_host::main
             at ./src/main.rs:177:24
  23: core::ops::function::FnOnce::call_once
             at /rustc/59eed8a2aac0230a8b53e89d4e99d55912ba6b35/library/core/src/ops/function.rs:227:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

The error seems to originate from this call:
https://github.com/hecrj/wgpu_glyph/blob/0.15.1/src/pipeline.rs#L290-L335

And this is the relevant bit of configuration, I believe:
https://github.com/hecrj/wgpu_glyph/blob/0.15.1/src/pipeline.rs#L236-L256

Based on some digging I did in the wgpu validation code, I think the problem is the mismatch between filtering: true and filterable: false.

This problem has been fixed on master (thank you, @Bobo1239!), but that fix hasn't made it into a release yet. I figured it's worth opening this issue despite the fix existing, to provide information to those searching the internet for that error message, and to document that the latest releases of wgpu_glyph and wgpu don't work with one another.

Does wgpu::Device need to be borrowed mutably ?

From my own attempts at working with wgpu, it seems like the wgpu::Device only needs to get mutably borrowed for submitting CommandBuffers into the queue.

Indeed, I can run the following line in wgpu_glyph:

sed -i 's/&mut wgpu::Device/\&wgpu::Device/g' $(find src -iname '*.rs')

and the whole crate can still be built without any issue, and its example binary still runs perfectly fine.

Font weight problem

The font weight of text rendered with this library seems to be off in comparison to the font rendering used in browsers.

Safari:
Screenshot 2022-01-20 at 10 32 12

wgpu_glyph:
Screenshot 2022-01-20 at 10 33 48

If you look closely, there's a pixel difference between these twoโ€”easy to spot at the plus or equals sign. They both don't have any font weight modifications; it's the default font weight.

The same holds true for Firefox and Chrome.

I'm not a font expert, so I have no idea what method would need to be implemented, which one is used in browsers to achieve this, or whether browsers diverge from default OS font rendering in this case.

Nevertheless, the text seems to be just a little bit too thin in comparison. Help would be much appreciated here :-)

Edit: using macOS.

error: could not compile `wgpu`

   Compiling wgpu v0.7.1
error[E0658]: use of unstable library feature 'future_readiness_fns'
  --> /Users/tiantengfei/.cargo/registry/src/github.com-1ecc6299db9ec823/wgpu-0.7.1/src/backend/direct.rs:17:14
   |
17 |     future::{ready, Ready},
   |              ^^^^^
   |
   = note: see issue #70921 <https://github.com/rust-lang/rust/issues/70921> for more information

error[E0658]: use of unstable library feature 'future_readiness_fns'
  --> /Users/tiantengfei/.cargo/registry/src/github.com-1ecc6299db9ec823/wgpu-0.7.1/src/backend/direct.rs:17:21
   |
17 |     future::{ready, Ready},
   |                     ^^^^^
   |
   = note: see issue #70921 <https://github.com/rust-lang/rust/issues/70921> for more information

error[E0658]: use of unstable library feature 'future_readiness_fns'
   --> /Users/tiantengfei/.cargo/registry/src/github.com-1ecc6299db9ec823/wgpu-0.7.1/src/backend/direct.rs:657:33
    |
657 |     type RequestAdapterFuture = Ready<Option<Self::AdapterId>>;
    |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: see issue #70921 <https://github.com/rust-lang/rust/issues/70921> for more information

error[E0658]: use of unstable library feature 'future_readiness_fns'
   --> /Users/tiantengfei/.cargo/registry/src/github.com-1ecc6299db9ec823/wgpu-0.7.1/src/backend/direct.rs:659:9
    |
659 |         Ready<Result<(Self::DeviceId, Self::QueueId), crate::RequestDeviceError>>;
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: see issue #70921 <https://github.com/rust-lang/rust/issues/70921> for more information

error[E0658]: use of unstable library feature 'future_readiness_fns'
   --> /Users/tiantengfei/.cargo/registry/src/github.com-1ecc6299db9ec823/wgpu-0.7.1/src/backend/direct.rs:688:9
    |
688 |         ready(id.ok())
    |         ^^^^^
    |
    = note: see issue #70921 <https://github.com/rust-lang/rust/issues/70921> for more information

error[E0658]: use of unstable library feature 'future_readiness_fns'
   --> /Users/tiantengfei/.cargo/registry/src/github.com-1ecc6299db9ec823/wgpu-0.7.1/src/backend/direct.rs:712:9
    |
712 |         ready(Ok((device, device_id)))
    |         ^^^^^
    |
    = note: see issue #70921 <https://github.com/rust-lang/rust/issues/70921> for more information

error: aborting due to 6 previous errors

For more information about this error, try `rustc --explain E0658`.
error: could not compile `wgpu`.

To learn more, run the command again with --verbose.

Wrong Render Result for SourceHanSerif fonts

Hello I'm new to wgpu glyph, I have managed to run the hello example, but I got a bad result when I replace the font with SourceHanSerifCN-VF(.otf or .ttf)ใ€‚

the result:

ๅ›พ็‰‡

if this line is added(after build the section and before queue), the app will panic:

    let bound = glyph_brush.glyph_bounds(section.clone());
    // panic: 
    //thread 'main' panicked at 'Invalid glyph_hor_side_bearing', C:\Users\MY_USERNAME\.cargo\registry\src\github.com-1ecc6299db9ec823\ab_glyph-0.2.13\src\ttfp.rs:333:1

I don't known if it's an issue for wgpu_glyph, or ab_glyph, or other dependencies, so I create this issue. Any advice will be appreciated:rose:.

environment

Windows 10, intel 7700, Nvidia 1060, 32gb ram

how to reproduce:

SourceHanIssue.zip

font file is downloaded from adobe-fonts/source-han-serif

Broken text rendering on wasm32 target in Firefox

I have managed to compile hello example to wasm32 target with some changes to winit initialization, however, when trying to render new text on the next frame, part of the text disappear (e.g I just render text with content that changes each frame with format!("Frame {}!, counter)).

I have modified the example I'm my fork to help reproduce the issue:
https://github.com/pacmancoder/wgpu_glyph/tree/issue-report/wasm32-text-rendering

UPD: Reproduces only on Firefox (v93)

Steps to run example on wasm32

# 0. Install prerequisites
cargo install wasm-bindgen-cli https
# 1. Compile wasm module
cargo build --example hello --target=wasm32-unknown-unknown
# 2. Invoke wasm-bindgen
wasm-bindgen target/wasm32-unknown-unknown/debug/examples/hello.wasm --out-dir . --target web --no-typescript
# 3. run http server
http
# 4. Open 127.0.0.1:8000 in browser

Results can be compared with non-wasm32 build of example cargo run --example hello

Normal behavior; Compiled for non-wasm32 target

image

Text artifacts of wasm32 target, part of the text is missing

image

Not sure yet if it is related to wgpu or wgpu_glyph implementation.

vulkan validation errors + panic

After adding wgpu_glyph to my application everything works fine.
However once when running, I got these repeating validation errors:

ERROR gfx_backend_vulkan 
VALIDATION [VUID-VkBufferImageCopy-imageOffset-00197 (0)] : vkCmdCopyBufferToImage(): Both pRegion[0] imageoffset.x (264) and (imageExtent.width + imageOffset.x) (312) must be >= zero or <= image subresource width (256). The Vulkan spec states: imageOffset.x and (imageExtent.width + imageOffset.x) must both be greater than or equal to 0 and less than or equal to the image subresource width (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-VkBufferImageCopy-imageOffset-00197)
object info: (type: IMAGE, hndl: 22)

ERROR gfx_backend_vulkan 
VALIDATION [VUID-vkCmdCopyBufferToImage-pRegions-00172 (0)] : vkCmdCopyBufferToImage(): pRegion[0] exceeds image bounds.. The Vulkan spec states: The image region specified by each element of pRegions must be a region that is contained within dstImage (https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VUID-vkCmdCopyBufferToImage-pRegions-00172)
object info: (type: COMMAND_BUFFER, hndl: 0)

And then it panicked with:

thread '<unnamed>' panicked at 'assertion failed: `(left == right)`
  left: `Ok(false)`,
 right: `Ok(true)`: GPU got stuck on a frame (image 1) :(', /home/rubic/.cargo/git/checkouts/wgpu-53e70f8674b08dd4/dbef9f3/wgpu-native/src/swap_chain.rs:178:5
stack backtrace:
   0: backtrace::backtrace::libunwind::trace
             at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.29/src/backtrace/libunwind.rs:88
   1: backtrace::backtrace::trace_unsynchronized
             at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.29/src/backtrace/mod.rs:66
   2: std::sys_common::backtrace::_print
             at src/libstd/sys_common/backtrace.rs:47
   3: std::sys_common::backtrace::print
             at src/libstd/sys_common/backtrace.rs:36
   4: std::panicking::default_hook::{{closure}}
             at src/libstd/panicking.rs:200
   5: std::panicking::default_hook
             at src/libstd/panicking.rs:214
   6: std::panicking::rust_panic_with_hook
             at src/libstd/panicking.rs:477
   7: std::panicking::continue_panic_fmt
             at src/libstd/panicking.rs:384
   8: std::panicking::begin_panic_fmt
             at src/libstd/panicking.rs:339
   9: wgpu_swap_chain_get_next_texture
  10: wgpu::SwapChain::get_next_texture
  11: pf_sandbox::wgpu::WgpuGraphics::run
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
Illegal instruction (core dumped)

I believe this was caused by wgpu_glyph as my application does not copy buffers to images, but wgpu_glyph does.

Vertically centered text appears too low

I created an example here that illustrates. I would expect that the base of the text and the top of a capital letter are each the same distance from the vertical border of the window, but instead I observe that the base of the text is closer to the bottom of the window than the top of the capital is to the top of the window.

Stoke text

Hello,

Do have any plan to handle stroke text ?
I don't know if it here or in ab_glyph ?

Best regards
Marc-Antoine

Getting individual glyph positions for cursor rendering

Suppose I want to make an editable text field. This requires displaying a cursor in the text. However I cannot figure out how to get the position (baseline of the row would probably also be required) of a given glyph at a particular index in a section. I see that it is possible to get the bounding box a complete section, but I individual glyphs seem harder.

Stutter/hangups from displaying dynamic message

I've been investigating frame stutter in a game and it seems to happen when drawing dynamic characters in this library. "dynamic" meaning characters that change every frame, e.g. FPS counter or score.

I've found that it takes ~10-50ms of CPU time sometimes, which is too much. According to Nvidia's profiler, this is coming from a vkAllocateMemory call. Any ideas what is going on here? I assume this has something to do with caching because of the dynamic aspect, but even when all I am doing is flipping between 0 and 1 displayed in alternating frames it takes 10s of ms. But when I show 0 twice or 1 twice, the second time doesn't cause stutter.

Could also be a faulty gpu memory allocator in wgpu/gfx-rs I suppose, but curious if you know offhand what this might be.

Glyph rendering issues with GL backend

I have modified the hello.rs example to display a decreasing counter that is updated every frame.
Where the numbers are different compared to the first frame I get the following artifacts:
Selection_010

The numbers are rendered as expected when using the vulkan backend for wgpu.
I can also run the opengl example in glyph_brush without artifacts.

I would greatly appreciate any advice that can help me narrow down the cause of this problem.

Here is source code of my modified hello.rs:

use std::error::Error;
use wgpu_glyph::{ab_glyph, GlyphBrushBuilder, Section, Text};

fn main() -> Result<(), Box<dyn Error>> {
    env_logger::init();

    // Open window and create a surface
    let event_loop = winit::event_loop::EventLoop::new();

    let window = winit::window::WindowBuilder::new()
        .with_resizable(false)
        .build(&event_loop)
        .unwrap();

    let instance = wgpu::Instance::new(wgpu::BackendBit::GL);
    let surface = unsafe { instance.create_surface(&window) };

    // Initialize GPU
    let (device, queue) = futures::executor::block_on(async {
        let adapter = instance
            .request_adapter(&wgpu::RequestAdapterOptions {
                power_preference: wgpu::PowerPreference::HighPerformance,
                compatible_surface: Some(&surface),
            })
            .await
            .expect("Request adapter");

        adapter
            .request_device(&wgpu::DeviceDescriptor::default(), None)
            .await
            .expect("Request device")
    });

    // Create staging belt and a local pool
    let mut staging_belt = wgpu::util::StagingBelt::new(1024);
    let mut local_pool = futures::executor::LocalPool::new();
    let local_spawner = local_pool.spawner();

    // Prepare swap chain
    let render_format = wgpu::TextureFormat::Bgra8UnormSrgb;
    let mut size = window.inner_size();

    let mut swap_chain = device.create_swap_chain(
        &surface,
        &wgpu::SwapChainDescriptor {
            usage: wgpu::TextureUsage::RENDER_ATTACHMENT,
            format: render_format,
            width: size.width,
            height: size.height,
            present_mode: wgpu::PresentMode::Mailbox,
        },
    );

    // Prepare glyph_brush
    let inconsolata = ab_glyph::FontArc::try_from_slice(include_bytes!(
        "Inconsolata-Regular.ttf"
    ))?;

    let mut counter: usize = 9876543210;

    let mut glyph_brush = GlyphBrushBuilder::using_font(inconsolata)
        .build(&device, render_format);

    // Render loop
    window.request_redraw();

    event_loop.run(move |event, _, control_flow| {

        *control_flow = winit::event_loop::ControlFlow::Poll;

        match event {
            winit::event::Event::WindowEvent {
                event: winit::event::WindowEvent::CloseRequested,
                ..
            } => *control_flow = winit::event_loop::ControlFlow::Exit,
            winit::event::Event::WindowEvent {
                event: winit::event::WindowEvent::Resized(new_size),
                ..
            } => {
                size = new_size;

                swap_chain = device.create_swap_chain(
                    &surface,
                    &wgpu::SwapChainDescriptor {
                        usage: wgpu::TextureUsage::RENDER_ATTACHMENT,
                        format: render_format,
                        width: size.width,
                        height: size.height,
                        present_mode: wgpu::PresentMode::Mailbox,
                    },
                );
            }
            winit::event::Event::RedrawRequested { .. } => {
                counter -= 1;
                // Get a command encoder for the current frame
                let mut encoder = device.create_command_encoder(
                    &wgpu::CommandEncoderDescriptor {
                        label: Some("Redraw"),
                    },
                );

                // Get the next frame
                let frame = swap_chain
                    .get_current_frame()
                    .expect("Get next frame")
                    .output;

                // Clear frame
                {
                    let _ = encoder.begin_render_pass(
                        &wgpu::RenderPassDescriptor {
                            label: Some("Render pass"),
                            color_attachments: &[
                                wgpu::RenderPassColorAttachmentDescriptor {
                                    attachment: &frame.view,
                                    resolve_target: None,
                                    ops: wgpu::Operations {
                                        load: wgpu::LoadOp::Clear(
                                            wgpu::Color {
                                                r: 0.4,
                                                g: 0.4,
                                                b: 0.4,
                                                a: 1.0,
                                            },
                                        ),
                                        store: true,
                                    },
                                },
                            ],
                            depth_stencil_attachment: None,
                        },
                    );
                }



                glyph_brush.queue(Section {
                    screen_position: (30.0, 30.0),
                    bounds: (size.width as f32, size.height as f32),
                    text: vec![Text::new(&counter.to_string())
                        .with_color([0.0, 0.0, 0.0, 1.0])
                        .with_scale(40.0)],
                    ..Section::default()
                });

                glyph_brush.queue(Section {
                    screen_position: (30.0, 90.0),
                    bounds: (size.width as f32, size.height as f32),
                    text: vec![Text::new("Hello wgpu_glyph!")
                        .with_color([1.0, 1.0, 1.0, 1.0])
                        .with_scale(40.0)],
                    ..Section::default()
                });

                // Draw the text!
                glyph_brush
                    .draw_queued(
                        &device,
                        &mut staging_belt,
                        &mut encoder,
                        &frame.view,
                        size.width,
                        size.height,
                    )
                    .expect("Draw queued");

                // Submit the work!
                staging_belt.finish();
                queue.submit(Some(encoder.finish()));

                // Recall unused staging buffers
                use futures::task::SpawnExt;

                local_spawner
                    .spawn(staging_belt.recall())
                    .expect("Recall staging belt");

                local_pool.run_until_stalled();
            }
            winit::event::Event::MainEventsCleared => window.request_redraw(),
            _ => {
            }
        }
    })
}

Here is the log at the info level:

    Finished dev [unoptimized + debuginfo] target(s) in 0.04s
     Running `target/debug/examples/hello`
[2021-04-06T15:16:29Z INFO  winit::platform_impl::platform::x11::window] Guessed window scale factor: 1.25
[2021-04-06T15:16:29Z INFO  gfx_backend_gl::window::egl] Client extensions: "EGL_EXT_platform_base EGL_EXT_device_base EGL_EXT_device_enumeration EGL_EXT_device_query EGL_KHR_client_get_all_proc_addresses EGL_EXT_client_extensions EGL_KHR_debug EGL_KHR_platform_x11 EGL_EXT_platform_x11 EGL_EXT_platform_device EGL_EXT_platform_wayland EGL_KHR_platform_wayland EGL_MESA_platform_gbm EGL_KHR_platform_gbm EGL_MESA_platform_surfaceless"
[2021-04-06T15:16:29Z INFO  gfx_backend_gl::window::egl] Loading Wayland library to get the current display
[2021-04-06T15:16:29Z INFO  gfx_backend_gl::window::egl] Loading X11 library to get the current display
[2021-04-06T15:16:29Z INFO  gfx_backend_gl::window::egl] Using X11 platform
[2021-04-06T15:16:29Z INFO  gfx_backend_gl::window::egl] Display vendor "NVIDIA", version (1, 5), extensions: "EGL_EXT_buffer_age EGL_EXT_client_sync EGL_EXT_create_context_robustness EGL_EXT_image_dma_buf_import EGL_EXT_image_dma_buf_import_modifiers EGL_MESA_image_dma_buf_export EGL_EXT_output_base EGL_EXT_stream_acquire_mode EGL_EXT_sync_reuse EGL_IMG_context_priority EGL_KHR_config_attribs EGL_KHR_create_context_no_error EGL_KHR_context_flush_control EGL_KHR_create_context EGL_KHR_fence_sync EGL_KHR_get_all_proc_addresses EGL_KHR_partial_update EGL_KHR_swap_buffers_with_damage EGL_KHR_no_config_context EGL_KHR_gl_colorspace EGL_KHR_gl_renderbuffer_image EGL_KHR_gl_texture_2D_image EGL_KHR_gl_texture_3D_image EGL_KHR_gl_texture_cubemap_image EGL_KHR_image EGL_KHR_image_base EGL_KHR_image_pixmap EGL_KHR_reusable_sync EGL_KHR_stream EGL_KHR_stream_attrib EGL_KHR_stream_consumer_gltexture EGL_KHR_stream_cross_process_fd EGL_KHR_stream_fifo EGL_KHR_stream_producer_eglsurface EGL_KHR_surfaceless_context EGL_KHR_wait_sync EGL_NV_nvrm_fence_sync EGL_NV_post_sub_buffer EGL_NV_quadruple_buffer EGL_NV_stream_consumer_eglimage EGL_NV_stream_cross_display EGL_NV_stream_cross_object EGL_NV_stream_cross_process EGL_NV_stream_cross_system EGL_NV_stream_dma EGL_NV_stream_flush EGL_NV_stream_metadata EGL_NV_stream_remote EGL_NV_stream_reset EGL_NV_stream_socket EGL_NV_stream_socket_inet EGL_NV_stream_socket_unix EGL_NV_stream_sync EGL_NV_stream_fifo_next EGL_NV_stream_fifo_synchronous EGL_NV_stream_consumer_gltexture_yuv EGL_NV_stream_attrib EGL_NV_stream_origin EGL_NV_system_time EGL_NV_output_drm_flip_event EGL_NV_triple_buffer"
[2021-04-06T15:16:29Z INFO  gfx_backend_gl::window::egl] Trying native-render
[2021-04-06T15:16:29Z WARN  gfx_backend_gl::window::egl] No config found!
[2021-04-06T15:16:29Z INFO  gfx_backend_gl::window::egl] Trying presentation
[2021-04-06T15:16:29Z INFO  gfx_backend_gl] Vendor: "NVIDIA Corporation"
[2021-04-06T15:16:29Z INFO  gfx_backend_gl] Renderer: "GeForce RTX 2070/PCIe/SSE2"
[2021-04-06T15:16:29Z INFO  gfx_backend_gl] Version: 3.2, NVIDIA 460.39
[2021-04-06T15:16:29Z INFO  gfx_backend_gl] Shading Language: 3.2
[2021-04-06T15:16:29Z INFO  gfx_backend_gl] Supported Features: INDEPENDENT_BLENDING | SAMPLER_ANISOTROPY | INSTANCE_RATE | MUTABLE_COMPARISON_SAMPLER | NDC_Y_UP
[2021-04-06T15:16:29Z INFO  gfx_backend_gl] Legacy Features: DRAW_INSTANCED | DRAW_INDEXED_INSTANCED | VERTEX_BASE | CONSTANT_BUFFER | COPY_BUFFER | SAMPLER_OBJECTS | EXPLICIT_LAYOUTS_IN_SHADER | INSTANCED_ATTRIBUTE_BINDING
[2021-04-06T15:16:29Z INFO  gfx_backend_gl] Memory types: [
        (
            MemoryType {
                properties: CPU_VISIBLE | COHERENT | CPU_CACHED,
                heap_index: 1,
            },
            Buffer(
                TRANSFER_SRC | TRANSFER_DST | UNIFORM_TEXEL | STORAGE_TEXEL | UNIFORM | STORAGE | INDEX | VERTEX | INDIRECT,
            ),
        ),
        (
            MemoryType {
                properties: CPU_VISIBLE | COHERENT,
                heap_index: 1,
            },
            Buffer(
                TRANSFER_SRC | TRANSFER_DST | UNIFORM_TEXEL | STORAGE_TEXEL | UNIFORM | STORAGE | INDEX | VERTEX | INDIRECT,
            ),
        ),
        (
            MemoryType {
                properties: DEVICE_LOCAL,
                heap_index: 0,
            },
            Buffer(
                TRANSFER_SRC | TRANSFER_DST | UNIFORM_TEXEL | STORAGE_TEXEL | UNIFORM | STORAGE | INDEX | VERTEX | INDIRECT,
            ),
        ),
        (
            MemoryType {
                properties: DEVICE_LOCAL,
                heap_index: 0,
            },
            Image,
        ),
    ]
[2021-04-06T15:16:29Z INFO  gfx_backend_gl] Debug output is enabled
[2021-04-06T15:16:29Z INFO  gfx_backend_gl::device]     Created frame buffer 1
[2021-04-06T15:16:29Z WARN  gfx_backend_gl::device] View format 33321 is different from base 6403
[2021-04-06T15:16:29Z WARN  naga::front::spv] Unknown decoration ColMajor
[2021-04-06T15:16:29Z WARN  naga::front::spv] Unknown decoration MatrixStride
[2021-04-06T15:16:29Z WARN  naga::front::spv] Treating VertexIndex as unsigned
[2021-04-06T15:16:29Z INFO  gfx_backend_gl::device]     Compiled shader 2
[2021-04-06T15:16:29Z INFO  gfx_backend_gl::device]     Compiled shader 3
[2021-04-06T15:16:29Z INFO  gfx_backend_gl::device]     Linked program 1

Wasm support?

Hello! So I tried out some of the examples following the directions in wgpu-rs's wiki to compile them to wasm, but they all seemed to fail on trying to create a surface from the winit window (or various other reasons; I tried to get them to work by modifying them based on wgpu-rs's examples, but to no avail). Am I doing something wrong, or does this library not support targeting wasm? If the latter, could support for wasm be added?

Thanks!

Stroked text?

Is it possible to draw text with a stroke using this library? Will this be coming in the future?

GlyphBrush bundles upstream GlyphBrush and Pipeline

I wish to calculate an optimal window size (using GlyphCruncher::glyph_bounds), then create that window. Resizing the window after creation has unwanted side-effects.

Using wgpu_glyph it does not appear that I am able to do this, at least without creating the GlyphBrush twice. Yet from examining the source, it appears that the only reason for this is that GlyphBrush encapsulates both the glyph_brush and the pipeline.

Probably the simplest solution (though awkward) is to allow a GlyphBrush to be created from an existing object of the upstream GlyphBrush type.

Bug: wgpu_glyph renders gibberish psuedo-glyphs when used with ANGLE and or OpenGL backend on ARM OSX

I'm running into an issue with wgpu_glyph where it renders gibberish letters if an OpenGL or ANGLE backend is used. But if a Metal backend is used everything renders perfectly normally. Unfortunately Chrome does not yet support Metal unless enabled in an experimental feature flag. See images below

ANGLE On Chrome 114, With M2 Macbook:
Screenshot 2023-06-03 at 4 13 40 PM
METAL On Chrome 114, With M2 Macbook:
Screenshot 2023-06-03 at 4 14 38 PM

One thing to note is that the gibberish letters are different every time a new window is opened. But are not different within the same window. This is not the case with Firefox where the gibberish is different every time. It does work without changing any feature flags in Safari. But that is because Safari Uses Metal by default. This issue also happens with Firefox (113.0) .

Vulkan Validation Errors

I'm getting vulkan validation errors when running the examples on windows, the following output is from the hello example, but I get similar results from the clipping and depth examples as well.

[2021-02-16T00:39:25Z ERROR gfx_backend_vulkan]
    VALIDATION [VUID-VkRenderPassBeginInfo-framebuffer-03210 (-872316813)] : Validation Error: [ VUID-VkRenderPassBeginInfo-framebuffer-03210 ] Object 0: handle = 0xbb4e7a000000001f, type = VK_OBJECT_TYPE_RENDER_PASS; | MessageID = 0xcc018073 | vkCmdBeginRenderPass(): Image view #0 created from an image with usage set as 0x11, but image info #0 used to create the framebuffer had usage set as 0x10 The Vulkan spec states: If framebuffer was created with a VkFramebufferCreateInfo::flags value that included VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT, each element of the pAttachments member of a VkRenderPassAttachmentBeginInfo structure included in the pNext chain must be a VkImageView of an image created with a value of VkImageCreateInfo::usage equal to the usage member of the corresponding element of VkFramebufferAttachmentsCreateInfo::pAttachments used to create framebuffer (https://vulkan.lunarg.com/doc/view/1.2.162.0/windows/1.2-extensions/vkspec.html#VUID-VkRenderPassBeginInfo-framebuffer-03210)
    object info: (type: RENDER_PASS, hndl: 13496859273694543903)

[2021-02-16T00:39:25Z ERROR gfx_backend_vulkan]
    VALIDATION [VUID-VkRenderPassBeginInfo-framebuffer-03210 (-872316813)] : Validation Error: [ VUID-VkRenderPassBeginInfo-framebuffer-03210 ] Object 0: handle = 0x8483000000000025, type = VK_OBJECT_TYPE_RENDER_PASS; | MessageID = 0xcc018073 | vkCmdBeginRenderPass(): Image view #0 created from an image with usage set as 0x11, but image info #0 used to create the framebuffer had usage set as 0x10 The Vulkan spec states: If framebuffer was created with a VkFramebufferCreateInfo::flags value that included VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT, each element of the pAttachments member of a VkRenderPassAttachmentBeginInfo structure included in the pNext chain must be a VkImageView of an image created with a value of VkImageCreateInfo::usage equal to the usage member of the corresponding element of VkFramebufferAttachmentsCreateInfo::pAttachments used to create framebuffer (https://vulkan.lunarg.com/doc/view/1.2.162.0/windows/1.2-extensions/vkspec.html#VUID-VkRenderPassBeginInfo-framebuffer-03210)
    object info: (type: RENDER_PASS, hndl: 9548475634955583525)

This is running with VulkanSDK version 1.2.162.0 on an nvidia gtx 1050.

Creating an unrelated buffer stops text from rendering

Hello, I'm not sure if this is a problem with wgpu, wgpu_glyph, or my understanding of wgpu so I'm posting it to both projects.

I've created a minimal runnable example here: https://github.com/MichaelBaker/wgpu-glyph-example

This is the line that causes the problem. If you comment this line out, the text renders correctly: https://github.com/MichaelBaker/wgpu-glyph-example/blob/5bcd7200d1a27df7add4a68b744b41df9b1de681/src/main.rs#L70

My best guess is that creating a buffer here corrupts or invalidates wgpu_glyph's cached transform buffer because if I remove this check from the pipeline it starts rendering correctly:

if transform != pipeline.current_transform {

Otherwise this library has worked great so thanks for putting it together.

I'm on a Macbook Pro running macOS 10.15.3 (Catalina).

Here is the wgpu issue: gfx-rs/wgpu-rs#288

Broken Documentation links?

Im writing a game engine and using wgpu_glyph for the text rendering and here is the snippet of my code

         render_items.transformed_text.iter()
            .map(|text| (wgpu_glyph::Section{
                screen_position: (text.position.x, text.position.y),
                bounds: (self.size.width as f32, self.size.height as f32),
                text: vec![wgpu_glyph::Text::new(&text.text).with_color(text.colour.to_raw())],
                ..Default::default()
                }, text.transformation))
            .for_each(|(section, transform)| {
                let text_transform = unflatten_matrix(transform);
                let ortho = unflatten_matrix(orthographic_projection(self.size.width, self.size.height));
                let transform = flatten_matrix(ortho * text_transform);
                self.glyph_brush.queue(section);
                self.glyph_brush.draw_queued_with_transform(
                    &self.device, &mut staging_belt, &mut encoder, &view, &self.camera_bind_group, transform,
                ).unwrap();
            });
        let text_sections = render_items.text
            .iter()
            .map(|text| wgpu_glyph::Section{
                screen_position: (text.position.x, text.position.y),
                bounds: (self.size.width as f32, self.size.height as f32),
                text: vec![wgpu_glyph::Text::new(&text.text).with_color(text.colour.to_raw())],
                ..Default::default()
            })
            .collect::<Vec<wgpu_glyph::Section>>();
        text_sections.into_iter().for_each(|s| self.glyph_brush.queue(s));
        self.glyph_brush.draw_queued(&self.device, &mut staging_belt, &mut encoder, &view, &self.camera_bind_group, self.size.width, self.size.height).unwrap();

This produces a strange result where the text that is supposed to have no transformations (other than the orthographic transformation) So i went to the documentation for the glyph_brush and under queue it states Benefits from caching, see caching behaviour; however, when I click the link to see this caching behavior it links nowhere.

Get bounding box of drawn text

I need to know the dimensions of the rendered text after it is drawn or calculate if beforehand. Is there a suitable method for that in this library?

My Uniform Overrides Internal Transform Uniform

Hi I encountered a problem when using wgpu with DX12.
The following issue did not appear when using wgpu with Vulcan on linux.

As soon as I create a bind group it overrides one inside of glyph.
In this case, it is transform uniform.
For example if I edit examples/hello.rs
like so:

... [Begining Of The File]
let mut glyph_brush = GlyphBrushBuilder::using_font_bytes(inconsolata)
        .expect("Load fonts")
        .build(&device, render_format);

//Here I create my uniform, that should not interrupt glyph inner workings
{
        let data = TransformUniform::default();

        let bind_group_layout_descriptor = wgpu::BindGroupLayoutDescriptor {
            label: Some("new_1"),
            bindings: &[wgpu::BindGroupLayoutEntry {
                binding: 1,
                visibility: wgpu::ShaderStage::VERTEX,
                ty: wgpu::BindingType::UniformBuffer { dynamic: false },
            }],
        };

        let buffer = device.create_buffer_with_data(
            &data.as_bytes(),
            wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
        );

        let bind_group_layout =
            device.create_bind_group_layout(&bind_group_layout_descriptor);

        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
            label: Some("new_2"),
            layout: &bind_group_layout,
            bindings: &[wgpu::Binding {
                binding: 1,
                resource: wgpu::BindingResource::Buffer {
                    buffer: &buffer,
                    range: 0..std::mem::size_of_val(&data)
                        as wgpu::BufferAddress,
                },
            }],
        });
}

#[repr(C)]
#[derive(Clone, Copy, AsBytes)]
struct TransformUniform {
    transform: [f32; 16],
}
impl Default for TransformUniform {
    fn default() -> Self {
        Self {
            // Here I intentionally create projection with wrong size to demonstrate that it affects glyph
            transform: orthographic_projection(100.0, 100.0),
        }
    }
}

... [Rest Of The File]

It gives me output like this
ing
Same Code on Linux with Vulcan
ing

This issue is probably not wgpu-glyph related and should be submitted to main wgpu repo,
but I decided to ask here first in case I made some stupid mistake related to my small wgpu knowledge

Way to get font name

I don't see any way to get the name of a font. am I overlooking something or isn't such method not implemented?

repeatedly blinks

Hello!

The glyph is working super awesome, except when rendering, it blink repeatedly... the library that I'm working on that uses it is this.

I thought at first it's a problem on my side, and I tried limiting FPS, debugged by rechecking everything from examples, and did anything I for optimization, but still couldn't save the blinking problem...

I'm using latest Rust, on Windows x64 latest version

hello example is just a gray background

re: iced-rs/iced#96

hello example is just a gray background

I also have the same problem as the author of the iced issue. I was originally investigating why iced doesn't display text in its examples.

AdapterInfo { name: "Radeon (TM) Pro WX 4100", vendor: 4098, device: 26595, device_type: DiscreteGpu }

Gpu driver stops respnding on large scale.

OS: Arch Linux
GPU: GTX 960
Driver: Nvidia proprietary driver

If I change the example to use a scale of Scale::uniform(200) then I get a panic

thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `Ok(false)`,
 right: `Ok(true)`: GPU got stuck on a frame (image 0) :(', /home/rubic/.cargo/git/checkouts/wgpu-53e70f8674b08dd4/dbef9f3/wgpu-native/src/swap_chain.rs:178:5
stack backtrace:
   0: std::sys::unix::backtrace::tracing::imp::unwind_backtrace
             at src/libstd/sys/unix/backtrace/tracing/gcc_s.rs:39
   1: std::sys_common::backtrace::_print
             at src/libstd/sys_common/backtrace.rs:71
   2: std::panicking::default_hook::{{closure}}
             at src/libstd/sys_common/backtrace.rs:59
             at src/libstd/panicking.rs:197
   3: std::panicking::default_hook
             at src/libstd/panicking.rs:211
   4: std::panicking::rust_panic_with_hook
             at src/libstd/panicking.rs:474
   5: std::panicking::continue_panic_fmt
             at src/libstd/panicking.rs:381
   6: std::panicking::begin_panic_fmt
             at src/libstd/panicking.rs:336
   7: wgpu_swap_chain_get_next_texture
   8: wgpu::SwapChain::get_next_texture
   9: hello::main
  10: std::rt::lang_start::{{closure}}
  11: std::panicking::try::do_call
             at src/libstd/rt.rs:49
             at src/libstd/panicking.rs:293
  12: __rust_maybe_catch_panic
             at src/libpanic_unwind/lib.rs:87
  13: std::rt::lang_start_internal
             at src/libstd/panicking.rs:272
             at src/libstd/panic.rs:388
             at src/libstd/rt.rs:48
  14: main
  15: __libc_start_main
  16: _start

enabling logging does also display this warning:

[2019-07-14T08:43:35Z WARN  wgpu_glyph] Increasing glyph texture size (256, 256) -> (512, 512). Consider building with `.initial_cache_size((512, 512))` to avoid resizing

Rotate text

Hey, wgpu_glyph is awesome. I am using it for a nodegraph UI, and it's beautiful.
I have one question though. What do you advise as the best way to rotate text?
I've found 'draw_queued_with_transform', which allows me to provide a transform matrix for the position/scale/rotation of the text I'm drawing but:

  • I have to do a 'draw_queued_with_transform' call for each peice of text that has a different rotation, whereas with positioning, i could have lots of sections, but one draw call.

I want to double check I'm not going down the wrong path.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.