-
Notifications
You must be signed in to change notification settings - Fork 437
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Runtime VertexAttributes #736
Comments
For the graphics pipeline it is possible by writing your own implementation of the VertexDefinition trait. This trait is a bit crappy though. For the buffer, if everything is a float you can simply create a buffer of type Also note that for the moment the content of the vertex buffer is not checked. In other words you can use any buffer of any content in combination with any vertex definition and will not get an error. That eliminates a potential problem. |
Slightly off-topic but I wanted to write a glTF loader as well, so I'll probably encounter the same problems. |
Let me piggy back this issue: I was looking for a nice way to leverage runtime vertex attributes as well. When I tried to implement runtime vertex attributes, I ended up with a #[derive(Hash, Eq, PartialEq, Debug)]
pub struct VertexAttribute {
pub name: Cow<'static, str>,
pub format: Format,
}
pub struct Mesh {
attributes: HashMap<VertexAttribute, Arc<dyn BufferAccess>>,
length: u64,
} This is similar to how other higher level graphics APIs I know handled it, basically attributes are declared with their name and format, e.g.: const ATTRIBUTE_POSITION: VertexAttribute = VertexAttribute::new("position", Format::R32G32_SFLOAT);
const ATTRIBUTE_COLOR: VertexAttribute = VertexAttribute::new("color", Format::R32G32B32_SFLOAT); And can then be used to construct a mesh using a builder pattern, something akin to this: let mesh = MeshBuilder::new(&memory_allocator)
.add(
ATTRIBUTE_POSITION,
vec![
Vec2::new(-0.5, -0.25),
Vec2::new(0.0, 0.5),
Vec2::new(0.25, -0.1),
],
)
.build(); As only the As we have non-interleaved buffers, we can now use a "generalized" VertexDefintion, such as: pub struct NonInterleavedInput {}
impl NonInterleavedInput {
pub fn new() -> Self {
Self {}
}
// TODO: allow marking of bindings as per-instance
}
unsafe impl VertexDefinition for NonInterleavedInput {
#[inline]
fn definition(
&self,
interface: &ShaderInterface,
) -> Result<VertexInputState, IncompatibleVertexDefinitionError> {
let mut attributes: Vec<(u32, VertexInputAttributeDescription)> = Vec::new();
let mut bindings: HashMap<u32, VertexInputBindingDescription> = HashMap::new();
for element in interface.elements() {
tracing::debug!("creating binding and attribute for element: {:?}", element,);
bindings.insert(
element.location,
VertexInputBindingDescription {
stride: element.ty.num_components * element.ty.num_elements * 4,
input_rate: VertexInputRate::Vertex,
},
);
attributes.push((
element.location,
VertexInputAttributeDescription {
binding: element.location,
format: to_format(&element.ty), // to_format helper function left out for brevity
offset: 0_u32,
},
));
}
tracing::debug!(
"resulting vertex input state has bindings: {:?} and attributes: {:?}",
bindings,
attributes
);
Ok(VertexInputState::new()
.bindings(bindings)
.attributes(attributes))
}
} However we require the impl Mesh {
pub fn buffers(&self, interface: &ShaderInterface) -> Vec<Arc<dyn BufferAccess>> {
let bufs_len = interface.elements().len();
let mut bufs: Vec<Option<Arc<dyn BufferAccess>>> = Vec::with_capacity(bufs_len);
bufs.resize(bufs_len, None);
for element in interface.elements() {
let name = element.name.as_ref().unwrap();
if let Some(buf) = self.attributes.get(&VertexAttribute {
name: Cow::Owned(name.clone().into_owned()),
format: to_format(&element.ty),
}) {
bufs[element.location as usize] = Some(buf.clone());
}
}
bufs.iter()
.map(|maybe_buf| maybe_buf.clone().unwrap())
.collect() // TODO: check if all required bindings are available
}
pub fn len(&self) -> u64 {
self.length
}
} It can be used in a pipeline as follows: .bind_pipeline_graphics(pipeline.clone())
.bind_vertex_buffers(0, mesh.buffers(vs.entry_point().input_interface()))
.draw(mesh.len() as u32, 1, 0, 0) The above approach is quite hacky and I am not certain about the safety, so some feedback and review of the above approach would be highly appreciated. This leads me to a second point, that would make the above nicer to implement. Currently there is no way to retrieve the I would propose to store the shader's P.S. any feedback welcome :) |
Can you create a
GraphicsPipeline
or aBuffer
when you only know the vertex attributes at runtime? I see there is an example for loading shaders at runtime, but the Vertex data is still specified in the code.Say someone wants to write a viewer for GLTF 2.0 scenes. The spec says meshes can have any of eight different vertex attributes (POSITION, NORMAL, TANGENT, TEXCOORD_0, TEXCOORD_1, COLOR_0, JOINTS_0, WEIGHTS_0) in any order (for now, let's assume they are all interleaved in one buffer). Accounting for all arrangements would require defining
floor(8! * e) - 1 = 109,600
structs. There HAS to be a better way to do this!The text was updated successfully, but these errors were encountered: