use std::{
borrow::Borrow,
collections::HashMap,
ops::Range,
rc::Rc,
};
use gfx_hal::{
command::ClearValue,
device::Device,
pool::CommandPool as _,
prelude::CommandBuffer as GfxCommandBuffer,
};
use super::{
pipeline::RenderPipeline,
viewport::ViewPort,
};
pub enum CommandPoolFeatures {
ShortLivedBuffers,
ResetBuffersIndividually,
None,
All,
}
pub enum CommandBufferFeatures {
ResetEverySubmission,
TiedToRenderPass,
SimultaneousRecording,
None,
All,
}
pub enum CommandBufferLevel {
Primary,
Secondary,
}
pub enum Command<RenderBackend: gfx_hal::Backend> {
BeginRecording,
SetViewports {
start_at: u32,
viewports: Vec<ViewPort>,
},
SetScissors {
start_at: u32,
viewports: Vec<ViewPort>,
},
BeginRenderPass {
render_pass: Rc<super::render_pass::RenderPass<RenderBackend>>,
surface: Rc<super::surface::Surface<RenderBackend>>,
frame_buffer: Rc<super::framebuffer::Framebuffer<RenderBackend>>,
viewport: ViewPort,
},
EndRenderPass,
AttachGraphicsPipeline {
pipeline: Rc<RenderPipeline<RenderBackend>>,
},
Draw {
vertices: Range<u32>,
},
PushConstants {
pipeline: Rc<RenderPipeline<RenderBackend>>,
stage: super::pipeline::PipelineStage,
offset: u32,
bytes: Vec<u32>,
},
BindVertexBuffer {
buffer: Rc<super::buffer::Buffer<RenderBackend>>,
},
EndRecording,
}
pub struct CommandBuffer<'command_pool, RenderBackend: gfx_hal::Backend> {
command_buffer: &'command_pool mut RenderBackend::CommandBuffer,
flags: gfx_hal::command::CommandBufferFlags,
}
impl<'command_pool, RenderBackend: gfx_hal::Backend>
CommandBuffer<'command_pool, RenderBackend>
{
pub fn issue_command(&mut self, command: Command<RenderBackend>) {
use gfx_hal::command::CommandBuffer as _;
unsafe {
match command {
Command::BeginRecording => {
self.command_buffer.begin_primary(self.flags)
}
Command::SetViewports {
start_at,
viewports,
} => self.command_buffer.set_viewports(
start_at,
viewports
.into_iter()
.map(|viewport| viewport.internal_viewport()),
),
Command::SetScissors {
start_at,
viewports,
} => self.command_buffer.set_scissors(
start_at,
viewports
.into_iter()
.map(|viewport| viewport.internal_viewport().rect),
),
Command::BeginRenderPass {
render_pass,
frame_buffer,
surface,
viewport,
} => self.command_buffer.begin_render_pass(
render_pass.internal_render_pass(),
frame_buffer.internal_frame_buffer(),
viewport.internal_viewport().rect,
vec![gfx_hal::command::RenderAttachmentInfo::<RenderBackend> {
image_view: surface
.internal_surface_image()
.expect("No internal surface set when beginning the render pass.")
.borrow(),
clear_value: ClearValue {
color: gfx_hal::command::ClearColor {
float32: [0.0, 0.0, 0.0, 1.0],
},
},
}]
.into_iter(),
gfx_hal::command::SubpassContents::Inline,
),
Command::AttachGraphicsPipeline { pipeline } => self
.command_buffer
.bind_graphics_pipeline(pipeline.internal_pipeline()),
Command::EndRenderPass => self.command_buffer.end_render_pass(),
Command::PushConstants {
pipeline,
stage,
offset,
bytes,
} => self.command_buffer.push_graphics_constants(
pipeline.internal_pipeline_layout(),
stage,
offset,
bytes.as_slice(),
),
Command::Draw { vertices } => {
self.command_buffer.draw(vertices.clone(), 0..1)
}
Command::BindVertexBuffer { buffer } => {
self.command_buffer.bind_vertex_buffers(
0,
vec![(buffer.internal_buffer(), gfx_hal::buffer::SubRange::WHOLE)]
.into_iter(),
)
}
Command::EndRecording => self.command_buffer.finish(),
}
}
}
pub fn issue_commands(&mut self, commands: Vec<Command<RenderBackend>>) {
for command in commands {
self.issue_command(command);
}
}
pub fn reset(&mut self) {
unsafe {
self.command_buffer.reset(true);
}
}
}
pub struct CommandBufferBuilder {
flags: gfx_hal::command::CommandBufferFlags,
level: CommandBufferLevel,
}
impl CommandBufferBuilder {
pub fn new(level: CommandBufferLevel) -> Self {
let flags = gfx_hal::command::CommandBufferFlags::empty();
return CommandBufferBuilder { flags, level };
}
pub fn with_feature(mut self, feature: CommandBufferFeatures) -> Self {
let flags = match feature {
CommandBufferFeatures::ResetEverySubmission => {
gfx_hal::command::CommandBufferFlags::ONE_TIME_SUBMIT
}
CommandBufferFeatures::TiedToRenderPass => {
gfx_hal::command::CommandBufferFlags::RENDER_PASS_CONTINUE
}
CommandBufferFeatures::SimultaneousRecording => {
gfx_hal::command::CommandBufferFlags::SIMULTANEOUS_USE
}
CommandBufferFeatures::None => {
gfx_hal::command::CommandBufferFlags::empty()
}
CommandBufferFeatures::All => gfx_hal::command::CommandBufferFlags::all(),
};
self.flags.insert(flags);
return self;
}
pub fn build<'command_pool, RenderBackend: gfx_hal::Backend>(
self,
command_pool: &'command_pool mut CommandPool<RenderBackend>,
name: &str,
) -> CommandBuffer<'command_pool, RenderBackend> {
let command_buffer =
command_pool.fetch_or_allocate_command_buffer(name, self.level);
let flags = self.flags;
return CommandBuffer {
command_buffer,
flags,
};
}
}
pub struct CommandPoolBuilder {
command_pool_flags: gfx_hal::pool::CommandPoolCreateFlags,
}
pub mod internal {
pub fn command_buffer_for<
'render_context,
RenderBackend: gfx_hal::Backend,
>(
command_buffer: &'render_context super::CommandBuffer<
'render_context,
RenderBackend,
>,
) -> &'render_context RenderBackend::CommandBuffer {
return command_buffer.command_buffer;
}
}
impl CommandPoolBuilder {
pub fn new() -> Self {
return Self {
command_pool_flags: gfx_hal::pool::CommandPoolCreateFlags::empty(),
};
}
pub fn with_features(mut self, flag: CommandPoolFeatures) -> Self {
let flags = match flag {
CommandPoolFeatures::ShortLivedBuffers => {
gfx_hal::pool::CommandPoolCreateFlags::TRANSIENT
}
CommandPoolFeatures::ResetBuffersIndividually => {
gfx_hal::pool::CommandPoolCreateFlags::RESET_INDIVIDUAL
}
CommandPoolFeatures::None => {
gfx_hal::pool::CommandPoolCreateFlags::empty()
}
CommandPoolFeatures::All => gfx_hal::pool::CommandPoolCreateFlags::all(),
};
self.command_pool_flags.insert(flags);
return self;
}
pub fn build<B: gfx_hal::Backend>(
self,
gpu: &super::gpu::Gpu<B>,
) -> CommandPool<B> {
let command_pool = unsafe {
gpu
.internal_logical_device()
.create_command_pool(
gpu.internal_queue_family(),
self.command_pool_flags,
)
.expect("")
};
return CommandPool {
command_pool,
command_buffers: HashMap::new(),
};
}
}
pub struct CommandPool<RenderBackend: gfx_hal::Backend> {
command_pool: RenderBackend::CommandPool,
command_buffers: HashMap<String, RenderBackend::CommandBuffer>,
}
impl<RenderBackend: gfx_hal::Backend> CommandPool<RenderBackend> {
fn fetch_or_allocate_command_buffer(
&mut self,
name: &str,
level: CommandBufferLevel,
) -> &mut RenderBackend::CommandBuffer {
if self.command_buffers.contains_key(name) {
return self.command_buffers.get_mut(name).unwrap();
}
let buffer = unsafe {
self
.command_pool
.allocate_one(gfx_hal::command::Level::Primary)
};
self.command_buffers.insert(name.to_string(), buffer);
return self.command_buffers.get_mut(name).unwrap();
}
pub fn deallocate_command_buffer(&mut self, name: &str) {
if self.command_buffers.contains_key(name) == false {
return;
}
let buffer = self
.command_buffers
.remove(&name.to_string())
.expect(format!("Command Buffer {} doesn't exist", name).as_str());
unsafe { self.command_pool.free(vec![buffer].into_iter()) }
}
pub fn get_mutable_command_buffer(
&mut self,
name: &str,
) -> Option<&mut RenderBackend::CommandBuffer> {
return self.command_buffers.get_mut(name);
}
#[inline]
pub fn get_command_buffer(
&self,
name: &str,
) -> Option<&RenderBackend::CommandBuffer> {
return self.command_buffers.get(name);
}
#[inline]
pub fn reset_pool(&mut self, release_resources: bool) {
unsafe {
self.command_pool.reset(release_resources);
}
}
#[inline]
pub fn destroy(mut self, gpu: &super::gpu::Gpu<RenderBackend>) {
unsafe {
self.command_pool.reset(true);
gpu
.internal_logical_device()
.destroy_command_pool(self.command_pool);
}
}
}