sim2d/streaming_renderer/texture/
atlas.rs

1use {
2    super::Texture,
3    anyhow::{Context, Result},
4    ash::vk,
5    demo_vk::graphics::vulkan::{VulkanContext, raii},
6    std::sync::Arc,
7};
8
9/// Used to constrain the variable descriptor array and pool sizes. This is well
10/// below the 500_000 limit imposed by the spec for the minimum supported number
11/// of UPDATE_AFTER_BIND descriptors in a descriptor poolw.
12const MAX_TEXTURES: u32 = 10_000;
13
14/// The TextureAtlas maintains the per-frame descriptor sets used for binding
15/// textures.
16///
17/// New textures can be added to the atlas at any time and will be available for
18/// use in the next frame.
19///
20/// # Shader Requirements
21///
22/// To use the texture atlas, the fragment shader must include a descriptor set
23/// with a sampler binding and a texture binding. This looks something like this
24/// in glsl:
25///
26/// ```glsl
27/// layout(set = 0, binding = 0) uniform sampler u_Sampler;
28/// layout(set = 0, binding = 1) uniform texture2D u_Textures[];
29/// ```
30pub struct TextureAtlas {
31    // All textures currently in-use by the atlas.
32    textures: Vec<Arc<Texture>>,
33
34    // The sampler used when reading textures.
35    _sampler: raii::Sampler,
36
37    descriptor_set_layout: raii::DescriptorSetLayout,
38    descriptor_set: vk::DescriptorSet,
39    _descriptor_pool: raii::DescriptorPool,
40}
41
42impl TextureAtlas {
43    pub fn new(ctx: &VulkanContext) -> Result<Self> {
44        let sampler = raii::Sampler::new(
45            "TextureAtlas Immutable Sampler",
46            ctx.device.clone(),
47            &vk::SamplerCreateInfo {
48                mag_filter: vk::Filter::LINEAR,
49                min_filter: vk::Filter::LINEAR,
50                mipmap_mode: vk::SamplerMipmapMode::LINEAR,
51                address_mode_u: vk::SamplerAddressMode::CLAMP_TO_EDGE,
52                address_mode_v: vk::SamplerAddressMode::CLAMP_TO_EDGE,
53                address_mode_w: vk::SamplerAddressMode::CLAMP_TO_EDGE,
54                mip_lod_bias: 0.0,
55                anisotropy_enable: vk::FALSE,
56                max_anisotropy: 0.0,
57                compare_enable: vk::FALSE,
58                compare_op: vk::CompareOp::ALWAYS,
59                min_lod: 0.0,
60                max_lod: vk::LOD_CLAMP_NONE,
61                border_color: vk::BorderColor::FLOAT_OPAQUE_WHITE,
62                unnormalized_coordinates: vk::FALSE,
63                ..Default::default()
64            },
65        )
66        .context("Unable to create texture atlas sampler!")?;
67
68        let descriptor_set_layout = {
69            let bindings = [
70                vk::DescriptorSetLayoutBinding {
71                    binding: 0,
72                    descriptor_type: vk::DescriptorType::SAMPLER,
73                    descriptor_count: 1,
74                    stage_flags: vk::ShaderStageFlags::FRAGMENT,
75                    p_immutable_samplers: &sampler.raw,
76                    ..Default::default()
77                },
78                vk::DescriptorSetLayoutBinding {
79                    binding: 1,
80                    descriptor_type: vk::DescriptorType::SAMPLED_IMAGE,
81                    descriptor_count: MAX_TEXTURES,
82                    stage_flags: vk::ShaderStageFlags::FRAGMENT,
83                    p_immutable_samplers: std::ptr::null(),
84                    _marker: std::marker::PhantomData,
85                },
86            ];
87            let binding_flags = [
88                vk::DescriptorBindingFlags::empty(),
89                vk::DescriptorBindingFlags::PARTIALLY_BOUND
90                    | vk::DescriptorBindingFlags::VARIABLE_DESCRIPTOR_COUNT
91                    | vk::DescriptorBindingFlags::UPDATE_AFTER_BIND
92                    | vk::DescriptorBindingFlags::UPDATE_UNUSED_WHILE_PENDING,
93            ];
94            let mut binding_flags_create_info =
95                vk::DescriptorSetLayoutBindingFlagsCreateInfo {
96                    binding_count: binding_flags.len() as u32,
97                    p_binding_flags: binding_flags.as_ptr(),
98                    ..Default::default()
99                };
100            let create_info = vk::DescriptorSetLayoutCreateInfo {
101                flags:
102                    vk::DescriptorSetLayoutCreateFlags::UPDATE_AFTER_BIND_POOL,
103                binding_count: bindings.len() as u32,
104                p_bindings: bindings.as_ptr(),
105                ..Default::default()
106            }
107            .push_next(&mut binding_flags_create_info);
108            raii::DescriptorSetLayout::new(
109                "TextureAtlas Descriptor Set layout",
110                ctx.device.clone(),
111                &create_info,
112            )
113            .context("Unable to create TextureAtlas descriptor set layout!")?
114        };
115        let descriptor_pool = {
116            let pool_sizes = [
117                vk::DescriptorPoolSize {
118                    ty: vk::DescriptorType::SAMPLER,
119                    descriptor_count: 1,
120                },
121                vk::DescriptorPoolSize {
122                    ty: vk::DescriptorType::SAMPLED_IMAGE,
123                    descriptor_count: MAX_TEXTURES,
124                },
125            ];
126            raii::DescriptorPool::new(
127                "TextureAtlas DescriptorPool",
128                ctx.device.clone(),
129                &vk::DescriptorPoolCreateInfo {
130                    flags: vk::DescriptorPoolCreateFlags::UPDATE_AFTER_BIND,
131                    max_sets: 1,
132                    pool_size_count: pool_sizes.len() as u32,
133                    p_pool_sizes: pool_sizes.as_ptr(),
134                    ..Default::default()
135                },
136            )
137            .context("Error creating TextureAtlas descriptor pool!")?
138        };
139        let descriptor_set = {
140            let counts = [MAX_TEXTURES];
141            let mut descriptor_set_variable_descriptor_count_allocate_info =
142                vk::DescriptorSetVariableDescriptorCountAllocateInfo {
143                    descriptor_set_count: 1,
144                    p_descriptor_counts: counts.as_ptr(),
145                    ..Default::default()
146                };
147            let allocate_info = vk::DescriptorSetAllocateInfo {
148                descriptor_pool: descriptor_pool.raw,
149                descriptor_set_count: 1,
150                p_set_layouts: &descriptor_set_layout.raw,
151                ..Default::default()
152            }
153            .push_next(
154                &mut descriptor_set_variable_descriptor_count_allocate_info,
155            );
156            unsafe {
157                ctx.allocate_descriptor_sets(&allocate_info).context(
158                    "Unable to allocate TextureAtlas descriptor set!",
159                )?[0]
160            }
161        };
162
163        Ok(Self {
164            textures: vec![],
165            _sampler: sampler,
166            descriptor_set_layout,
167            _descriptor_pool: descriptor_pool,
168            descriptor_set,
169        })
170    }
171
172    pub fn descriptor_set_layout(&self) -> &raii::DescriptorSetLayout {
173        &self.descriptor_set_layout
174    }
175
176    /// Returns the atlas's descriptor set.
177    ///
178    /// Used by the application to bind the atlas descriptor.
179    pub fn descriptor_set(&self) -> vk::DescriptorSet {
180        self.descriptor_set
181    }
182
183    /// Adds a texture to the atlas and returns the index which can be used in
184    /// vertices to reference the texture again.
185    pub fn add_texture(
186        &mut self,
187        ctx: &VulkanContext,
188        texture: Arc<Texture>,
189    ) -> i32 {
190        let texture_index = self.textures.len() as i32;
191        let view = texture.view().raw;
192        self.textures.push(texture);
193
194        // Safe because this only writes to the newest texture index, which was
195        // never in use until after this method returns it.
196        unsafe {
197            let image_info = vk::DescriptorImageInfo {
198                sampler: vk::Sampler::null(),
199                image_view: view,
200                image_layout: vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
201            };
202            ctx.update_descriptor_sets(
203                &[vk::WriteDescriptorSet {
204                    dst_set: self.descriptor_set,
205                    dst_binding: 1,
206                    dst_array_element: texture_index as u32,
207                    descriptor_count: 1,
208                    descriptor_type: vk::DescriptorType::SAMPLED_IMAGE,
209                    p_image_info: &image_info,
210                    p_buffer_info: std::ptr::null(),
211                    p_texel_buffer_view: std::ptr::null(),
212                    ..Default::default()
213                }],
214                &[],
215            );
216        }
217
218        texture_index
219    }
220}