You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -69,28 +69,28 @@ var shaderMaterial = new THREE.MeshShaderMaterial({
69
69
vertexShader:vShader.text(),
70
70
fragmentShader:fShader.text()
71
71
});
72
-
{% endhighlight %}
72
+
```
73
73
* gfx-rs (for comparison)
74
-
{% highlight Rust %}
74
+
```rust
75
75
#[shader_param(MyBatch)]
76
76
structParams {
77
77
color: [f32, ..4],
78
78
}
79
79
letdata=Params {
80
80
color: [0.0, 0.0, 0.0, 1.0],
81
81
};
82
-
{% endhighlight %}
82
+
```
83
83
84
84
### SYF 101
85
85
SYF: Shoot Yourself in the Foot = "to do or say something that causes problems for you".
86
86
87
87
Notice how almost every implementation requires you to specify the parameter name as a string. This forces the engine to go through all known parameters and compare them with your string. Obviously, this work is wasted for any subsequent calls. It is also a violation of the [DRY](http://en.wikipedia.org/wiki/Don%27t_repeat_yourself) principle and a potential hazard: every time you ask to match the parameter by name, there is a chance of error (parameter not found because you copy-pasted the name wrong?).
88
88
89
89
In some engines, you can get a handle to the parameter like this:
This is a bit more verbose, and partially solves the problem, but clearly "color" is still repeated twice here (as a string and a part of the variable name). Besides, another hazard remains - what if a given parameter has an incompatible type with what shader expects?
95
95
96
96
Three.js comes the closest to being safe - your variable name is used to query the shader, and the type can be verified inside `MeshShaderMaterial` call. Note, however, that in _JavaScript_ you can change the variable type at run-time, which raises the SYF factor significantly.
@@ -104,14 +104,14 @@ We are using a procedural macro in Rust to generate the following code at compil
104
104
3. Implementation of `fill_params()` - a function that fills up the parameter value, which can be uploaded to GPU.
105
105
This is all done behind the `shader_param` attribute:
106
106
4. Creates a type alias to the `RefBatch<L ,T>`, named `MyBatch` (see the macro parameter).
107
-
{% highlight Rust %}
107
+
```rust
108
108
#[shader_param(MyBatch)]
109
109
structMyParam {
110
110
color: [f32, ..4],
111
111
}
112
-
{% endhighlight %}
112
+
```
113
113
Generated code:
114
-
{% highlight Rust %}
114
+
```rust
115
115
structMyParam {
116
116
color: [f32, ..4],
117
117
}
@@ -147,24 +147,24 @@ impl ::gfx::shade::ShaderParam<_MyParamLink> for MyParam {
147
147
}
148
148
}
149
149
}
150
-
{% endhighlight %}
150
+
```
151
151
152
152
The end product of this macro is a `MyBatch` type that we can use to create batches with `MyParam` parameters:
153
-
{% highlight Rust %}
153
+
```rust
154
154
letbatch:MyBatch=context.batch(...).unwrap();
155
-
{% endhighlight %}
155
+
```
156
156
The `unwrap()` here ignores these possible errors (listing only those related to shader parameters):
157
157
* the structure doesn't provide a parameter that shader needs
158
158
* a shader parameter is not covered by the structure
159
159
* some parameter type is not compatible between the shader and the structure
160
160
161
161
Later, you provide the `MyParam` instance by reference for every draw call with this batch:
162
-
{% highlight Rust %}
162
+
```rust
163
163
letdata=MyParam {
164
164
color: [0.0, 0.0, 1.0, 0.0],
165
165
};
166
166
renderer.draw((&batch, &context, &data), &frame);
167
-
{% endhighlight %}
167
+
```
168
168
Notice that the data is decoupled from the batch right until the draw call, yet the batch has all the type guarantees about data safety of the shader parameters, thus `draw()` can not fail.
Copy file name to clipboardExpand all lines: _posts/2015-03-01-device.md
+6-6Lines changed: 6 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -11,7 +11,7 @@ An abstract device has different types associated with it: buffer, texture, samp
11
11
2. In the resource management routines (`create_buffer()` and such), which belong to the device.
12
12
13
13
Since both `CommandBuffer` and the `Device` needed access to all the resource types, we decided to enclose them into a separate `Resources` trait, which became an associated type for our command buffers and devices:
The GL backend implements `Resources` with a phantom enum:
42
-
{% highlight Rust %}
42
+
```rust
43
43
#[derive(Copy, Clone, PartialEq, Debug)]
44
44
pubenumGlResources {}
45
45
@@ -53,7 +53,7 @@ impl Resources for GlResources {
53
53
typeTexture=Texture;
54
54
typeSampler=Sampler;
55
55
}
56
-
{% endhighlight %}
56
+
```
57
57
58
58
59
59
The migration was started by @bjz in a long series of pull requests ([#564](https://github.com/gfx-rs/gfx-rs/issues/564), [#589](https://github.com/gfx-rs/gfx-rs/issues/589), [#590](https://github.com/gfx-rs/gfx-rs/issues/590), [#592](https://github.com/gfx-rs/gfx-rs/issues/592), [#593](https://github.com/gfx-rs/gfx-rs/issues/593), [#597](https://github.com/gfx-rs/gfx-rs/issues/597)) and finished by @kvark ([`gfx-rs`/#598](https://github.com/gfx-rs/gfx-rs/pull/598), [`gfx-rs`/#609](https://github.com/gfx-rs/gfx-rs/pull/609), [`gfx_device_gl`/#1](https://github.com/gfx-rs/gfx_device_gl/pull/1)). The result - [`gfx_device_gl`](https://github.com/gfx-rs/gfx_device_gl) is a separate crate now that `gfx` itself doesn't depend on. Examples are moved into [their own home](https://github.com/gfx-rs/gfx_examples) as well. Even the macro-generated implementations for shader parameters and vertex formats are API-independent now.
The `gfx_pipeline_base` macro generates a trio of `Data`, `Meta`, and `Init` structs (inside the new module `pipe`) from this definiton, where the user-specified types are exactly what the `Meta` part is going to have. The other two generated structs will look like this:
Since the `Init` one will need to be passed for PSO construction by the user, it's clear that our solution doesn't require (or even allow) the user to specify any redundant information, or miss anything. Let's say the user wants to enable blending for the `pixel_color`. All that needs to be done is changing the meta type from `RenderTarget<T>` into `BlendTarget<T>`. This would not change the corresponding `Data` component (which will still be `RenderTargetView<R, T>`), but it would add a mandatory `gfx::state::Blend` to the `Init` struct field.
54
54
55
55
### In-place Init
56
56
For simple applications the PSO initialization format is often known in advance. To save the user from a burden of instantiating the `Init` struct, we provide a convenient extended macro to specify the values in place:
This extended version will also generate the trio of structs, but in addition have the `Init::new()` method to give you an instance right away! Here is an example code for PSO creation after this macro is invoked:
71
-
{% highlight Rust %}
71
+
```rust
72
72
letpso=factory.create_pipeline_state( // defined in `gfx::traits::FactoryExt`
73
73
&shaders, // `gfx::ShaderSet<R>` - has all the shaders
letpso=factory.create_pipeline_simple( // defined in `gfx::traits::FactoryExt`
82
82
&vertex_shader, &fragment_shader,
83
83
gfx::state::CullFace::Nothing, Init::new()
84
84
).unwrap();
85
-
{% endhighlight %}
85
+
```
86
86
87
87
### Drawing
88
88
The resulting type will be `gfx::PipelineState<pipe::Meta>`, but fortunately the compiler can infer that for you. Using this PSO is rather trivial - the user is only required to construct the `Data` portion themselves:
89
-
{% highlight Rust %}
89
+
```rust
90
90
letdata=pipe::Data {
91
91
vertex:.., const_locals:..,
92
92
tex_diffuse:.., buf_noise:..,
93
93
pixel_color:.., depth:..,
94
94
};
95
95
letslice=gfx::mesh::Slice {...};
96
96
encoder.draw(&slice, &pso, &data);
97
-
{% endhighlight %}
97
+
```
98
98
This is rather minimalistic, but, more importantly, there is next to no opportunity to shoot yourself in the foot! All the inputs of the PSO are guaranteed at compile time. Performance-wise the solution is also perfect - all the mapping from the user data and the PSO inputs is already ready for us (contained in the `Meta`, which is constructed during PSO creation), so the actual binding doesn't involve any lookups.
99
99
100
100
### Structure
101
101
Some PSO components operate on structured data. Namely, vertex buffers and constant buffers are supposed to map to Rust structures. In order to assist the user in defining one, we have a special macro:
102
-
{% highlight Rust %}
102
+
```rust
103
103
gfx_vertex_struct!(Vertex {
104
104
x@_x:i8,
105
105
y@_y:f32,
106
106
//shader_name@ field_name: type,
107
107
});
108
-
{% endhighlight %}
108
+
```
109
109
The macro will create `Vertex` struct that implements `gfx::Structure<gfx::format::Format>`, allowing it to be used as a generic parameter to vertex buffers. The rust fields are then guaranteed to map the corresponding shader fields, at run-time during PSO creation.
110
110
111
111
A similar macro is introduced for the constant buffers, it implements `gfx::Structure<gfx::shade::ConstFormat>`:
112
-
{% highlight Rust %}
112
+
```rust
113
113
gfx_constant_struct!(Local {
114
114
x:i8,
115
115
y:f32,
116
116
});
117
-
{% endhighlight %}
117
+
```
118
118
119
119
## Analysis
120
120
PSO is the new paradigm of Gfx-rs interface. It deprecates a lot of the old core concepts (`Batch`, `Output`, `Mesh`, and others), and cleans up the internal structure quite a bit. The [implementation](https://github.com/gfx-rs/gfx/pull/828) can almost be seen as a rewrite of Gfx-rs as we knew it. Moreover, it is perfectly compatible with DX12 and Metal, while still allowing older APIs to emulate PSOs efficiently (see our GL backend).
0 commit comments