amethyst / rfcs

RFCs are documents that contain major plans and decisions for the engine
Apache License 2.0
32 stars 10 forks source link

[RFC] New amethyst render #12

Open zakarumych opened 6 years ago

zakarumych commented 6 years ago

Fancy and shiny new amethyst render proposal.

This RFC made in attempt to systematize ideas and thoughts on new render I went on writing almost a yer ago.

What this RFC is about

I would try to describe how new amethyst render can look like. Both from user perspective and implementation. The aim of this RFC is to gather feedback on mentioned problems and proposed solutions.

What wrong with current render

Let's step back and look at current render. Why do we even want to replace it? First thing that came to mind is singlethreadedness. My early attempt to make things run in parallel only made it worse (proven by @Xaeroxe, when he simplified it to run in one thread performance increased). Come to think of it make it obvious, OpenGL has singlethreaded heart. Commands we encode in parallel become serialized at flush time.

The second pain point is singlethreadedness. Yes. Again. It hurts this much. We can't even create and upload resources (images and buffers) in parallel. This means we can't do it in our Systems. Current workaround is to defer resource initialization to be complete by render. Loading code is overcomplicated because of this. Also makes it impossible to generate data for GPU each frame outside render (think mesh generation from voxels).

Significant overhead. Current render works on pre-ll gfx that supports only OpenGL right now. Each layer adds an overhead. New APIs provide opportunities for optimizing in way more places and reduces problem with CPU-bottleneck. Yet pre-ll gfx doesn't support newer APIs and even if will it gives user same freedom as OpenGL where user can't optimize based on usage too much. OpenGL users utilize arcane techniques to squeeze as much performance as possible. If we start doing so we may end up with unmaintainable pile of hacks buried into endless pit of sorrow.

Solution

We need to write new render that will be based on modern graphics APIs like Vulkan, DirectX 12, Metal.

But which to choose? We can't choose one without sacrificing platform support. We can't manually support each of them either. Gladly it already taken care of.

gfx-hal

gfx-hal is not an evolution of pre-ll gfx. It's a brand new thing. gfx-hal's API is based on Vulkan API with added rustyness but with minimum overhead.

gfx-hal should open the path to support following platforms

Sadly gfx-hal is not even close to become stable.

ash

Another alternative is ash. With Vulkan/Metal bridge like MoltenVK or gfx-portability we would support:

ash requires more boilerplate and careful implementation. It is essentially raw Vulkan API for rust. Which means it is pretty stable.

Supporting multiple backends in our higher-level render.

It can be done. It is even simpler to do in higher-level code. But I don't think it is a feasible option.

High-level render design outline

Modules

While amethyst will use the render as a whole it doesn't mean render code must be written as a huge code blob. It may be helpful to design render as collection of modules each of which solves one problem at a time.

What problems higher-level render should solve you may ask. Let's describe few:

Memory management.

Modern APIs have complex memory management story with lots of properties, rules and requirements for the application. Higher-level render should give the user straightforward API for create/destroy resources and transfer data between resources and host.

Work scheduling.

Vulkan have 3 types of objects in API for scheduling work to the device. Namely vkQueue, vkCommandPool and vkCommandBuffer.

vkQueue inherit capabilities from its family and user is responsible to not try to schedule unsupported commands. Higher-level render should check that (at compile time where possible).

vkCommandPool is simple as an axe. No fancy wrapper required except tracking queue family it belongs to.

vkCommandBuffer have implicit state that changes subset of functions that can be used with it. Higher-level render should prevent both wrong usage and unnoticed state change. To prevent implicit transition to the Invalid state there must be facility to hold resources referenced in recorded commands from being destroyed.

Pipelines

Manually describing and switching graphics and compute pipelines is hard and error-prone. Higher-level render should support pipelines described in declarative manner and automate their binding.

Synchronization

New graphics APIs such as Vulkan require explicit synchronization between commands when they depend on each other or use same resource. This topic is really complex. Rules are sophisticated. Errors could be hidden until release. Framegraph approach allow automatic synchronization between nodes of the graph. gfx-chain library does this kind of automatic scheduling between queues and deriving synchronization required. It should be reworked to remove gfx-hal dependency from which only few structures used anyway. Because of upfront knowledge for the resource usage it is possible to greatly optimize memory usage by aliasing transient resource that is never exists together.

Descriptors

Handling descriptors is non-trivial work and should be simplified by higher-level render. But can be done later as the only one who will work with them are render-pass writers. Suboptimal usage of descriptors are very simple and should be OK until becoming a bottleneck.

Higher-level primitives

While graphics API consume resources, pipelines and lists of encoded commands to to their job the user shouldn't be faced with such low-level concepts unless he tries to render something non-trivial. Well defined common use cases could be bundled and provided out of the box.

would be a good start.

What there already is

At this point I have memory manager ready-to-test and prototype of command buffer/queue safety wrappers. There is also

TODO: Add shiny diagrams and fancy snippets.