glium/texture/
bindless.rs

1/*!
2
3Without bindless textures, using a texture in a shader requires binding the texture to a specific
4bind point before drawing. This not only slows down rendering, but may also prevent you from
5grouping multiple draw calls into one because of the limitation to the number of available
6texture units.
7
8Instead, bindless textures allow you to manually manipulate pointers to textures in video memory.
9You can use thousands of textures if you want.
10
11# Initialization
12
13Before using a bindless texture, you must turn it into a `ResidentTexture`. This is done by
14calling `resident` on the texture you want.
15
16Bindless textures are a very recent feature that is supported only by recent hardware and
17drivers. `resident` will return an `Err` if this feature is not supported.
18
19```no_run
20# use glutin::surface::{ResizeableSurface, SurfaceTypeTrait};
21# fn example<T>(display: glium::Display<T>, texture: glium::texture::Texture2d)
22#     where T: SurfaceTypeTrait + ResizeableSurface {
23let texture = texture.resident().unwrap();
24# }
25```
26
27In a real application, you will likely manage a `Vec<ResidentTexture>`.
28
29# Usage
30
31You can then use a `TextureHandle` as if it was a pointer to a texture. A `TextureHandle` can be
32built from a `&ResidentTexture` and can't outlive it.
33
34```no_run
35#[macro_use]
36extern crate glium;
37
38# fn main() {
39#[derive(Copy, Clone)]
40struct UniformBuffer<'a> {
41    texture: glium::texture::TextureHandle<'a>,
42    some_value: f32,
43}
44
45implement_uniform_block!(UniformBuffer<'a>, texture, some_value);
46
47# use glutin::surface::{ResizeableSurface, SurfaceTypeTrait};
48# fn example<T>(display: glium::Display<T>, texture: glium::texture::bindless::ResidentTexture)
49#     where T: SurfaceTypeTrait + ResizeableSurface {
50let uniform_buffer = glium::uniforms::UniformBuffer::new(&display, UniformBuffer {
51    texture: glium::texture::TextureHandle::new(&texture, &Default::default()),
52    some_value: 5.0,
53});
54# }
55# }
56```
57
58Inside your shader, you can refer to the texture with a traditional `sampler*` variable. Glium
59currently doesn't check whether the type of your texture matches the expected type (but it may
60do in the future). Binding the wrong type of texture may lead to undefined values when sampling
61the texture.
62
63*/
64use crate::texture::any::TextureAny;
65use crate::TextureExt;
66use crate::GlObject;
67
68use crate::ContextExt;
69use crate::gl;
70
71use std::marker::PhantomData;
72use std::ops::{Deref, DerefMut};
73
74use crate::program::BlockLayout;
75use crate::uniforms::AsUniformValue;
76use crate::uniforms::LayoutMismatchError;
77use crate::uniforms::UniformBlock;
78use crate::uniforms::UniformValue;
79use crate::uniforms::UniformType;
80use crate::uniforms::SamplerBehavior;
81
82/// A texture that is resident in video memory. This allows you to use bindless textures in your
83/// shaders.
84pub struct ResidentTexture {
85    texture: Option<TextureAny>,
86    handle: gl::types::GLuint64,
87}
88
89impl ResidentTexture {
90    /// Takes ownership of the given texture and makes it resident.
91    // TODO: sampler
92    pub fn new(texture: TextureAny) -> Result<ResidentTexture, BindlessTexturesNotSupportedError> {
93        let handle = {
94            let mut ctxt = texture.get_context().make_current();
95
96            if !ctxt.extensions.gl_arb_bindless_texture {
97                return Err(BindlessTexturesNotSupportedError);
98            }
99
100            let handle = unsafe { ctxt.gl.GetTextureHandleARB(texture.get_id()) };
101            unsafe { ctxt.gl.MakeTextureHandleResidentARB(handle) };
102            ctxt.resident_texture_handles.push(handle);
103            handle
104        };
105
106        // store the handle in the context
107        Ok(ResidentTexture {
108            texture: Some(texture),
109            handle,
110        })
111    }
112
113    /// Unwraps the texture and restores it.
114    #[inline]
115    pub fn into_inner(mut self) -> TextureAny {
116        self.into_inner_impl()
117    }
118
119    /// Implementation of `into_inner`. Also called by the destructor.
120    fn into_inner_impl(&mut self) -> TextureAny {
121        let texture = self.texture.take().unwrap();
122
123        {
124            let mut ctxt = texture.get_context().make_current();
125            unsafe { ctxt.gl.MakeTextureHandleNonResidentARB(self.handle) };
126            ctxt.resident_texture_handles.retain(|&t| t != self.handle);
127        }
128
129        texture
130    }
131}
132
133impl Deref for ResidentTexture {
134    type Target = TextureAny;
135
136    #[inline]
137    fn deref(&self) -> &TextureAny {
138        self.texture.as_ref().unwrap()
139    }
140}
141
142impl DerefMut for ResidentTexture {
143    #[inline]
144    fn deref_mut(&mut self) -> &mut TextureAny {
145        self.texture.as_mut().unwrap()
146    }
147}
148
149impl Drop for ResidentTexture {
150    #[inline]
151    fn drop(&mut self) {
152        self.into_inner_impl();
153    }
154}
155
156/// Represents a handle to a texture. Contains a raw pointer to a texture that is hidden from you.
157#[derive(Copy, Clone)]
158pub struct TextureHandle<'a> {
159    value: gl::types::GLuint64,
160    marker: PhantomData<&'a ResidentTexture>,
161}
162
163impl<'a> TextureHandle<'a> {
164    /// Builds a new handle.
165    #[inline]
166    pub fn new(texture: &'a ResidentTexture, _: &SamplerBehavior) -> TextureHandle<'a> {
167        // FIXME: take sampler into account
168        TextureHandle {
169            value: texture.handle,
170            marker: PhantomData,
171        }
172    }
173
174    /// Sets the value to the given texture.
175    #[inline]
176    pub fn set(&mut self, texture: &'a ResidentTexture, _: &SamplerBehavior) {
177        // FIXME: take sampler into account
178        self.value = texture.handle;
179    }
180}
181
182impl<'a> AsUniformValue for TextureHandle<'a> {
183    #[inline]
184    fn as_uniform_value(&self) -> UniformValue<'_> {
185        // TODO: u64
186        unimplemented!();
187    }
188}
189
190impl<'a> UniformBlock for TextureHandle<'a> {
191    fn matches(layout: &BlockLayout, base_offset: usize)
192               -> Result<(), LayoutMismatchError>
193    {
194        if let BlockLayout::BasicType { ty, offset_in_buffer } = *layout {
195            // TODO: unfortunately we have no idea what the exact type of this handle is
196            //       strong typing should be considered
197            //
198            //       however there is no safety problem here ; the worse that can happen in case of
199            //       wrong type is zeroes or undefined data being returned when sampling
200            match ty {
201                UniformType::Sampler1d => (),
202                UniformType::ISampler1d => (),
203                UniformType::USampler1d => (),
204                UniformType::Sampler2d => (),
205                UniformType::ISampler2d => (),
206                UniformType::USampler2d => (),
207                UniformType::Sampler3d => (),
208                UniformType::ISampler3d => (),
209                UniformType::USampler3d => (),
210                UniformType::Sampler1dArray => (),
211                UniformType::ISampler1dArray => (),
212                UniformType::USampler1dArray => (),
213                UniformType::Sampler2dArray => (),
214                UniformType::ISampler2dArray => (),
215                UniformType::USampler2dArray => (),
216                UniformType::SamplerCube => (),
217                UniformType::ISamplerCube => (),
218                UniformType::USamplerCube => (),
219                UniformType::Sampler2dRect => (),
220                UniformType::ISampler2dRect => (),
221                UniformType::USampler2dRect => (),
222                UniformType::Sampler2dRectShadow => (),
223                UniformType::SamplerCubeArray => (),
224                UniformType::ISamplerCubeArray => (),
225                UniformType::USamplerCubeArray => (),
226                UniformType::SamplerBuffer => (),
227                UniformType::ISamplerBuffer => (),
228                UniformType::USamplerBuffer => (),
229                UniformType::Sampler2dMultisample => (),
230                UniformType::ISampler2dMultisample => (),
231                UniformType::USampler2dMultisample => (),
232                UniformType::Sampler2dMultisampleArray => (),
233                UniformType::ISampler2dMultisampleArray => (),
234                UniformType::USampler2dMultisampleArray => (),
235                UniformType::Sampler1dShadow => (),
236                UniformType::Sampler2dShadow => (),
237                UniformType::SamplerCubeShadow => (),
238                UniformType::Sampler1dArrayShadow => (),
239                UniformType::Sampler2dArrayShadow => (),
240                UniformType::SamplerCubeArrayShadow => (),
241
242                _ => return Err(LayoutMismatchError::TypeMismatch {
243                    expected: ty,
244                    obtained: UniformType::Sampler2d,       // TODO: wrong
245                })
246            }
247
248            if offset_in_buffer != base_offset {
249                return Err(LayoutMismatchError::OffsetMismatch {
250                    expected: offset_in_buffer,
251                    obtained: base_offset,
252                });
253            }
254
255            Ok(())
256
257        } else if let BlockLayout::Struct { members } = layout {
258            if members.len() == 1 {
259                <TextureHandle<'_> as UniformBlock>::matches(&members[0].1, base_offset)
260
261            } else {
262                Err(LayoutMismatchError::LayoutMismatch {
263                    expected: layout.clone(),
264                    obtained: BlockLayout::BasicType {
265                        ty: UniformType::Sampler2d,       // TODO: wrong
266                        offset_in_buffer: base_offset,
267                    }
268                })
269            }
270
271        } else {
272            Err(LayoutMismatchError::LayoutMismatch {
273                expected: layout.clone(),
274                obtained: BlockLayout::BasicType {
275                    ty: UniformType::Sampler2d,       // TODO: wrong
276                    offset_in_buffer: base_offset,
277                }
278            })
279        }
280    }
281
282    #[inline]
283    fn build_layout(base_offset: usize) -> BlockLayout {
284        BlockLayout::BasicType {
285            ty: UniformType::Sampler2d,       // TODO: wrong
286            offset_in_buffer: base_offset,
287        }
288    }
289}
290
291// TODO: implement `vertex::Attribute` on `TextureHandle`
292
293/// Bindless textures are not supported.
294#[derive(Debug, Copy, Clone)]
295pub struct BindlessTexturesNotSupportedError;
296
297#[cfg(test)]
298mod test {
299    use std::mem;
300    use super::TextureHandle;
301
302    #[test]
303    fn texture_handle_size() {
304        assert_eq!(mem::size_of::<TextureHandle<'_>>(), 8);
305    }
306}