lambda_platform/gfx/
gpu.rs1use gfx_hal::{
2 adapter::Adapter,
3 prelude::{
4 PhysicalDevice,
5 QueueFamily,
6 },
7 queue::{
8 Queue,
9 QueueGroup,
10 },
11};
12#[cfg(test)]
13use mockall::automock;
14
15use super::{
16 command::CommandBuffer,
17 fence::{
18 RenderSemaphore,
19 RenderSubmissionFence,
20 },
21 surface,
22};
23
24pub struct GpuBuilder {
26 render_queue_type: RenderQueueType,
27}
28
29impl GpuBuilder {
30 pub fn new() -> Self {
32 return Self {
33 render_queue_type: RenderQueueType::Graphical,
34 };
35 }
36
37 pub fn with_render_queue_type(mut self, queue_type: RenderQueueType) -> Self {
39 self.render_queue_type = queue_type;
40 return self;
41 }
42
43 pub fn build<RenderBackend: gfx_hal::Backend>(
46 self,
47 instance: &mut super::Instance<RenderBackend>,
48 surface: Option<&surface::Surface<RenderBackend>>,
49 ) -> Result<Gpu<RenderBackend>, String> {
50 match (surface, self.render_queue_type) {
51 (Some(surface), RenderQueueType::Graphical) => {
52 let adapter = instance.first_adapter();
53
54 let queue_family = adapter
55 .queue_families
56 .iter()
57 .find(|family| {
58 return surface.can_support_queue_family(family)
59 && family.queue_type().supports_graphics();
60 })
61 .expect("No compatible queue family found.")
62 .id();
63
64 return Ok(Gpu::new(adapter, queue_family));
65 }
66 (Some(_surface), RenderQueueType::Compute) => {
67 todo!("Support a Compute based GPU.")
68 }
69 (_, _) => return Err("Failed to build GPU.".to_string()),
70 }
71 }
72}
73
74pub struct Gpu<B: gfx_hal::Backend> {
76 adapter: gfx_hal::adapter::Adapter<B>,
77 gpu: gfx_hal::adapter::Gpu<B>,
78 queue_group: QueueGroup<B>,
79}
80
81#[derive(Clone, Copy, Debug, PartialEq)]
83pub enum RenderQueueType {
84 Compute,
85 Graphical,
86 GraphicalCompute,
87 Transfer,
88}
89
90impl<RenderBackend: gfx_hal::Backend> Gpu<RenderBackend> {
91 pub(super) fn new(
95 adapter: Adapter<RenderBackend>,
96 queue_family: gfx_hal::queue::QueueFamilyId,
97 ) -> Self {
98 let queue_family = adapter
99 .queue_families
100 .iter()
101 .find(|family| family.id() == queue_family)
102 .expect("Failed to find the queue family requested for the GPU.");
103
104 let mut gpu = unsafe {
105 adapter
106 .physical_device
107 .open(&[(queue_family, &[1.0])], gfx_hal::Features::empty())
108 .expect("Failed to open the device.")
109 };
110
111 let queue_group = gpu.queue_groups.pop().unwrap();
112
113 return Self {
114 adapter,
115 gpu,
116 queue_group,
117 };
118 }
119
120 pub fn submit_command_buffer<'render_context>(
122 &mut self,
123 command_buffer: &mut CommandBuffer<RenderBackend>,
124 signal_semaphores: Vec<&RenderSemaphore<RenderBackend>>,
125 fence: &mut RenderSubmissionFence<RenderBackend>,
126 ) {
127 let commands =
128 vec![super::command::internal::command_buffer_for(command_buffer)]
129 .into_iter();
130 unsafe {
131 self
132 .queue_group
133 .queues
134 .first_mut()
135 .expect("Couldn't find the primary queue to submit commands to. ")
136 .submit(
137 commands,
138 vec![].into_iter(),
139 signal_semaphores.into_iter().map(|semaphore| {
142 return semaphore.internal_semaphore();
143 }),
144 Some(fence.internal_fence_mut()),
145 );
146 }
147 }
148
149 pub fn render_to_surface(
151 &mut self,
152 surface: &mut surface::Surface<RenderBackend>,
153 semaphore: &mut RenderSemaphore<RenderBackend>,
154 ) -> Result<(), &str> {
155 let (render_surface, render_image) = surface.internal_surface_and_image();
156
157 let result = unsafe {
158 self.queue_group.queues[0].present(
159 render_surface,
160 render_image,
161 Some(semaphore.internal_semaphore_mut()),
162 )
163 };
164
165 if result.is_err() {
166 logging::error!(
167 "Failed to present to the surface: {:?}",
168 result.err().unwrap()
169 );
170 surface.remove_swapchain(self);
171 return Err(
172 "Rendering failed. Swapchain for the surface needs to be reconfigured.",
173 );
174 }
175
176 return Ok(());
177 }
178}
179
180impl<RenderBackend: gfx_hal::Backend> Gpu<RenderBackend> {
181 pub(super) fn internal_logical_device(&self) -> &RenderBackend::Device {
182 return &self.gpu.device;
183 }
184
185 pub(super) fn internal_physical_device(
186 &self,
187 ) -> &RenderBackend::PhysicalDevice {
188 return &self.adapter.physical_device;
189 }
190
191 pub(super) fn internal_queue_family(&self) -> gfx_hal::queue::QueueFamilyId {
192 return self.queue_group.family;
193 }
194}
195
196#[cfg(test)]
197mod tests {
198 #[test]
199 fn test_gpu_builder_default_state() {
200 use super::{
201 GpuBuilder,
202 RenderQueueType,
203 };
204
205 let builder = GpuBuilder::new();
206
207 assert_eq!(builder.render_queue_type, RenderQueueType::Graphical);
208 }
209
210 #[test]
211 fn test_gpu_builder_with_render_queue_type() {
212 use super::{
213 GpuBuilder,
214 RenderQueueType,
215 };
216
217 let builder =
218 GpuBuilder::new().with_render_queue_type(RenderQueueType::Compute);
219
220 assert_eq!(builder.render_queue_type, RenderQueueType::Compute);
221 }
222
223 #[test]
224 fn test_gpu_builder_build() {}
225}