227 lines
7.7 KiB
Rust
227 lines
7.7 KiB
Rust
// Copyright 2020 Sergiusz 'q3k' Bazanski <q3k@q3k.org>
|
|
//
|
|
// This file is part of Abrasion.
|
|
//
|
|
// Abrasion is free software: you can redistribute it and/or modify it under
|
|
// the terms of the GNU General Public License as published by the Free
|
|
// Software Foundation, version 3.
|
|
//
|
|
// Abrasion is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
// details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License along with
|
|
// Abrasion. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
use std::sync::Arc;
|
|
|
|
use image;
|
|
use image::GenericImageView;
|
|
use vulkano::command_buffer as vcb;
|
|
use vulkano::buffer as vb;
|
|
use vulkano::device as vd;
|
|
use vulkano::format as vf;
|
|
use vulkano::image as vm;
|
|
use vulkano::sampler as vs;
|
|
use vulkano::sync::GpuFuture;
|
|
use vulkano::command_buffer::CommandBuffer;
|
|
|
|
use crate::physics::color;
|
|
|
|
/// Construct a mipmapped vulkan image from an iterator of raw data.
|
|
fn mipmapped_from_iter<P, I, F>(
|
|
width: u32, height: u32,
|
|
format: F,
|
|
data: I,
|
|
graphics_queue: Arc<vd::Queue>,
|
|
) -> Arc<vm::ImmutableImage<F>> where
|
|
P: Send + Sync + Clone + 'static,
|
|
I: ExactSizeIterator<Item = P>,
|
|
F: vf::FormatDesc + vf::AcceptsPixels<P> + 'static + Send + Sync + Copy,
|
|
vf::Format: vf::AcceptsPixels<P>,
|
|
{
|
|
let dimensions = vm::Dimensions::Dim2d{ width, height };
|
|
|
|
let image_usage = vm::ImageUsage {
|
|
transfer_destination: true,
|
|
transfer_source: true,
|
|
sampled: true,
|
|
..vm::ImageUsage::none()
|
|
};
|
|
|
|
// Make a temporary image to generate mipmaps from. This is used to bypass
|
|
// a bug in Vulkano's AutoCommandBufferBuilder that prevents blitting
|
|
// inside the same image across different mipmaps. This is okay, as we plan
|
|
// to move mipmap generation to compile-time anyway.
|
|
|
|
let source = vb::CpuAccessibleBuffer::from_iter(
|
|
graphics_queue.device().clone(),
|
|
vb::BufferUsage::transfer_source(),
|
|
false,
|
|
data,
|
|
).unwrap();
|
|
|
|
let (mipmap_source_image, mipmap_source_image_init) = vm::ImmutableImage::uninitialized(
|
|
graphics_queue.clone().device().clone(),
|
|
dimensions,
|
|
format,
|
|
vm::MipmapsCount::One,
|
|
vm::ImageUsage {
|
|
transfer_destination: true,
|
|
transfer_source: true,
|
|
sampled: true,
|
|
..vm::ImageUsage::none()
|
|
},
|
|
vm::ImageLayout::ShaderReadOnlyOptimal,
|
|
graphics_queue.device().active_queue_families(),
|
|
).unwrap();
|
|
|
|
let (image, image_init) = vm::ImmutableImage::uninitialized(
|
|
graphics_queue.clone().device().clone(),
|
|
dimensions,
|
|
format,
|
|
vm::MipmapsCount::Log2,
|
|
image_usage,
|
|
vm::ImageLayout::ShaderReadOnlyOptimal,
|
|
graphics_queue.device().active_queue_families(),
|
|
).unwrap();
|
|
|
|
let mut cb = vcb::AutoCommandBufferBuilder::new(graphics_queue.device().clone(), graphics_queue.family()).unwrap();
|
|
|
|
// Transfer buffer into mipmap_source_image.
|
|
cb.copy_buffer_to_image_dimensions(
|
|
source, mipmap_source_image_init,
|
|
[0, 0, 0], dimensions.width_height_depth(), 0,
|
|
dimensions.array_layers_with_cube(), 0,
|
|
).unwrap();
|
|
|
|
// Copy mip level 0 (original image) using image_init.
|
|
cb.blit_image(
|
|
mipmap_source_image.clone(), [0, 0, 0], [width as i32, height as i32, 1], 0, 0,
|
|
image_init, [0, 0, 0], [width as i32, height as i32, 1], 0, 0,
|
|
1, vs::Filter::Linear
|
|
).unwrap();
|
|
|
|
|
|
// Generates all other mip levels using image.
|
|
let img_dimensions = vm::ImageAccess::dimensions(&image);
|
|
for mip_idx in 1..image.mipmap_levels() {
|
|
let dest_dim = img_dimensions.mipmap_dimensions(mip_idx).unwrap();
|
|
cb.blit_image(
|
|
mipmap_source_image.clone(), [0, 0, 0], [width as i32, height as i32, 1], 0, 0,
|
|
image.clone(), [0, 0, 0], [dest_dim.width() as i32, dest_dim.height() as i32, 1i32], 0, mip_idx,
|
|
1, vs::Filter::Linear
|
|
).unwrap();
|
|
}
|
|
|
|
let future = cb.build().unwrap().execute(graphics_queue.clone()).unwrap();
|
|
|
|
future.flush().unwrap();
|
|
image
|
|
}
|
|
|
|
/// Represents a layout of color channels whose users (single colors and textures) can be converted
|
|
/// to Vulkan images.
|
|
pub trait ChannelLayoutVulkan {
|
|
fn vulkan_from_image(
|
|
image: Arc<image::DynamicImage>,
|
|
graphics_queue: Arc<vd::Queue>,
|
|
) -> Arc<vm::ImmutableImage<vf::Format>>;
|
|
|
|
fn vulkan_from_value(
|
|
&self,
|
|
graphics_queue: Arc<vd::Queue>,
|
|
) -> Arc<vm::ImmutableImage<vf::Format>>;
|
|
}
|
|
|
|
impl ChannelLayoutVulkan for color::XYZ {
|
|
fn vulkan_from_image(
|
|
image: Arc<image::DynamicImage>,
|
|
graphics_queue: Arc<vd::Queue>,
|
|
) -> Arc<vm::ImmutableImage<vf::Format>> {
|
|
let (width, height) = (image.width(), image.height());
|
|
let rgba = image.to_rgba();
|
|
|
|
let mut xyz = Vec::new();
|
|
for (_, _, color) in rgba.enumerate_pixels() {
|
|
let image::Rgba([r, g, b, a]) = color;
|
|
let r = (*r as f32) / 255.0;
|
|
let g = (*g as f32) / 255.0;
|
|
let b = (*b as f32) / 255.0;
|
|
let a = (*a as f32) / 255.0;
|
|
let (x, y, z) = color::srgb_to_cie_xyz(r, g, b);
|
|
xyz.push(x);
|
|
xyz.push(y);
|
|
xyz.push(z);
|
|
xyz.push(a);
|
|
}
|
|
|
|
// TODO(q3k): RGB -> CIE XYZ
|
|
mipmapped_from_iter(width, height, vf::Format::R32G32B32A32Sfloat, xyz.into_iter(), graphics_queue)
|
|
}
|
|
|
|
fn vulkan_from_value(
|
|
&self,
|
|
graphics_queue: Arc<vd::Queue>,
|
|
) -> Arc<vm::ImmutableImage<vf::Format>> {
|
|
let (image_view, future) = vm::ImmutableImage::from_iter(
|
|
vec!([self.x, self.y, self.z, 1.0 as f32]).into_iter(),
|
|
vm::Dimensions::Dim2d{ width: 1, height: 1 },
|
|
vm::MipmapsCount::One,
|
|
vf::Format::R32G32B32A32Sfloat,
|
|
graphics_queue.clone(),
|
|
).unwrap();
|
|
future.flush().unwrap();
|
|
image_view
|
|
}
|
|
}
|
|
|
|
impl ChannelLayoutVulkan for color::LinearF32 {
|
|
fn vulkan_from_image(
|
|
image: Arc<image::DynamicImage>,
|
|
graphics_queue: Arc<vd::Queue>,
|
|
) -> Arc<vm::ImmutableImage<vf::Format>> {
|
|
let (width, height) = (image.width(), image.height());
|
|
assert!(match image.color() {
|
|
image::ColorType::L8 => true,
|
|
image::ColorType::L16 => true,
|
|
_ => false,
|
|
}, "linearf32 texture must be 8-bit grayscale");
|
|
let gray = image.to_luma();
|
|
|
|
mipmapped_from_iter(width, height, vf::Format::R8Unorm, gray.into_raw().iter().cloned(), graphics_queue)
|
|
}
|
|
|
|
fn vulkan_from_value(
|
|
&self,
|
|
graphics_queue: Arc<vd::Queue>,
|
|
) -> Arc<vm::ImmutableImage<vf::Format>> {
|
|
let mut image = image::ImageBuffer::<image::Luma<f32>, Vec<f32>>::new(1, 1);
|
|
image.put_pixel(0, 0, image::Luma([self.d]));
|
|
|
|
let (image_view, future) = vm::ImmutableImage::from_iter(
|
|
image.into_raw().iter().cloned(),
|
|
vm::Dimensions::Dim2d{ width: 1, height: 1 },
|
|
vm::MipmapsCount::One,
|
|
vf::Format::R32Sfloat,
|
|
graphics_queue.clone(),
|
|
).unwrap();
|
|
|
|
future.flush().unwrap();
|
|
image_view
|
|
}
|
|
}
|
|
|
|
fn get_mip_dim(mip_idx: u32, img_dimensions: vm::ImageDimensions) -> Result<[i32; 3], String> {
|
|
if let Some(dim) = img_dimensions.mipmap_dimensions(mip_idx) {
|
|
if let vm::ImageDimensions::Dim2d { width, height, .. } = dim {
|
|
Ok([width as i32, height as i32, 1])
|
|
} else {
|
|
Err("MipMapping: Did not get 2D image for blitting".to_string())
|
|
}
|
|
} else {
|
|
Err(format!("MipMapping: image has no mip map at level {}", mip_idx).to_string())
|
|
}
|
|
}
|