use std::ops::Range;
use gfx_hal::{
device::Device,
pass::Subpass,
pso::{
BlendState,
ColorBlendDesc,
ColorMask,
EntryPoint,
Face,
GraphicsPipelineDesc,
PrimitiveAssemblerDesc,
Rasterizer,
},
Backend,
};
use super::{
assembler::{
PrimitiveAssemblerBuilder,
VertexAttribute,
},
buffer::Buffer,
gpu::Gpu,
shader::ShaderModule,
};
pub struct RenderPipelineBuilder<RenderBackend: Backend> {
pipeline_layout: Option<RenderBackend::PipelineLayout>,
push_constants: Vec<PushConstantUpload>,
buffers: Vec<Buffer<RenderBackend>>,
attributes: Vec<VertexAttribute>,
}
pub type PipelineStage = gfx_hal::pso::ShaderStageFlags;
pub type PushConstantUpload = (PipelineStage, Range<u32>);
impl<RenderBackend: Backend> RenderPipelineBuilder<RenderBackend> {
pub fn new() -> Self {
return Self {
pipeline_layout: None,
push_constants: Vec::new(),
buffers: Vec::new(),
attributes: Vec::new(),
};
}
pub fn with_buffer(
&mut self,
buffer: Buffer<RenderBackend>,
attributes: Vec<VertexAttribute>,
) -> &mut Self {
self.buffers.push(buffer);
self.attributes.extend(attributes);
return self;
}
pub fn with_push_constant(
&mut self,
stage: PipelineStage,
bytes: u32,
) -> &mut Self {
self.push_constants.push((stage, 0..bytes));
return self;
}
pub fn with_push_constants(
mut self,
push_constants: Vec<PushConstantUpload>,
) -> Self {
self.push_constants.extend(push_constants);
return self;
}
pub fn build(
self,
gpu: &Gpu<RenderBackend>,
render_pass: &super::render_pass::RenderPass<RenderBackend>,
vertex_shader: &ShaderModule<RenderBackend>,
fragment_shader: Option<&ShaderModule<RenderBackend>>,
buffers: &Vec<&Buffer<RenderBackend>>,
attributes: &[VertexAttribute],
) -> RenderPipeline<RenderBackend> {
let push_constants = self.push_constants.into_iter();
let pipeline_layout = unsafe {
gpu
.internal_logical_device()
.create_pipeline_layout(vec![].into_iter(), push_constants)
.expect(
"The GPU does not have enough memory to allocate a pipeline layout",
)
};
let mut builder = PrimitiveAssemblerBuilder::new();
let primitive_assembler =
builder.build(vertex_shader, Some(buffers), Some(attributes));
let fragment_entry = match fragment_shader {
Some(shader) => Some(EntryPoint::<RenderBackend> {
entry: shader.entry(),
module: super::internal::module_for(shader),
specialization: shader.specializations().clone(),
}),
None => None,
};
let mut pipeline_desc = GraphicsPipelineDesc::new(
primitive_assembler.internal_primitive_assembler(),
Rasterizer {
cull_face: Face::BACK,
..Rasterizer::FILL
},
fragment_entry,
&pipeline_layout,
Subpass {
index: 0,
main_pass: render_pass.internal_render_pass(),
},
);
pipeline_desc.blender.targets.push(ColorBlendDesc {
mask: ColorMask::ALL,
blend: Some(BlendState::ALPHA),
});
let pipeline = unsafe {
let pipeline_build_result = gpu
.internal_logical_device()
.create_graphics_pipeline(&pipeline_desc, None);
match pipeline_build_result {
Ok(pipeline) => pipeline,
Err(e) => panic!("Failed to create graphics pipeline: {:?}", e),
}
};
return RenderPipeline {
pipeline_layout,
pipeline,
buffers: self.buffers,
};
}
}
#[derive(Debug)]
pub struct RenderPipeline<RenderBackend: Backend> {
pipeline_layout: RenderBackend::PipelineLayout,
pipeline: RenderBackend::GraphicsPipeline,
buffers: Vec<Buffer<RenderBackend>>,
}
impl<RenderBackend: Backend> RenderPipeline<RenderBackend> {
pub fn destroy(self, gpu: &super::gpu::Gpu<RenderBackend>) {
logging::debug!("Destroying render pipeline");
unsafe {
for buffer in self.buffers {
buffer.destroy(gpu);
}
gpu
.internal_logical_device()
.destroy_pipeline_layout(self.pipeline_layout);
gpu
.internal_logical_device()
.destroy_graphics_pipeline(self.pipeline);
}
}
}
impl<RenderBackend: Backend> RenderPipeline<RenderBackend> {
pub(super) fn internal_pipeline_layout(
&self,
) -> &RenderBackend::PipelineLayout {
return &self.pipeline_layout;
}
pub(super) fn internal_pipeline(&self) -> &RenderBackend::GraphicsPipeline {
return &self.pipeline;
}
}