gfx-rs / gfx

[maintenance mode] A low-overhead Vulkan-like GPU API for Rust.
http://gfx-rs.github.io/
Apache License 2.0
5.35k stars 547 forks source link

Add support for switching render backend at runtime #56

Closed emberian closed 6 years ago

emberian commented 10 years ago

In particular, switching between OpenGL, DirectX, and whatever other APIs may be supported by gfx-rs (Mantle, Metal, ...) in the future.

emberian commented 10 years ago

I am unsure the precise requirements here but I plan on looking into it further and doing a full report here.

brendanzab commented 10 years ago

This would be great, and very much in scope for gfx-rs. :+1:

emberian commented 10 years ago

I've started reviewing PRs for D3D compatability.

emberian commented 10 years ago

I basically think this is impossible with any sane non-runtime-switchable interface, see https://github.com/rust-lang/rfcs/pull/195#issuecomment-53127823. I'm going to close this for now, though it is sad...

brendanzab commented 10 years ago

sadface

brendanzab commented 10 years ago

Maybe we need a status tag for "blocked on rust-lang/rust"

emberian commented 10 years ago

I'm not sure it will ever be possible.

emberian commented 10 years ago

Although, it's conceivable that we could have a SwitchableDevice trait that returns trait objects instead of any concrete type, in addition to just Device.

eugene2k commented 8 years ago

I remember the game engines in the latter half of the 90's. You had Direct3D and OpenGL and even software rasterizers, and you could actually see the difference between the API's. E.g. software rasterizers would render these blocky textures, since bilinear filtering would take too much CPU, and a lot of the time vendor implementations of OpenGL vs Direct3D would differ such that for example some 3D accelerators had artefacts in OpenGL, but not in Direct3D, or things like glass materials didn't look like glass in Direct3D because it didn't support something and you could change the rasterizer in the settings. I haven't seen an engine that allows the user to change the rasterizer for over a decade now. I figure it's because things like the aforementioned artifacts are all but gone now, so I'm wondering: what would be the use in having gfx support switching rendering apis at runtime?

emberian commented 8 years ago

@eugene2k Unity supports it, for example. Though of course not at runtime. I originally opened this issue as a, "can we do this?". I am not any more sure that it's valuable as I was when I opened it :) One thing that might be nice is shipping a single binary for all the various APIs, which could be quite useful - Vulkan where it's supported, OpenGL or DX11 where it's not.

What is clearly out of scope is changing the backend of a currently-running renderer - you'll definitely need to tear it down and build it back up with the new API in place. I'm not sure whether and to what extent windowing APIs support reusing windows in this way (the biggest concern really being Windows, which is where multiple APIs are usually present, though in the future I guess it will be OpenGL vs Vulkan on Linux, or OpenGL vs Metal on OS X/iOS).

eugene2k commented 8 years ago

Hmm... I didn't think about api switching in that context, but it should be possible without runtime switching, shouldn't it? Theoretially all you need is to create two (or more) renderers instead of one, place them into separate dlls, and choose between them at runtime, before initialization.

emberian commented 8 years ago

Yes, that's a fine theory, but that's not how it works out in Rust, especially how we use generics. You need to have everything going through the same ABI or otherwise via trait objects, which looks exactly the same as runtime switching.

dkushner commented 7 years ago

If I may offer my use case and experience, we desperately need this feature:

One thing that might be nice is shipping a single binary for all the various APIs, which could be quite useful - Vulkan where it's supported, OpenGL or DX11 where it's not.

We are trying to reimplement our visualization front-end for long-running distributed computing applications and our end-users are generally non-programmers or at least not keen enough to configure and build the application such that it fits their specific machine. We definitely need the ability to select the particular implementation we'd like to use at runtime. Being able to switch that backend at runtime would be nice but being able to deploy a single binary for each platform and allow the end user to specify rendering backends via config file is a serious must.

Also, if anyone at all has any examples of an application that provides multiple, selectable backends that does not use the gfx_app package, I would really love to see some sample source.

kvark commented 7 years ago

@dkushner it all comes down to narrowing down the part of the program that knows about different backends. E.g. it may just be your main() function doing:

match config.graphics_backend {
  Backend::OpenGL3 => {
      gfx_window_glutin::init(...);
      gfx_device_gl::init(...);
      run(window, device, factory);
  },
  Backend::Direct3D11 => {
      gfx_window_dxgi::init(...);
      gfx_device_dx11::init(...);
      run(window, device, factory);
  },
}

Another (simpler) solution is to use config flags, like Amethyst does. It used to have enums for some of the types, where enumeration values are basically backend versions of the same concepts (GL Device, D3D11 Device, etc), but this approach turned out to be pretty verbose.

msiglreith commented 7 years ago

I did a PoC for an additional backend some time ago, which would handle runtime dispatching. https://github.com/msiglreith/gfx-frontend/blob/master/src/lib.rs An application would then only interface this backend or it could be, additionally, coupled with compile time switching as done in Amethyst.

dkushner commented 7 years ago

@kvark, oh yeah I'm up to here at least, my issue is mostly the fact that I need a service like RenderManager to be in charge of creating GPU-bound resources at any arbitrary time over the lifetime of the application. For this reason, RenderManager needs to hold a reference to the Factory and Device but these traits cannot be trait objects, so I'm thinking the absolute only way to accomplish this is to similarly type paramaterize the RenderManager implementation? Is my thinking correct?

Basically, it's like your example but instead of passing the device and factory off to a run() I need to store it in a struct.

kvark commented 6 years ago

With LL transition we are getting more traits and associated types. We are full on the generic backend API, and providing run-time switching is escaping the scope. If that's badly needed, there are 2 ways to achieve it that I can think of:

  1. Enclose render-dependent code into a separate executable (or dynamic library). For example, original Unreal shipped with a launcher app that allowed you to configure the settings and choose a graphics backend. Technically, the game could have multiple executables for different backends, and I think some of the games do.
  2. Wrap every type into an enum at the type level like @msiglreith proposed. This is rather tedious, and I believe should be explored from the point of view of procedural macros or compiler plugins, generating all the boilerplate automatically.

On this note, I believe the issue can be closed.