Gfx: Are Users Supposed to Use gfx_app::Application?

Created on 31 Mar 2016  Â·  30Comments  Â·  Source: gfx-rs/gfx

I'm trying to figure out if I'm supposed to use gfx_app::Application since that's what all the examples use, but don't see a way to take user input. Looking at the implementation it hard codes the keys it will accept. Is there a way to handle user input with this struct or is this boilerplate I'm supposed to copy and modify?

let mut harness = Harness::new();
main: loop {
    // quit when Esc is pressed.
    for event in window.poll_events() {
        match event {
            glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Escape)) |
            glutin::Event::Closed => break 'main,
            _ => {},
        }
    }
    // draw a frame
    app.render(&mut device);
    window.swap_buffers().unwrap();
    device.cleanup();
    harness.bump()
}
pre-ll api discussion

Most helpful comment

gfx_app is a test runner that was added to help with the examples. You can use it as a guide, but I would not recommend building off of it.

All 30 comments

gfx_app is a test runner that was added to help with the examples. You can use it as a guide, but I would not recommend building off of it.

Is there something you would recommend? I've tried piston_window but it's difficult trying to find out where the Init{} parameters in Applicaiton line up with with the window returned from it.

User input is not yet handled, blocked by #907

I wonder if there is a way to make this clearer to people looking at the examples. It might be confusing having it as a separate crate makes it look like it is part of the user facing library. Glium has a support module for example: https://github.com/tomaka/glium/tree/master/examples/support, and examples import it using mod support; - making it clear that it is part of client code.

From my perspective, it would be nicer if the examples showed how to set up the boilerplate needed to use the library. I really liked the simplicity of the examples, but feel like it only showed me how to use gfx with gfx_app. What I thought were core concepts of the library (say gfx_app::shade::Source) turned out to actually be stuff I need to re-implement myself. I went from understanding how to use the library, to looking at glutin and seeing what the alternatives are there; raised the bar to entry.

I do like this library, so keep up the good work. It was easier (and more powerful) to get started than say kiss3d, but feel a little lost since there is so much if windows OS do this in the gfx_app crate.

Well, before any of the examples are switched to showing how to do it right, here's the basic thing you have to do:
1] use gfx_vertex! macro to describe your vertex buffer in the format of a struct
2] use the gfx_pipeline! macro to describe the shader uniforms/constants in a Data struct type and create some associated boilerplate.
3] init the window with the help of one of the gfx_window_* crates
4] create a command buffer and obtain the encoder from it with something like this:

    let mut encoder: gfx::Encoder<Resources, CommandBuffer> =
        factory.create_command_buffer().into();

5] create a vertex buffer, a pipeline state object, textures if you're using them etc. The PSO has a type of gfx::PipelineState<Resources, pipe::Meta>
6] create an instance of your Data struct
7] use encoder.draw(&slice, &pso, &data) to draw your data

I agree we should have one complete example implemented without using gfx_app. Cube might be that one. A PR would be welcome ;)

I was just bit by this playing around with gfx, too, and inlined all the gfx_app stuff into the triangle example.

@kvark, I could share this, if you don't mind that it's the triangle instead of the cube.

The cube has texturing, though I suppose it won't matter too much.

@aepsil0n sounds good!

hm… I just realized that I simply copied the shade module. That one seems to be generally quite useful. Any ideas how to remove that dependency?

I'm not sure I understand. All you need is to provide two arrays for the shaders in create_shader_simple...

Well, my concern is that it will only work for one shader language version, if I do it like that. Would that be okay? I guess there's a point to made to keep it simple instead of generic, so that users get a feeling for the general code structure rather than being overwhelmed by everything possible. But maybe it should still be cross-platform compatible?

I guess, I'll just submit a pull request, then we can discuss this better.

@aepsil0n technically, you could still support multiple GLSL versions by explicitly checking for gl_device.get_info().shading_language. I would complicate the example a little bit though, so I believe a clean triangle with a single GLSL version (say, 150 core) is better.

Done. Perhaps it would be a good idea to also add some documentation making this more clear to users.

@kruffin can this be closed, given @aepsil0n PR #930 is in?

Hey @kvark, you can close this; that example along with @eugene2k's steps in the comments were exactly what I needed. Even without the Application struct it's still a small amount of code, making it easier for me to grasp. Thanks everyone and well done!

I just wanted to mention that I'm a new user to gfx, and I still had this problem. I saw that the triangle example used glutin, and that most of the other examples used gfx_app. Since most of the examples use gfx_app, I had assumed that was part of the higher level api that gfx was shipping with. I had assumed that triangle and others were using glutin because they were simple examples meant to illustrate how to use gfx with other window libraries as needed.

I think there should still be some clearer documentation that gfx_app is intended to be an application for examples, not part of gfx itself. Perhaps a note at the end of each example's readme.md or at gfx/examples/readme.md?

@assumptionsoup agreed! The usage of gfx_app is blocked by #1113 at the moment.

It appears that I closed the bug a bit too early.

I found gfx_app essential for deduplicating code between backends, however it has some ugly sides:

  • Forcing a constructor with limited params. This doesn't allow any additional data fields.
  • Taking a WindowBuilder as a parameter from outside. Many games has a forced screen resolution.
  • Using an enum which is runtime dispatch in code. This really can be a trait instead.
  • Almost no documentation.

I'm thinking about redesigning it from scratch and bump the semver. Let's open a new issue thread if this sounds good.

@ishitatsuyuki

Forcing a constructor with limited params. This doesn't allow any additional data fields.
Almost no documentation.
I'm thinking about redesigning it from scratch and bump the semver.

PRs welcome!

Taking a WindowBuilder as a parameter from outside. Many games has a forced screen resolution.

So these games would just set the resolution inside WindowBuilder. What's the problem here?

Using an enum which is runtime dispatch in code. This really can be a trait instead.

What enum exactly?

Taking a WindowBuilder as a parameter from outside. Many games has a forced screen resolution.

So these games would just set the resolution inside WindowBuilder. What's the problem here?

This is something from outside of the struct, which is making it less OOP.

Using an enum which is runtime dispatch in code. This really can be a trait instead.

What enum exactly?

The backend enum; I originally thought that it could be a template; but runtime dispatching seems to have a demand (e.g. adaptive GL version). Nevermind this point.

@ishitatsuyuki

This is something from outside of the struct, which is making it less OOP.

I don't understand, I see it inside the struct: https://docs.rs/winit/0.6.3/winit/struct.WindowBuilder.html#method.with_dimensions

I'm saying that the arguments are passed from outside of the Application struct. The title and size of the application should be specified inside the logic, not from the caller. (Just noticed now, it doesn't even allow us to touch the window (e.g. change the title) from inside of the struct!?)

@ishitatsuyuki ah, I see. It was developed within the assumption that the implementor of Application and the user are the same person.
Could you explain how you are proposing to use gfx_app?

gfx_app should be composed inside the main logic struct, and acting as helper for determining backends. We should leave only one or two lines of bootstrapping code in fn main().

If the main() is implemented by the same author as Application, then what's the harm in setting the resolution from there? My concern here is keeping it simple for the examples and simple apps.

Originally I assumed that anything complex should just use the window crates and manage the event loops manually. I'm open to ideas on how to make it more universal, just as long as the original case doesn't suffer.

I would also appreciate some examples that at least demonstrate how to manually set up the boilerplate encapsulated by Application as other projects may wish to manage the underlying window and device implementations in a different way.

To be specific, I want to create the requisite device and factory objects in main and then give them to my own struct implementations like RenderManager. However, it is impossible to store these as trait objects, so I'm curious what approach should be taken (this is likely more a limitation of my rust knowledge than the lib itself).

Triangle example shows how to use gfx-rs without the gfx_app crate, closing.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

kvark picture kvark  Â·  4Comments

Fluci picture Fluci  Â·  5Comments

kvark picture kvark  Â·  3Comments

djcsdy picture djcsdy  Â·  4Comments

kvark picture kvark  Â·  5Comments