#![allow(dead_code)]
use glsl_to_spirv::ShaderType;
use rayon::prelude::*;
use std::io::{Read, Write};
use std::sync::Arc;
use truck_platform::*;
use wgpu::*;
#[derive(Clone, Debug)]
pub struct Plane<'a> {
pub vertex_shader: &'a str,
pub fragment_shader: &'a str,
pub id: RenderID,
}
#[macro_export]
macro_rules! new_plane {
($vertex_shader: expr, $fragment_shader: expr) => {
Plane {
vertex_shader: include_str!($vertex_shader),
fragment_shader: include_str!($fragment_shader),
id: RenderID::gen(),
}
};
}
impl<'a> Rendered for Plane<'a> {
impl_render_id!(id);
fn vertex_buffer(
&self,
handler: &DeviceHandler,
) -> (Arc<BufferHandler>, Option<Arc<BufferHandler>>) {
let vertex_buffer = BufferHandler::from_slice(
&[0 as u32, 1, 2, 2, 1, 3],
handler.device(),
BufferUsage::VERTEX,
);
(Arc::new(vertex_buffer), None)
}
fn bind_group_layout(&self, handler: &DeviceHandler) -> Arc<BindGroupLayout> {
Arc::new(bind_group_util::create_bind_group_layout(
handler.device(),
&[],
))
}
fn bind_group(&self, handler: &DeviceHandler, layout: &BindGroupLayout) -> Arc<BindGroup> {
Arc::new(handler.device().create_bind_group(&BindGroupDescriptor {
label: None,
layout,
entries: &[],
}))
}
fn pipeline(
&self,
handler: &DeviceHandler,
layout: &PipelineLayout,
sample_count: u32,
) -> Arc<RenderPipeline> {
let (device, sc_desc) = (handler.device(), handler.sc_desc());
let vertex_spirv = compile_shader(self.vertex_shader, ShaderType::Vertex);
let vertex_module = device.create_shader_module(wgpu::util::make_spirv(&vertex_spirv));
let fragment_spirv = compile_shader(self.fragment_shader, ShaderType::Fragment);
let fragment_module = device.create_shader_module(wgpu::util::make_spirv(&fragment_spirv));
Arc::new(
handler
.device()
.create_render_pipeline(&RenderPipelineDescriptor {
layout: Some(layout),
vertex_stage: ProgrammableStageDescriptor {
module: &vertex_module,
entry_point: "main",
},
fragment_stage: Some(ProgrammableStageDescriptor {
module: &fragment_module,
entry_point: "main",
}),
rasterization_state: Some(RasterizationStateDescriptor {
front_face: FrontFace::Ccw,
cull_mode: CullMode::None,
depth_bias: 0,
depth_bias_slope_scale: 0.0,
depth_bias_clamp: 0.0,
clamp_depth: false,
}),
primitive_topology: PrimitiveTopology::TriangleList,
color_states: &[ColorStateDescriptor {
format: sc_desc.format,
color_blend: BlendDescriptor::REPLACE,
alpha_blend: BlendDescriptor::REPLACE,
write_mask: ColorWrite::ALL,
}],
depth_stencil_state: Some(DepthStencilStateDescriptor {
format: TextureFormat::Depth32Float,
depth_write_enabled: true,
depth_compare: wgpu::CompareFunction::Less,
stencil: StencilStateDescriptor {
front: StencilStateFaceDescriptor::IGNORE,
back: StencilStateFaceDescriptor::IGNORE,
read_mask: 0,
write_mask: 0,
},
}),
vertex_state: VertexStateDescriptor {
index_format: IndexFormat::Uint32,
vertex_buffers: &[VertexBufferDescriptor {
stride: std::mem::size_of::<u32>() as BufferAddress,
step_mode: InputStepMode::Vertex,
attributes: &[VertexAttributeDescriptor {
format: VertexFormat::Uint,
offset: 0,
shader_location: 0,
}],
}],
},
sample_count,
sample_mask: !0,
alpha_to_coverage_enabled: false,
label: None,
}),
)
}
}
pub fn render_one<R: Rendered>(scene: &mut Scene, texture: &Texture, object: &R) {
scene.add_object(object);
scene.render_scene(&texture.create_view(&Default::default()));
scene.remove_object(object);
}
pub fn render_ones<'a, R: 'a + Rendered, I: IntoIterator<Item = &'a R>>(
scene: &mut Scene,
texture: &Texture,
object: I,
) {
scene.add_objects(object);
scene.render_scene(&texture.create_view(&Default::default()));
scene.clear_objects();
}
pub fn compile_shader(code: &str, shadertype: ShaderType) -> Vec<u8> {
let mut spirv = glsl_to_spirv::compile(&code, shadertype).unwrap();
let mut compiled = Vec::new();
spirv.read_to_end(&mut compiled).unwrap();
compiled
}
pub fn nontex_answer_texture(scene: &mut Scene) -> Texture {
let sc_desc = scene.sc_desc();
let tex_desc = texture_descriptor(&sc_desc);
let texture = scene.device().create_texture(&tex_desc);
let mut plane = new_plane!("shaders/plane.vert", "shaders/unicolor.frag");
render_one(scene, &texture, &mut plane);
texture
}
pub fn random_texture(scene: &mut Scene) -> Texture {
let sc_desc = scene.sc_desc();
let tex_desc = texture_descriptor(&sc_desc);
let texture = scene.device().create_texture(&tex_desc);
let mut plane = new_plane!("shaders/plane.vert", "shaders/random.frag");
render_one(scene, &texture, &mut plane);
texture
}
pub fn gradation_texture(scene: &mut Scene) -> Texture {
let sc_desc = scene.sc_desc();
let tex_desc = texture_descriptor(&sc_desc);
let texture = scene.device().create_texture(&tex_desc);
let mut plane = new_plane!("shaders/plane.vert", "shaders/gradation.frag");
render_one(scene, &texture, &mut plane);
texture
}
pub fn init_device(instance: &Instance) -> (Arc<Device>, Arc<Queue>) {
futures::executor::block_on(async {
let adapter = instance
.request_adapter(&RequestAdapterOptions {
power_preference: PowerPreference::Default,
compatible_surface: None,
})
.await
.unwrap();
writeln!(&mut std::io::stderr(), "{:?}", adapter.get_info()).unwrap();
let (device, queue) = adapter
.request_device(
&DeviceDescriptor {
features: Default::default(),
limits: Limits::default(),
shader_validation: true,
},
None,
)
.await
.unwrap();
(Arc::new(device), Arc::new(queue))
})
}
pub fn swap_chain_descriptor(size: (u32, u32)) -> SwapChainDescriptor {
SwapChainDescriptor {
usage: TextureUsage::OUTPUT_ATTACHMENT,
format: TextureFormat::Rgba8Unorm,
width: size.0,
height: size.1,
present_mode: PresentMode::Mailbox,
}
}
pub fn texture_descriptor(sc_desc: &SwapChainDescriptor) -> TextureDescriptor<'static> {
TextureDescriptor {
label: None,
size: Extent3d {
width: sc_desc.width,
height: sc_desc.height,
depth: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: TextureDimension::D2,
format: sc_desc.format,
usage: TextureUsage::OUTPUT_ATTACHMENT | TextureUsage::COPY_SRC,
}
}
pub fn texture_copy_view<'a>(texture: &'a Texture) -> TextureCopyView<'a> {
TextureCopyView {
texture: &texture,
mip_level: 0,
origin: Origin3d::ZERO,
}
}
pub fn buffer_copy_view<'a>(buffer: &'a Buffer, size: (u32, u32)) -> BufferCopyView<'a> {
BufferCopyView {
buffer: &buffer,
layout: TextureDataLayout {
offset: 0,
bytes_per_row: size.0 * 4,
rows_per_image: size.1,
},
}
}
pub fn read_buffer(device: &Device, buffer: &Buffer) -> Vec<u8> {
let buffer_slice = buffer.slice(..);
let buffer_future = buffer_slice.map_async(MapMode::Read);
device.poll(Maintain::Wait);
futures::executor::block_on(async {
match buffer_future.await {
Ok(_) => buffer_slice.get_mapped_range().iter().map(|b| *b).collect(),
Err(_) => panic!("failed to run compute on gpu!"),
}
})
}
pub fn read_texture(handler: &DeviceHandler, texture: &Texture) -> Vec<u8> {
let (device, queue, sc_desc) = (handler.device(), handler.queue(), handler.sc_desc());
let size = (sc_desc.width * sc_desc.height * 4) as u64;
let buffer = device.create_buffer(&BufferDescriptor {
label: None,
mapped_at_creation: false,
usage: BufferUsage::COPY_DST | BufferUsage::MAP_READ,
size,
});
let mut encoder = device.create_command_encoder(&CommandEncoderDescriptor { label: None });
encoder.copy_texture_to_buffer(
texture_copy_view(&texture),
buffer_copy_view(&buffer, (sc_desc.width, sc_desc.height)),
Extent3d {
width: sc_desc.width,
height: sc_desc.height,
depth: 1,
},
);
queue.submit(Some(encoder.finish()));
read_buffer(device, &buffer)
}
pub fn save_buffer<P: AsRef<std::path::Path>>(path: P, vec: &Vec<u8>, size: (u32, u32)) {
image::save_buffer(path, &vec, size.0, size.1, image::ColorType::Rgba8).unwrap();
}
pub fn same_buffer(vec0: &Vec<u8>, vec1: &Vec<u8>) -> bool {
vec0.par_iter()
.zip(vec1)
.all(move |(i, j)| std::cmp::max(i, j) - std::cmp::min(i, j) < 3)
}
pub fn count_difference(vec0: &Vec<u8>, vec1: &Vec<u8>) -> usize {
vec0.par_iter()
.zip(vec1)
.filter(move |(i, j)| *std::cmp::max(i, j) - *std::cmp::min(i, j) > 2)
.count()
}
pub fn os_alt_exec_test<F: Fn(BackendBit, &str)>(test: F) {
if cfg!(target_os = "windows") {
test(BackendBit::VULKAN, "output/vulkan/");
test(BackendBit::DX12, "output/dx12/");
} else if cfg!(target_os = "macos") {
test(BackendBit::METAL, "output/");
} else {
test(BackendBit::VULKAN, "output/");
}
}