Vala プログラミング

WebGPU プログラミング

おなが@京都先端科学大

Rust Vulkano Cubemap

f:id:onagat12:20181229021533g:plain

cubemapの描画
Vulkano issue #922 (Usage of CubeMap) を参考にしました。
1) The data for the buffer is the image data of six images appended to each other
in the order of: left, right, bottom, top, back, and front (at least that is the order
I have). The size of the cubemap is the width of one of the square images.
2) Say you load an image into a Vec of u8's, where each value is the color
component r, g, b, or a; or any other variant. Just append the next image's data
onto that Vec.

1 cubemap imageデータの生成
 まず、次のようにして各部分の imageデータ(img_posx, img_negx, ...)を
生成します。

let img_posx = image::load_from_memory_with_format(include_bytes!("images/posx512.jpg"),
    ImageFormat::JPEG).unwrap().to_rgba();
let img_negx = image::load_from_memory_with_format(include_bytes!("images/negx512.jpg"),
    ImageFormat::JPEG).unwrap().to_rgba();
・・・

生成される imageデータの型は Vec<u8> になる。
 次に、空の Vec<u8> データを準備し、これに posx, negx, posy, negy, posz,
negz の順に各imageデータを追加して、 cubemap 全体の imageデータ
(一つの Vec<u8> データ image_data)にする。
 これから cubemap texture を生成する。

let cubemap_images = [img_posx, img_negx, img_posy, img_negy, img_posz, img_negz];
let mut image_data: Vec<u8> = Vec::new();

for image in cubemap_images.into_iter() {
    let mut image0 = image.clone().into_raw().clone();
    image_data.append(&mut image0);
}


2 cubemap texture の生成
 cubemap texture の生成には、通常の texture 生成と同じ文を使用する。
ただし、from_iter()メソッドの dimensions 引数は Cubemap を設定する。
また、image_data(型は Vec<u8> )には、1で生成した cubemap imageデータを
設定する。

let (texture, tex_future) = {
    ImmutableImage::from_iter(
        image_data.iter().cloned(),
        Dimensions::Cubemap { size: 512 },
        Format::R8G8B8A8Srgb,
        vk.queue.clone()
    ).unwrap()
};

cubemap プログラム

#[macro_use]
extern crate vulkano;
extern crate vulkano_shaders;
extern crate winit;
extern crate vulkano_win;
extern crate arcball;
extern crate cgmath;
extern crate image;

use vulkano_win::VkSurfaceBuild;
use vulkano::buffer::BufferUsage;
use vulkano::buffer::cpu_access::CpuAccessibleBuffer;
use vulkano::command_buffer::{AutoCommandBufferBuilder, DynamicState};
use vulkano::device::{Device, DeviceExtensions};
use vulkano::framebuffer::{Framebuffer, Subpass, FramebufferAbstract, RenderPassAbstract};
use vulkano::format::Format;
use vulkano::instance::{Instance, PhysicalDevice};
use vulkano::image::{SwapchainImage, ImmutableImage, Dimensions};
use vulkano::image::attachment::AttachmentImage;
use vulkano::pipeline::{GraphicsPipeline, GraphicsPipelineAbstract};
use vulkano::pipeline::viewport::Viewport;
use vulkano::swapchain;
use vulkano::swapchain::{Swapchain, SurfaceTransform, PresentMode, AcquireError};
use vulkano::sync;
use vulkano::sync::GpuFuture;
use vulkano::sampler::{Sampler, SamplerAddressMode, Filter, MipmapMode};

use winit::{Window, EventsLoop, WindowBuilder, Event, WindowEvent};
use image::ImageFormat;
use std::sync::Arc;
use std::iter;
use std::time::Instant;
use cgmath::{Matrix3, Matrix4, Rad, Point3, Vector3};

#[derive(Debug, Clone)]
struct Vertex { position: [f32; 3] }
impl_vertex!(Vertex, position);

static WINDOW_NAME: &str = "Cubemap";
static WIN_WIDTH: f64 = 600.0;
static WIN_HEIGHT: f64 = 600.0;

struct Vulkan {
    images: Vec<std::sync::Arc<vulkano::image::SwapchainImage<winit::Window>>>,
    swapchain: Arc<vulkano::swapchain::Swapchain<winit::Window>>,
    device: Arc<vulkano::device::Device>,
    queue: Arc<vulkano::device::Queue>,
    events_loop: winit::EventsLoop,
    surface: Arc<vulkano::swapchain::Surface<winit::Window>>,
}

impl Vulkan {
    pub fn init_vk() -> Vulkan {
        let extensions = vulkano_win::required_extensions();
        let instance = Instance::new(None, &extensions, None).unwrap();

        let physical = PhysicalDevice::enumerate(&instance).next().unwrap();
        println!("Using device: {} (type: {:?})", physical.name(), physical.ty());

        // events_loop, surface, window
        let  events_loop = EventsLoop::new();
        let surface = WindowBuilder::new()
            .with_dimensions(winit::dpi::LogicalSize {width:WIN_WIDTH, height:WIN_HEIGHT})
            .with_title(WINDOW_NAME.to_string())
            .build_vk_surface(&events_loop, instance.clone()).unwrap();
        
        // (device, queues), queue_family
        let queue_family = physical.queue_families().find(|&q| {
            q.supports_graphics() && surface.is_supported(q).unwrap_or(false)
        }).unwrap();

        let device_ext = DeviceExtensions { khr_swapchain: true, .. DeviceExtensions::none() };
        let (device, mut queues) = Device::new(physical, physical.supported_features(),
            &device_ext, [(queue_family, 0.5)].iter().cloned()).unwrap();
        // we use only one queue, first one
        let queue = queues.next().unwrap();

        let initial_dimensions  = [WIN_WIDTH as u32, WIN_HEIGHT as u32];
        let caps = surface.capabilities(physical).unwrap();
    
        let (swapchain, images) = {
            let usage = caps.supported_usage_flags;
            let alpha = caps.supported_composite_alpha.iter().next().unwrap();
            let format = caps.supported_formats[0].0;
            //
            Swapchain::new(device.clone(), surface.clone(), caps.min_image_count, format,
                initial_dimensions, 1, usage, &queue, SurfaceTransform::Identity, alpha,
                PresentMode::Fifo, true, None).unwrap()
        };
      
        Vulkan {
            images,
            swapchain,
            device,
            queue,
            surface,
            events_loop,
        } 
    }
}

fn main() {
    // Vulkan Object initialization
    let mut vk = Vulkan::init_vk();

    let skybox_vertex_buffer = {
        let side2: f32 = 20.0 / 2.0;

        CpuAccessibleBuffer::from_iter(vk.device.clone(), BufferUsage::all(), [
            // Front
            Vertex { position: [-side2, -side2,  side2] },
    	    Vertex { position: [ side2, -side2,  side2] },
    	    Vertex { position: [ side2,  side2,  side2] },
            Vertex { position: [-side2,  side2,  side2] },
    	    // Right
    	    Vertex { position: [ side2, -side2,  side2] },
    	    Vertex { position: [ side2, -side2, -side2] },
    	    Vertex { position: [ side2,  side2, -side2] },
            Vertex { position: [ side2,  side2,  side2] },
    	    // Back
    	    Vertex { position: [-side2, -side2, -side2] },
    	    Vertex { position: [-side2,  side2, -side2] },
    	    Vertex { position: [ side2,  side2, -side2] },
            Vertex { position: [ side2, -side2, -side2] },
    	    // Left
    	    Vertex { position: [-side2, -side2,  side2] },
    	    Vertex { position: [-side2,  side2,  side2] },
            Vertex { position: [-side2,  side2, -side2] },
            Vertex { position: [-side2, -side2, -side2] },
            // Bottom
    	    Vertex { position: [-side2, -side2,  side2] },
    	    Vertex { position: [-side2, -side2, -side2] },
    	    Vertex { position: [ side2, -side2, -side2] },
            Vertex { position: [ side2, -side2,  side2] },
    	    // Top
            Vertex { position: [-side2,  side2,  side2] },
    	    Vertex { position: [ side2,  side2,  side2] },
    	    Vertex { position: [ side2,  side2, -side2] },
            Vertex { position: [-side2,  side2, -side2] }
        ].iter().cloned()).expect("failed to create buffer")
    };

    let skybox_index_buffer = vulkano::buffer::cpu_access::CpuAccessibleBuffer
        ::from_iter(vk.device.clone(), vulkano::buffer::BufferUsage::all(), [
            // Front
            0u16, 1, 2, 2, 3, 0,
            // Right
            4, 5, 6, 6, 7, 4,
            // Back
            8, 9, 10, 10, 11, 8,
            // Left
            12, 13, 14, 14, 15, 12,
            // Bottom
            16, 17, 18, 18, 19, 16,
            // Top
            20, 21, 22, 22, 23, 20,
        ].iter().cloned()).expect("failed to create buffer");

    // uniform buffer
    let uniform_buffer = vulkano::buffer::cpu_pool::CpuBufferPool::<vs::ty::Data>
        ::new(vk.device.clone(), vulkano::buffer::BufferUsage::all());

    let vs = vs::Shader::load(vk.device.clone()).expect("failed to create shader module");
    let fs = fs::Shader::load(vk.device.clone()).expect("failed to create shader module");

    // render pass
    let render_pass = Arc::new(single_pass_renderpass!(vk.device.clone(),
        attachments: {
            color: {
                load: Clear,
                store: Store,
                format: vk.swapchain.format(),
                samples: 1,
            },
            depth: {
                load: Clear,
                store: DontCare,
                format: vulkano::format::Format::D16Unorm,
                samples: 1,
            } // depth
        },
        pass: {
            color: [color],
            depth_stencil: {depth} // depth
        }
    ).unwrap());

    let window = vk.surface.window();

    let mut dimensions = if let Some(dimensions) = window.get_inner_size() {
        let dimensions: (u32, u32) = dimensions.to_physical(window.get_hidpi_factor()).into();
        [dimensions.0, dimensions.1]
    } else {
        return;
    };

    let img_posx = image::load_from_memory_with_format(include_bytes!("images/posx512.jpg"),
        ImageFormat::JPEG).unwrap().to_rgba();
    let img_negx = image::load_from_memory_with_format(include_bytes!("images/negx512.jpg"),
        ImageFormat::JPEG).unwrap().to_rgba();
    let img_posy = image::load_from_memory_with_format(include_bytes!("images/posy512.jpg"),
        ImageFormat::JPEG).unwrap().to_rgba();
    let img_negy = image::load_from_memory_with_format(include_bytes!("images/negy512.jpg"),
        ImageFormat::JPEG).unwrap().to_rgba();
    let img_posz = image::load_from_memory_with_format(include_bytes!("images/posz512.jpg"),
        ImageFormat::JPEG).unwrap().to_rgba();
    let img_negz = image::load_from_memory_with_format(include_bytes!("images/negz512.jpg"),
        ImageFormat::JPEG).unwrap().to_rgba();
    
    let cubemap_images = [img_posx, img_negx, img_posy, img_negy, img_posz, img_negz];
    let mut image_data: Vec<u8> = Vec::new();

    for image in cubemap_images.into_iter() {
        let mut image0 = image.clone().into_raw().clone();
        image_data.append(&mut image0);
    }
    
    let (texture, tex_future) = {
        ImmutableImage::from_iter(
            image_data.iter().cloned(),
            Dimensions::Cubemap { size: 512 },
            Format::R8G8B8A8Srgb,
            vk.queue.clone()
        ).unwrap()
    };

    let sampler = Sampler::new(vk.device.clone(), Filter::Linear, Filter::Linear,
        MipmapMode::Nearest, SamplerAddressMode::Repeat, SamplerAddressMode::Repeat,
        SamplerAddressMode::Repeat, 0.0, 1.0, 0.0, 0.0).unwrap();

    // pipeline and framebuffer
    let (mut pipeline, mut framebuffers) = 
        window_size_dependent_setup(vk.device.clone(), &vs, &fs, &vk.images, render_pass.clone());

    let mut recreate_swapchain = false;
    let mut previous_frame = Box::new(tex_future) as Box<GpuFuture>;
    let rotation_start = Instant::now();

    loop {
        previous_frame.cleanup_finished();

        if recreate_swapchain {
            dimensions = if let Some(dimensions) = window.get_inner_size() {
                let dimensions: (u32, u32) =
                    dimensions.to_physical(window.get_hidpi_factor()).into();
                [dimensions.0, dimensions.1]
            } else {
                return;
            };
            
            let (new_swapchain, new_images) = vk.swapchain.recreate_with_dimension(dimensions)
                .expect("swapcahain not recreate");
            
            vk.swapchain = new_swapchain;

            let (new_pipeline, new_framebuffers) = window_size_dependent_setup(vk.device.clone(), &vs, &fs,
                &new_images, render_pass.clone());
            
            pipeline = new_pipeline;
            framebuffers = new_framebuffers;

            recreate_swapchain = false;
        }

        let uniform_buffer_subbuffer = {
            let elapsed = rotation_start.elapsed();
            let rotation = elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1_000_000_000.0;
            let rotation = Matrix3::from_angle_y(Rad(0.6 * rotation as f32));

            let world  = Matrix4::from(rotation);
            let view = Matrix4::look_at(Point3::new(0.0, 0.0, 1.0), Point3::new(0.0, 0.0, 0.0), Vector3::new(0.0, -1.0, 0.0));
            let scale = Matrix4::from_scale(1.0);
            let aspect_ratio = dimensions[0] as f32 / dimensions[1] as f32;
            let proj = cgmath::perspective(Rad(std::f32::consts::FRAC_PI_2), aspect_ratio, 0.01, 100.0);

            let uniform_data = vs::ty::Data {
                world: world.into(),
                view : (view * scale).into(),
                proj : proj.into(),
            };

            uniform_buffer.next(uniform_data).unwrap()
        };

        let set0 = Arc::new(vulkano::descriptor::descriptor_set::PersistentDescriptorSet::start(pipeline.clone(), 0)
            .add_buffer(uniform_buffer_subbuffer).unwrap()
            .build().unwrap()
        );

        let set1 = Arc::new(vulkano::descriptor::descriptor_set::PersistentDescriptorSet::start(pipeline.clone(), 1)
            .add_sampled_image(texture.clone(), sampler.clone()).unwrap()
            .build().unwrap()
        );
        
        let (image_num, acquire_future) =
            match swapchain::acquire_next_image(vk.swapchain.clone(), None) {
                Ok(r) => r,
                Err(AcquireError::OutOfDate) => {
                    recreate_swapchain = true;
                    continue;
                },
                Err(err) => panic!("{:?}", err)
            };

        let command_buffer =
            AutoCommandBufferBuilder::primary_one_time_submit(vk.device.clone(), vk.queue.family()).unwrap() // Ok
                .begin_render_pass(framebuffers[image_num].clone(), false,
                vec![[0.1, 0.1, 0.1, 1.0].into(), 1f32.into()]).unwrap()
            .draw_indexed(pipeline.clone(),
                &DynamicState::none(),
                vec!(skybox_vertex_buffer.clone()), skybox_index_buffer.clone(),
                (set0.clone(), set1.clone()), ()).unwrap()
            .end_render_pass().unwrap()
            .build().unwrap();

        let _future = previous_frame.join(acquire_future)
            .then_execute(vk.queue.clone(), command_buffer).unwrap()
            .then_swapchain_present(vk.queue.clone(), vk.swapchain.clone(), image_num)
            .then_signal_fence_and_flush();
        
        previous_frame = Box::new(sync::now(vk.device.clone())) as Box<_>;

        let mut done = false;
        vk.events_loop.poll_events(|ev| {
            match ev {
                Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => done = true,
                Event::WindowEvent { event: WindowEvent::Resized(_), .. } => recreate_swapchain = true,
                _ => ()
            }
        });
        if done { return; }
    }
}

fn window_size_dependent_setup(
    device: Arc<Device>,
    vs: &vs::Shader,
    fs: &fs::Shader,
    images: &[Arc<SwapchainImage<Window>>],
    render_pass: Arc<RenderPassAbstract + Send + Sync>,
) -> (Arc<GraphicsPipelineAbstract + Send + Sync>, Vec<Arc<FramebufferAbstract + Send + Sync>> ) {
    let dimensions = images[0].dimensions();

    let depth_buffer = AttachmentImage::transient(device.clone(), dimensions, Format::D16Unorm).unwrap();

    let framebuffers = images.iter().map(|image| {
        Arc::new(
            Framebuffer::start(render_pass.clone())
                .add(image.clone()).unwrap()
                .add(depth_buffer.clone()).unwrap()
                .build().unwrap()
        ) as Arc<FramebufferAbstract + Send + Sync>
    }).collect::<Vec<_>>();

    let pipeline = Arc::new(GraphicsPipeline::start()
        .vertex_input_single_buffer::<Vertex>()
        .vertex_shader(vs.main_entry_point(), ())
        .triangle_list()
        .viewports_dynamic_scissors_irrelevant(1)
        .viewports(iter::once(Viewport {
            origin: [0.0, 0.0],
            dimensions: [dimensions[0] as f32, dimensions[1] as f32],
            depth_range: 0.0 .. 1.0,
        }))
        .fragment_shader(fs.main_entry_point(), ())
        .depth_stencil_simple_depth()
        .render_pass(Subpass::from(render_pass.clone(), 0).unwrap())
        .build(device.clone())
        .unwrap());

    (pipeline, framebuffers)
}

mod vs {
    vulkano_shaders::shader!{
        ty: "vertex",
        src: "
            #version 450

            layout(location = 0) in vec3 position;
            layout (location = 0) out vec3 ReflectDir;

            layout(set = 0, binding = 0) uniform Data {
                mat4 world;
                mat4 view;
                mat4 proj;
            } ubo;

            void main() 
            {
	        ReflectDir = position;
                gl_Position = ubo.proj * ubo.view * ubo.world * vec4(position, 1.0);
            }"
    }
}

mod fs {
    vulkano_shaders::shader!{
        ty: "fragment",
        src: "
            #version 450

            layout(location = 0) in vec3 ReflectDir;
            layout(location = 0) out vec4 f_color;

            layout(set = 1, binding = 0) uniform samplerCube cubetex;

            void main() 
            {
	         f_color = texture(cubetex, ReflectDir);
            }"
    }
}

2019-2-7 update