veloren/voxygen/src/render/texture.rs
Joshua Yanovski 866cc79d2e Enable allocation of new textures on atlas allocation failure.
This solves the problem of not being able to set the view distance too
high, especially in pathological cases like giant trees.  For
simplicity, we just freeze any atlas where allocation failed and start
allocating to a new texture and atlas, letting reference counting
destroy the old one when there are no more references to it.  Because of
the spatial locality of chunk allocations, chunks allocated together
will virtually always have similar lifetimes, so the odds of this
causing significant fragmentation are very low, meaning this simple
solution should not do much worse than a much fancier one.
2021-03-20 16:21:41 +01:00

187 lines
6.5 KiB
Rust

use super::{gfx_backend, RenderError};
use gfx::{self, traits::Factory};
use image::{DynamicImage, GenericImageView};
use vek::Vec2;
type DefaultShaderFormat = (gfx::format::R8_G8_B8_A8, gfx::format::Srgb);
/// Represents an image that has been uploaded to the GPU.
#[derive(Clone)]
pub struct Texture<F: gfx::format::Formatted = DefaultShaderFormat>
where
F::Surface: gfx::format::TextureSurface,
F::Channel: gfx::format::TextureChannel,
<F::Surface as gfx::format::SurfaceTyped>::DataType: Copy,
{
pub tex: gfx::handle::Texture<gfx_backend::Resources, <F as gfx::format::Formatted>::Surface>,
pub srv: gfx::handle::ShaderResourceView<
gfx_backend::Resources,
<F as gfx::format::Formatted>::View,
>,
pub sampler: gfx::handle::Sampler<gfx_backend::Resources>,
}
impl<F: gfx::format::Formatted> Texture<F>
where
F::Surface: gfx::format::TextureSurface,
F::Channel: gfx::format::TextureChannel,
<F::Surface as gfx::format::SurfaceTyped>::DataType: Copy,
{
pub fn new(
factory: &mut gfx_backend::Factory,
image: &DynamicImage,
filter_method: Option<gfx::texture::FilterMethod>,
wrap_mode: Option<gfx::texture::WrapMode>,
border: Option<gfx::texture::PackedColor>,
) -> Result<Self, RenderError> {
// TODO: Actualy handle images that aren't in rgba format properly.
let buffer = image.as_flat_samples_u8().ok_or_else(|| {
RenderError::CustomError(
"We currently do not support color formats using more than 4 bytes / pixel.".into(),
)
})?;
let (tex, srv) = factory
.create_texture_immutable_u8::<F>(
gfx::texture::Kind::D2(
image.width() as u16,
image.height() as u16,
gfx::texture::AaMode::Single,
),
gfx::texture::Mipmap::Provided,
// Guarenteed to be correct, since all the conversions from DynamicImage to
// FlatSamples<u8> go through the underlying ImageBuffer's implementation of
// as_flat_samples(), which guarantees that the resulting FlatSamples is
// well-formed.
&[buffer.as_slice()],
)
.map_err(RenderError::CombinedError)?;
let mut sampler_info = gfx::texture::SamplerInfo::new(
filter_method.unwrap_or(gfx::texture::FilterMethod::Scale),
wrap_mode.unwrap_or(gfx::texture::WrapMode::Clamp),
);
let transparent = [0.0, 0.0, 0.0, 1.0].into();
sampler_info.border = border.unwrap_or(transparent);
Ok(Self {
tex,
srv,
sampler: factory.create_sampler(sampler_info),
})
}
pub fn new_dynamic(
factory: &mut gfx_backend::Factory,
width: u16,
height: u16,
) -> Result<Self, RenderError> {
let tex = factory.create_texture(
gfx::texture::Kind::D2(
width,
height,
gfx::texture::AaMode::Single,
),
1_u8,
gfx::memory::Bind::SHADER_RESOURCE,
gfx::memory::Usage::Dynamic,
Some(<<F as gfx::format::Formatted>::Channel as gfx::format::ChannelTyped>::get_channel_type()),
)
.map_err(|err| RenderError::CombinedError(gfx::CombinedError::Texture(err)))?;
let srv = factory
.view_texture_as_shader_resource::<F>(&tex, (0, 0), gfx::format::Swizzle::new())
.map_err(|err| RenderError::CombinedError(gfx::CombinedError::Resource(err)))?;
Ok(Self {
tex,
srv,
sampler: factory.create_sampler(gfx::texture::SamplerInfo::new(
gfx::texture::FilterMethod::Scale,
gfx::texture::WrapMode::Clamp,
)),
})
}
pub fn new_immutable_raw(
factory: &mut gfx_backend::Factory,
kind: gfx::texture::Kind,
mipmap: gfx::texture::Mipmap,
data: &[&[<F::Surface as gfx::format::SurfaceTyped>::DataType]],
sampler_info: gfx::texture::SamplerInfo,
) -> Result<Self, RenderError> {
let (tex, srv) = factory
.create_texture_immutable::<F>(kind, mipmap, data)
.map_err(RenderError::CombinedError)?;
Ok(Self {
tex,
srv,
sampler: factory.create_sampler(sampler_info),
})
}
pub fn new_raw(
_device: &mut gfx_backend::Device,
factory: &mut gfx_backend::Factory,
kind: gfx::texture::Kind,
max_levels: u8,
bind: gfx::memory::Bind,
usage: gfx::memory::Usage,
levels: (u8, u8),
swizzle: gfx::format::Swizzle,
sampler_info: gfx::texture::SamplerInfo,
) -> Result<Self, RenderError> {
let tex = factory
.create_texture(
kind,
max_levels as gfx::texture::Level,
bind | gfx::memory::Bind::SHADER_RESOURCE,
usage,
Some(<<F as gfx::format::Formatted>::Channel as gfx::format::ChannelTyped>::get_channel_type())
)
.map_err(|err| RenderError::CombinedError(gfx::CombinedError::Texture(err)))?;
let srv = factory
.view_texture_as_shader_resource::<F>(&tex, levels, swizzle)
.map_err(|err| RenderError::CombinedError(gfx::CombinedError::Resource(err)))?;
Ok(Self {
tex,
srv,
sampler: factory.create_sampler(sampler_info),
})
}
/// Update a texture with the given data (used for updating the glyph cache
/// texture).
pub fn update(
&self,
encoder: &mut gfx::Encoder<gfx_backend::Resources, gfx_backend::CommandBuffer>,
offset: [u16; 2],
size: [u16; 2],
data: &[<F::Surface as gfx::format::SurfaceTyped>::DataType],
) -> Result<(), RenderError> {
let info = gfx::texture::ImageInfoCommon {
xoffset: offset[0],
yoffset: offset[1],
zoffset: 0,
width: size[0],
height: size[1],
depth: 0,
format: (),
mipmap: 0,
};
encoder
.update_texture::<<F as gfx::format::Formatted>::Surface, F>(
&self.tex, None, info, data,
)
.map_err(RenderError::TexUpdateError)
}
/// Get dimensions of the represented image.
pub fn get_dimensions(&self) -> Vec2<u16> {
let (w, h, ..) = self.tex.get_info().kind.get_dimensions();
Vec2::new(w, h)
}
}