engine/render/vulkan: implement mipmaps
parent
56132c49a1
commit
24e25651c5
|
@ -19,6 +19,7 @@ rust_binary(
|
||||||
"src/render/mod.rs",
|
"src/render/mod.rs",
|
||||||
"src/render/renderable.rs",
|
"src/render/renderable.rs",
|
||||||
"src/render/vulkan/data.rs",
|
"src/render/vulkan/data.rs",
|
||||||
|
"src/render/vulkan/material.rs",
|
||||||
"src/render/vulkan/mod.rs",
|
"src/render/vulkan/mod.rs",
|
||||||
"src/render/vulkan/pipeline.rs",
|
"src/render/vulkan/pipeline.rs",
|
||||||
"src/render/vulkan/pipeline_forward.rs",
|
"src/render/vulkan/pipeline_forward.rs",
|
||||||
|
|
|
@ -19,113 +19,21 @@ use std::sync::Mutex;
|
||||||
use std::time;
|
use std::time;
|
||||||
|
|
||||||
use image;
|
use image;
|
||||||
use image::GenericImageView;
|
|
||||||
use vulkano::device as vd;
|
use vulkano::device as vd;
|
||||||
use vulkano::format as vf;
|
use vulkano::format as vf;
|
||||||
use vulkano::image as vm;
|
use vulkano::image as vm;
|
||||||
use vulkano::sync::GpuFuture;
|
|
||||||
|
|
||||||
use crate::physics::color;
|
use crate::physics::color;
|
||||||
use crate::render::vulkan::data;
|
use crate::render::vulkan::data;
|
||||||
|
use crate::render::vulkan::material::ChannelLayoutVulkan;
|
||||||
use crate::util::file;
|
use crate::util::file;
|
||||||
|
|
||||||
pub trait ChannelLayout {
|
pub enum Texture<T: 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 ChannelLayout 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();
|
|
||||||
// TODO(q3k): RGB -> CIE XYZ
|
|
||||||
let (image_view, future) = vm::ImmutableImage::from_iter(
|
|
||||||
rgba.into_raw().iter().cloned(),
|
|
||||||
vm::Dimensions::Dim2d{ width, height },
|
|
||||||
vf::Format::R8G8B8A8Unorm,
|
|
||||||
graphics_queue.clone(),
|
|
||||||
).unwrap();
|
|
||||||
|
|
||||||
future.flush().unwrap();
|
|
||||||
image_view
|
|
||||||
}
|
|
||||||
|
|
||||||
fn vulkan_from_value(
|
|
||||||
&self,
|
|
||||||
graphics_queue: Arc<vd::Queue>,
|
|
||||||
) -> Arc<vm::ImmutableImage<vf::Format>> {
|
|
||||||
let mut image = image::ImageBuffer::<image::Rgba<f32>, Vec<f32>>::new(1, 1);
|
|
||||||
image.put_pixel(0, 0, image::Rgba([self.x, self.y, self.z, 0.0]));
|
|
||||||
|
|
||||||
let (image_view, future) = vm::ImmutableImage::from_iter(
|
|
||||||
image.into_raw().iter().cloned(),
|
|
||||||
vm::Dimensions::Dim2d{ width: 1, height: 1 },
|
|
||||||
vf::Format::R32G32B32A32Sfloat,
|
|
||||||
graphics_queue.clone(),
|
|
||||||
).unwrap();
|
|
||||||
future.flush().unwrap();
|
|
||||||
image_view
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ChannelLayout 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();
|
|
||||||
let (image_view, future) = vm::ImmutableImage::from_iter(
|
|
||||||
gray.into_raw().iter().cloned(),
|
|
||||||
vm::Dimensions::Dim2d{ width, height },
|
|
||||||
vf::Format::R8G8B8A8Unorm,
|
|
||||||
graphics_queue.clone(),
|
|
||||||
).unwrap();
|
|
||||||
|
|
||||||
future.flush().unwrap();
|
|
||||||
image_view
|
|
||||||
}
|
|
||||||
|
|
||||||
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 },
|
|
||||||
vf::Format::R32Sfloat,
|
|
||||||
graphics_queue.clone(),
|
|
||||||
).unwrap();
|
|
||||||
|
|
||||||
future.flush().unwrap();
|
|
||||||
image_view
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum Texture<T: ChannelLayout> {
|
|
||||||
Color(T),
|
Color(T),
|
||||||
ImageRef(String),
|
ImageRef(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ChannelLayout> Texture<T> {
|
impl<T: ChannelLayoutVulkan> Texture<T> {
|
||||||
fn vulkan_image(&self, graphics_queue: Arc<vd::Queue>) -> Arc<vm::ImmutableImage<vf::Format>> {
|
fn vulkan_image(&self, graphics_queue: Arc<vd::Queue>) -> Arc<vm::ImmutableImage<vf::Format>> {
|
||||||
match self {
|
match self {
|
||||||
Texture::<T>::Color(c) => c.vulkan_from_value(graphics_queue),
|
Texture::<T>::Color(c) => c.vulkan_from_value(graphics_queue),
|
||||||
|
|
|
@ -0,0 +1,213 @@
|
||||||
|
// 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 = 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 = 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 = 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();
|
||||||
|
|
||||||
|
// TODO(q3k): RGB -> CIE XYZ
|
||||||
|
mipmapped_from_iter(width, height, vf::Format::R8G8B8A8Unorm, rgba.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::Rgba<f32>, Vec<f32>>::new(1, 1);
|
||||||
|
image.put_pixel(0, 0, image::Rgba([self.x, self.y, self.z, 0.0]));
|
||||||
|
|
||||||
|
let (image_view, future) = vm::ImmutableImage::from_iter(
|
||||||
|
image.into_raw().iter().cloned(),
|
||||||
|
vm::Dimensions::Dim2d{ width: 1, height: 1 },
|
||||||
|
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::R8G8B8A8Unorm, 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 },
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,6 +27,7 @@ use vulkano::swapchain as vs;
|
||||||
use vulkano::sync::{FenceSignalFuture, GpuFuture};
|
use vulkano::sync::{FenceSignalFuture, GpuFuture};
|
||||||
|
|
||||||
pub mod data;
|
pub mod data;
|
||||||
|
pub mod material;
|
||||||
mod surface_binding;
|
mod surface_binding;
|
||||||
mod pipeline;
|
mod pipeline;
|
||||||
mod pipeline_forward;
|
mod pipeline_forward;
|
||||||
|
|
Loading…
Reference in New Issue