1use std::{
2 borrow::Borrow,
3 collections::HashMap,
4 ops::Range,
5 rc::Rc,
6};
7
8use gfx_hal::{
9 command::ClearValue,
10 device::Device,
11 pool::CommandPool as _,
12 prelude::CommandBuffer as GfxCommandBuffer,
13};
14
15use super::{
16 pipeline::RenderPipeline,
17 viewport::ViewPort,
18};
19
20pub enum CommandPoolFeatures {
23 ShortLivedBuffers,
27 ResetBuffersIndividually,
30 None,
32 All,
34}
35
36pub enum CommandBufferFeatures {
39 ResetEverySubmission,
42 TiedToRenderPass,
45 SimultaneousRecording,
48 None,
50 All,
52}
53
54pub enum CommandBufferLevel {
57 Primary,
61 Secondary,
63}
64
65pub enum Command<RenderBackend: gfx_hal::Backend> {
69 BeginRecording,
72 SetViewports {
73 start_at: u32,
74 viewports: Vec<ViewPort>,
75 },
76 SetScissors {
77 start_at: u32,
78 viewports: Vec<ViewPort>,
79 },
80 BeginRenderPass {
81 render_pass: Rc<super::render_pass::RenderPass<RenderBackend>>,
82 surface: Rc<super::surface::Surface<RenderBackend>>,
83 frame_buffer: Rc<super::framebuffer::Framebuffer<RenderBackend>>,
84 viewport: ViewPort,
85 },
86 EndRenderPass,
88 AttachGraphicsPipeline {
89 pipeline: Rc<RenderPipeline<RenderBackend>>,
90 },
91 Draw {
92 vertices: Range<u32>,
93 },
94 PushConstants {
95 pipeline: Rc<RenderPipeline<RenderBackend>>,
96 stage: super::pipeline::PipelineStage,
97 offset: u32,
98 bytes: Vec<u32>,
99 },
100 BindVertexBuffer {
101 buffer: Rc<super::buffer::Buffer<RenderBackend>>,
102 },
103 EndRecording,
104}
105
106pub struct CommandBuffer<'command_pool, RenderBackend: gfx_hal::Backend> {
110 command_buffer: &'command_pool mut RenderBackend::CommandBuffer,
111 flags: gfx_hal::command::CommandBufferFlags,
112}
113
114impl<'command_pool, RenderBackend: gfx_hal::Backend>
115 CommandBuffer<'command_pool, RenderBackend>
116{
117 pub fn issue_command(&mut self, command: Command<RenderBackend>) {
125 use gfx_hal::command::CommandBuffer as _;
126 unsafe {
127 match command {
128 Command::BeginRecording => {
129 self.command_buffer.begin_primary(self.flags)
130 }
131 Command::SetViewports {
132 start_at,
133 viewports,
134 } => self.command_buffer.set_viewports(
135 start_at,
136 viewports
137 .into_iter()
138 .map(|viewport| viewport.internal_viewport()),
139 ),
140 Command::SetScissors {
141 start_at,
142 viewports,
143 } => self.command_buffer.set_scissors(
144 start_at,
145 viewports
146 .into_iter()
147 .map(|viewport| viewport.internal_viewport().rect),
148 ),
149
150 Command::BeginRenderPass {
151 render_pass,
152 frame_buffer,
153 surface,
154 viewport,
155 } => self.command_buffer.begin_render_pass(
156 render_pass.internal_render_pass(),
157 frame_buffer.internal_frame_buffer(),
158 viewport.internal_viewport().rect,
159 vec![gfx_hal::command::RenderAttachmentInfo::<RenderBackend> {
160 image_view: surface
161 .internal_surface_image()
162 .expect("No internal surface set when beginning the render pass.")
163 .borrow(),
164 clear_value: ClearValue {
165 color: gfx_hal::command::ClearColor {
166 float32: [0.0, 0.0, 0.0, 1.0],
167 },
168 },
169 }]
170 .into_iter(),
171 gfx_hal::command::SubpassContents::Inline,
172 ),
173 Command::AttachGraphicsPipeline { pipeline } => self
174 .command_buffer
175 .bind_graphics_pipeline(pipeline.internal_pipeline()),
176 Command::EndRenderPass => self.command_buffer.end_render_pass(),
177 Command::PushConstants {
178 pipeline,
179 stage,
180 offset,
181 bytes,
182 } => self.command_buffer.push_graphics_constants(
183 pipeline.internal_pipeline_layout(),
184 stage,
185 offset,
186 bytes.as_slice(),
187 ),
188 Command::Draw { vertices } => {
189 self.command_buffer.draw(vertices.clone(), 0..1)
190 }
191 Command::BindVertexBuffer { buffer } => {
192 self.command_buffer.bind_vertex_buffers(
193 0,
194 vec![(buffer.internal_buffer(), gfx_hal::buffer::SubRange::WHOLE)]
195 .into_iter(),
196 )
197 }
198 Command::EndRecording => self.command_buffer.finish(),
199 }
200 }
201 }
202
203 pub fn issue_commands(&mut self, commands: Vec<Command<RenderBackend>>) {
207 for command in commands {
208 self.issue_command(command);
209 }
210 }
211
212 pub fn reset(&mut self) {
213 unsafe {
214 self.command_buffer.reset(true);
215 }
216 }
217}
218
219pub struct CommandBufferBuilder {
222 flags: gfx_hal::command::CommandBufferFlags,
223 level: CommandBufferLevel,
224}
225
226impl CommandBufferBuilder {
227 pub fn new(level: CommandBufferLevel) -> Self {
228 let flags = gfx_hal::command::CommandBufferFlags::empty();
229 return CommandBufferBuilder { flags, level };
230 }
231
232 pub fn with_feature(mut self, feature: CommandBufferFeatures) -> Self {
233 let flags = match feature {
234 CommandBufferFeatures::ResetEverySubmission => {
235 gfx_hal::command::CommandBufferFlags::ONE_TIME_SUBMIT
236 }
237 CommandBufferFeatures::TiedToRenderPass => {
238 gfx_hal::command::CommandBufferFlags::RENDER_PASS_CONTINUE
239 }
240 CommandBufferFeatures::SimultaneousRecording => {
241 gfx_hal::command::CommandBufferFlags::SIMULTANEOUS_USE
242 }
243 CommandBufferFeatures::None => {
244 gfx_hal::command::CommandBufferFlags::empty()
245 }
246 CommandBufferFeatures::All => gfx_hal::command::CommandBufferFlags::all(),
247 };
248
249 self.flags.insert(flags);
250 return self;
251 }
252
253 pub fn build<'command_pool, RenderBackend: gfx_hal::Backend>(
256 self,
257 command_pool: &'command_pool mut CommandPool<RenderBackend>,
258 name: &str,
259 ) -> CommandBuffer<'command_pool, RenderBackend> {
260 let command_buffer =
261 command_pool.fetch_or_allocate_command_buffer(name, self.level);
262
263 let flags = self.flags;
264
265 return CommandBuffer {
266 command_buffer,
267 flags,
268 };
269 }
270}
271
272pub struct CommandPoolBuilder {
273 command_pool_flags: gfx_hal::pool::CommandPoolCreateFlags,
274}
275
276pub mod internal {
277 pub fn command_buffer_for<
278 'render_context,
279 RenderBackend: gfx_hal::Backend,
280 >(
281 command_buffer: &'render_context super::CommandBuffer<
282 'render_context,
283 RenderBackend,
284 >,
285 ) -> &'render_context RenderBackend::CommandBuffer {
286 return command_buffer.command_buffer;
287 }
288}
289
290impl CommandPoolBuilder {
291 pub fn new() -> Self {
292 return Self {
293 command_pool_flags: gfx_hal::pool::CommandPoolCreateFlags::empty(),
294 };
295 }
296
297 pub fn with_features(mut self, flag: CommandPoolFeatures) -> Self {
299 let flags = match flag {
300 CommandPoolFeatures::ShortLivedBuffers => {
301 gfx_hal::pool::CommandPoolCreateFlags::TRANSIENT
302 }
303 CommandPoolFeatures::ResetBuffersIndividually => {
304 gfx_hal::pool::CommandPoolCreateFlags::RESET_INDIVIDUAL
305 }
306 CommandPoolFeatures::None => {
307 gfx_hal::pool::CommandPoolCreateFlags::empty()
308 }
309 CommandPoolFeatures::All => gfx_hal::pool::CommandPoolCreateFlags::all(),
310 };
311
312 self.command_pool_flags.insert(flags);
313 return self;
314 }
315
316 pub fn build<B: gfx_hal::Backend>(
318 self,
319 gpu: &super::gpu::Gpu<B>,
320 ) -> CommandPool<B> {
321 let command_pool = unsafe {
322 gpu
323 .internal_logical_device()
324 .create_command_pool(
325 gpu.internal_queue_family(),
326 self.command_pool_flags,
327 )
328 .expect("")
329 };
330
331 return CommandPool {
332 command_pool,
333 command_buffers: HashMap::new(),
334 };
335 }
336}
337
338pub struct CommandPool<RenderBackend: gfx_hal::Backend> {
339 command_pool: RenderBackend::CommandPool,
340 command_buffers: HashMap<String, RenderBackend::CommandBuffer>,
341}
342
343impl<RenderBackend: gfx_hal::Backend> CommandPool<RenderBackend> {
344 fn fetch_or_allocate_command_buffer(
346 &mut self,
347 name: &str,
348 level: CommandBufferLevel,
349 ) -> &mut RenderBackend::CommandBuffer {
350 if self.command_buffers.contains_key(name) {
351 return self.command_buffers.get_mut(name).unwrap();
352 }
353
354 let buffer = unsafe {
355 self
356 .command_pool
357 .allocate_one(gfx_hal::command::Level::Primary)
358 };
359
360 self.command_buffers.insert(name.to_string(), buffer);
361 return self.command_buffers.get_mut(name).unwrap();
362 }
363
364 pub fn deallocate_command_buffer(&mut self, name: &str) {
368 if self.command_buffers.contains_key(name) == false {
369 return;
370 }
371
372 let buffer = self
373 .command_buffers
374 .remove(&name.to_string())
375 .expect(format!("Command Buffer {} doesn't exist", name).as_str());
376
377 unsafe { self.command_pool.free(vec![buffer].into_iter()) }
378 }
379
380 pub fn get_mutable_command_buffer(
383 &mut self,
384 name: &str,
385 ) -> Option<&mut RenderBackend::CommandBuffer> {
386 return self.command_buffers.get_mut(name);
387 }
388
389 #[inline]
392 pub fn get_command_buffer(
393 &self,
394 name: &str,
395 ) -> Option<&RenderBackend::CommandBuffer> {
396 return self.command_buffers.get(name);
397 }
398
399 #[inline]
401 pub fn reset_pool(&mut self, release_resources: bool) {
402 unsafe {
403 self.command_pool.reset(release_resources);
404 }
405 }
406
407 #[inline]
410 pub fn destroy(mut self, gpu: &super::gpu::Gpu<RenderBackend>) {
411 unsafe {
412 self.command_pool.reset(true);
413 gpu
414 .internal_logical_device()
415 .destroy_command_pool(self.command_pool);
416 }
417 }
418}