1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
//! Traits and implementations for managing a texture atlas.
//!
//! # Big Idea
//!
//! A shader can define code like:
//!
//! ```glsl
//! layout(binding = 1) uniform sampler2D textures[MAX_TEXTURES];
//! ```
//!
//! Which describes an array of textures. Textures in the array can all have
//! different sizes and bindings. Individual draw calls can index into the
//! array using uniform buffers or push constants.
//!
//! The appeal is that the entire texture array only needs to be bound once
//! for the entire frame.

mod atlas_version;
mod gpu_atlas;
mod sampler_handle;
mod texture_handle;

pub use self::{
    atlas_version::AtlasVersion, gpu_atlas::GpuAtlas,
    sampler_handle::SamplerHandle, texture_handle::TextureHandle,
};

use crate::graphics::Graphics;

use anyhow::Result;
use ash::{version::DeviceV1_0, vk};

use super::vulkan::texture::TextureImage;

/// The maximum number of textures which can be managed by any given texture
/// atlas.
pub const MAX_SUPPORTED_TEXTURES: usize = 64;

/// A type which owns a collection of texture objects that can be bound once
/// per frame and individually accessed in calls to `vkDraw`.
pub trait TextureAtlas {
    /// The atlas's current version.
    fn version(&self) -> AtlasVersion;

    /// Build the array of descriptor image info objects which can be used to
    /// write all of this atlas's textures into a descriptor set.
    fn build_descriptor_image_info(&self) -> Vec<vk::DescriptorImageInfo>;

    /// Add a named sampler to the atlas. Samplers can be persistently bound to
    /// individual textures.
    fn add_sampler(&mut self, sampler: vk::Sampler) -> Result<SamplerHandle>;

    /// Add a texture to the atlas. The atlas owns the texture and will destroy
    /// it when the atlas is dropped.
    fn add_texture(&mut self, texture: TextureImage) -> Result<TextureHandle>;

    /// Take ownership of a texture owned by this atlas.
    ///
    /// # Unsafe Because
    ///
    /// - the caller must make sure that the texture atlas is not in use when
    ///   this method is called
    unsafe fn take_texture(
        &mut self,
        texture_handle: TextureHandle,
    ) -> Result<TextureImage>;

    /// Bind a sampler to a texture. Binding are persistent - they do not change
    /// until this method is called again.
    fn bind_sampler_to_texture(
        &mut self,
        sampler_handle: SamplerHandle,
        texture_handle: TextureHandle,
    ) -> Result<()>;
}

impl TextureAtlas for Graphics {
    fn version(&self) -> AtlasVersion {
        self.texture_atlas.version()
    }

    fn build_descriptor_image_info(&self) -> Vec<vk::DescriptorImageInfo> {
        self.texture_atlas.build_descriptor_image_info()
    }

    fn add_sampler(&mut self, sampler: vk::Sampler) -> Result<SamplerHandle> {
        self.texture_atlas.add_sampler(sampler)
    }

    fn bind_sampler_to_texture(
        &mut self,
        sampler_handle: SamplerHandle,
        texture_handle: TextureHandle,
    ) -> Result<()> {
        self.texture_atlas
            .bind_sampler_to_texture(sampler_handle, texture_handle)
    }

    fn add_texture(&mut self, texture: TextureImage) -> Result<TextureHandle> {
        self.texture_atlas.add_texture(texture)
    }

    /// This implementation is generally SAFE because it forces the device to
    /// idle prior to removing the texture.
    unsafe fn take_texture(
        &mut self,
        texture_handle: TextureHandle,
    ) -> Result<TextureImage> {
        self.device.logical_device.device_wait_idle()?;
        self.texture_atlas.take_texture(texture_handle)
    }
}