Comments (9)
Notes from @csherratt:
...it would be interesting if you treated them like a uniform buffer. The use can use a macro to build a structure that binds the shader parameters to a POD structure. The POD structure implements a trait that allows it to throw up its content into a GLSL shader when the graphics pipeline requires it.
I think it can be made to work with or without UBOs. Without a UBO it would just be given an interface to writing to the shader's uniforms, in the UBO case it could be a memory mapped uniform buffer or something similar. I wonder just how difficult it would be to cache the shader inspection using such an interface.
from gfx.
Notes from @glennw:
What I've done previously is have a fixed enum of shader constant types rather than strings. This makes it easy to efficiently pack the data and very efficient. It also means that all of the common parameters (projection matrices, lights etc) are consistent for every shader. Then we had a small set of custom/user defined parameters available per shader - these are internally still referenced as enums (e.g. user vector 0-31, user matrix 0-15), but there is a mapping in the shader of parameter string name <-> enum id.
so the user constants have to look like CustomFloat1, CustomMatrix2, etc?
That's what the render engine sees once they are resolved, but there is a mapping in the material file which maps custom parameter name in the shader (a string) to one of the user enum constants.
So per shader you set up that mapping, and then use custom strings/names in the shader, but they get mapped to one of the fixed user constant locations.
That's how we did it anyway, that's just one solution to the problem.
from gfx.
I've advanced with this issue quite a bit. Sorry for the wall of text... I hope to get some feedback.
Shader parameters are just a more complex case of the same problem we have with vertex attributes. Those also need to be matched against shader inputs. Comparing strings at that point is not the biggest threat - we also need to verify the semantics to match, and (more importantly) we need to iterate given resources for any requested one. So there is a total of 4 components that need to be optimized out:
- String comparison
- Iteration
- Semantic verification
- Cloning the data per draw call
The first two points apply equally to vertex attributes. Semantic verification is an open question about attributes, and we may want to add it later on. No cloning is done by the user (or render client) for vertex attributes, because they are already stored on the render thread.
Here is my proposed solution:
pub type EnvBlockHandle = u8;
pub type EnvUniformHandle = u16;
pub type EnvTextureHandle = u8;
struct Environment {
blocks: Vec<(String, device::dev::Buffer)>,
uniforms: Vec<(String, device::shade::UniformValue)>,
textures: Vec<(String, device::dev::Texture, device::dev::Sampler)>,
}
impl Environment {
pub fn new() -> Environment;
pub fn add_block(&mut self, &str, device::dev::Buffer) -> EnvBlockHandle ;
pub fn add_uniform(&mut self, &str, device::shade::UniformValue) -> EnvUniformHandle;
pub fn add_texture(&mut self, &str, device::dev::Texture, device::dev::Sampler) -> EnvTextureHandle;
}
impl render::Client {
// sends the environment for the render task to store
fn register_environment(&mut self, Environment) -> EnvironmentHandle;
fn unregister_environment(&mut self, EnvironmentHandle);
// these methods are to change existing environment variables
// passing an index instead of a full name is both cheap and DRY (yay!)
fn set_env_block(&mut self, EnvironmentHandle, EnvBlockHandle, device::dev::Buffer);
fn set_env_uniform(&mut self, EnvironmentHandle, EnvUniformHandle, device::shade::UniformValue);
fn set_env_texture(&mut self, EnvironmentHandle, EnvTextureHandle, device::dev::Texture, device::dev::Sampler);
}
Here is an example of the user code:
// at initialization
let mut env = gfx::Environment::new();
// we get a hold of this one to change it later
let var_color = env.add_uniform("color", gfx::ValueF32Vec([0.5, ..4]));
// not going to change it, so ignoring the result
env.add_texture("diffuse", my_texture_handle, my_sampler_handle);
let env_handle = renderer.register_environment(env_handle);
...
// at run-time
renderer.set_env_uniform(env_handle, var_color, my_color_value);
renderer.draw(..., env_handle);
During the rendering, render::Server
can keep the cache in the following form:
pub type EnvironmentCache = HashMap<(EnvironmentHandle, ProgramHandle), EnvironmentShortcut>;
Where the EnvironmentShortcut
has baked-in indices of all the resources that the program wants. We could find a more efficient representation for those, but for starters this should suffice:
struct EnvironmentShortcut
blocks: Vec<EnvBlockHandle>, // size equals to ProgramMeta::blocks
uniforms: Vec<EnvUniformHandle>, // size equals to ProgramMeta::uniforms
textures: Vec<EnvTextureHandle> // size equals to ProgramMeta::textures
}
If the shortcut is not found, it is constructed by looking up the corresponding parameters in the Environment
by matching names and semantics. In 99% of the draw calls the shortcut will be found, and walking it is the most efficient path to bind shader parameters. For each parameter, render thread will just jump to the value (by index) in the Environment
and bind it.
from gfx.
So is this Environment
is a builder kind of thing that can subsequently baked?
from gfx.
@bjz Baking is not the right term. Environment
is not replaced by EnvironmentShortcut
. The latter just accelerates access to the former (upon program binding) to avoid iteration, string comparisons, and semantic verification.
from gfx.
Ah ok, thanks for the clarification.
from gfx.
Here is a breakdown of our choices:
- Assign parameters per program, which will have its mutable state exposed. Cost is low, ergonomics is pretty good. Chance to shoot yourself in the foot (SYF chance) is high.
- Have Environment structure to contain all the parameters, it is partially-mutable, and can be used with multiple programs at will (that's what we have now). Cost is pretty low due to shortcut cache, ergonomics is a bit worse (due to the need to care about environment), and the SYF chance is normal (parameter values can still leak if user forgets to set them).
- Pass all the parameters with the draw call, thus making the program state immutable from the user perspective. Cost is higher, but depends on the implementation of the parameter descriptor. Cloning Environment would involve 3 heap allocations for the vectors, for example. Ergonomics is the same as in 2, and the SYF chance is low (user is in full control of the parameters)
Current consensus leans towards Choice 3, but we need to figure out how to make it performance effective (reduce the number of heap allocations and the memory to copy) and yet ergonomic.
from gfx.
Here is how Irrlicht handles it: http://irrlicht.sourceforge.net/docu/example010.html
Looks like it only supports arrays of floats, which is a bit too primitive for us... Note the IMaterialRendererServices
visitor.
from gfx.
Proposal prototype N2: https://gist.github.com/kvark/182cb352b7ef599dcdbc
Apparently, we can skip N2 and aim further (from @csherratt):
I imagine I would take the environment and take each shader uniform index and throw it into a structure on the client side.
from gfx.
Related Issues (20)
- Hitting `unreachable!` branch in `gfx-backend-vulkan` during swapchain creation, Void Linux, AMD ATI
- ```supports_family``` only available on iOS 13 HOT 4
- gfx_backend_gl and metal: can't find create HOT 3
- Link error: missing symbol _kCAGravityTopLeft on macOS HOT 3
- New Metal pipeline cache doesn't work properly on Dota2 + portability HOT 2
- Support dynamic array sizes on Metal HOT 1
- Black screen when --features=gl
- Use hasUnifiedMemory instead of isLowPower on Metal
- Entered unreachable code on vulkan create fence
- Errors from VVL on Nvidia HOT 10
- Screen doesn't get updated if gfx_device_gl::CommandBuffer::clear_color gets called HOT 1
- 3 build errors on Windows HOT 3
- LICENSE files in package subdirs
- create_surface failed with 'AndroidSurface failed: ERROR_NATIVE_WINDOW_IN_USE_KHR'
- pre-ll: Vertex & constant structs fields are re-ordered in rust 1.67 so don't work HOT 1
- Missing MESH_SHADER feature in mesh-shading example
- misaligned pointer in `queue::Queue::get` HOT 3
- pubg
- MTLGPUFamilyMac1 is deprecated HOT 1
- Tivel
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from gfx.