khora_core/renderer/traits/graphics_device.rs
1// Copyright 2025 eraflo
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::math::dimension;
16use crate::renderer::api::{
17 command::{
18 BindGroupDescriptor, BindGroupId, BindGroupLayoutDescriptor, BindGroupLayoutId,
19 CommandBufferId, ComputePipelineDescriptor, ComputePipelineId,
20 },
21 core::{GraphicsAdapterInfo, ShaderModuleDescriptor, ShaderModuleId},
22 pipeline::{
23 PipelineLayoutDescriptor, PipelineLayoutId, RenderPipelineDescriptor, RenderPipelineId,
24 },
25 resource::{
26 BufferDescriptor, BufferId, SamplerDescriptor, SamplerId, TextureDescriptor, TextureId,
27 TextureViewDescriptor, TextureViewId,
28 },
29 util::TextureFormat,
30};
31use crate::renderer::error::ResourceError;
32use crate::renderer::traits::CommandEncoder;
33use std::fmt::Debug;
34use std::future::Future;
35
36/// Defines the abstract interface for a graphics device.
37///
38/// This trait is the central point of interaction with the underlying graphics API
39/// (like WGPU, Vulkan, etc.). It abstracts away the specifics of the backend and
40/// provides a unified API for creating, managing, and destroying GPU resources such
41/// as buffers, textures, and pipelines.
42///
43/// It is the cornerstone of the `khora-infra` crate's responsibility, where a
44/// concrete type will implement this trait to provide actual rendering capabilities.
45pub trait GraphicsDevice: Send + Sync + Debug + 'static {
46 // --- Shader Management ---
47
48 /// Creates a shader module from a descriptor.
49 ///
50 /// # Errors
51 /// Returns a [`ResourceError`] if the shader source is invalid or fails to compile.
52 fn create_shader_module(
53 &self,
54 descriptor: &ShaderModuleDescriptor,
55 ) -> Result<ShaderModuleId, ResourceError>;
56
57 /// Destroys a shader module, releasing its GPU resources.
58 fn destroy_shader_module(&self, id: ShaderModuleId) -> Result<(), ResourceError>;
59
60 // --- Pipeline Management ---
61
62 /// Creates a render pipeline from a descriptor.
63 ///
64 /// A render pipeline represents the entire configurable state of the GPU for a
65 /// draw call, including shaders, vertex layouts, and blend states.
66 ///
67 /// # Errors
68 /// Returns a [`ResourceError`] if the pipeline configuration is invalid or compilation fails.
69 fn create_render_pipeline(
70 &self,
71 descriptor: &RenderPipelineDescriptor,
72 ) -> Result<RenderPipelineId, ResourceError>;
73
74 /// Creates a pipeline layout from a descriptor.
75 ///
76 /// The pipeline layout defines the set of resource bindings (e.g., uniform buffers,
77 /// textures) that a pipeline can access.
78 ///
79 /// # Errors
80 /// Returns a [`ResourceError`] if the layout is invalid.
81 fn create_pipeline_layout(
82 &self,
83 descriptor: &PipelineLayoutDescriptor,
84 ) -> Result<PipelineLayoutId, ResourceError>;
85
86 /// Destroys a render pipeline, releasing its GPU resources.
87 fn destroy_render_pipeline(&self, id: RenderPipelineId) -> Result<(), ResourceError>;
88
89 // --- Compute Pipeline Management ---
90
91 /// Creates a compute pipeline from a descriptor.
92 ///
93 /// # Errors
94 /// Returns a [`ResourceError`] if the pipeline configuration is invalid or compilation fails.
95 fn create_compute_pipeline(
96 &self,
97 descriptor: &ComputePipelineDescriptor,
98 ) -> Result<ComputePipelineId, ResourceError>;
99
100 /// Destroys a compute pipeline, releasing its GPU resources.
101 fn destroy_compute_pipeline(&self, id: ComputePipelineId) -> Result<(), ResourceError>;
102
103 // --- Bind Group Management ---
104
105 /// Creates a bind group layout from a descriptor.
106 ///
107 /// A bind group layout describes the structure and types of resources that can be
108 /// bound to shaders at a specific bind group index.
109 ///
110 /// # Errors
111 /// Returns a [`ResourceError`] if the layout is invalid.
112 fn create_bind_group_layout(
113 &self,
114 descriptor: &BindGroupLayoutDescriptor,
115 ) -> Result<BindGroupLayoutId, ResourceError>;
116
117 /// Creates a bind group from a descriptor.
118 ///
119 /// A bind group represents the actual bound resources (buffers, textures, etc.)
120 /// that conform to a specific bind group layout.
121 ///
122 /// # Errors
123 /// Returns a [`ResourceError`] if the bind group configuration is invalid or
124 /// doesn't match its layout.
125 fn create_bind_group(
126 &self,
127 descriptor: &BindGroupDescriptor,
128 ) -> Result<BindGroupId, ResourceError>;
129
130 /// Destroys a bind group layout, releasing its resources.
131 fn destroy_bind_group_layout(&self, id: BindGroupLayoutId) -> Result<(), ResourceError>;
132
133 /// Destroys a bind group, releasing its resources.
134 fn destroy_bind_group(&self, id: BindGroupId) -> Result<(), ResourceError>;
135
136 // --- Buffer Management ---
137
138 /// Creates a new GPU buffer.
139 fn create_buffer(&self, descriptor: &BufferDescriptor) -> Result<BufferId, ResourceError>;
140
141 /// Creates a new GPU buffer and initializes it with the provided data.
142 /// This is often more efficient than `create_buffer` followed by `write_buffer` for static data.
143 fn create_buffer_with_data(
144 &self,
145 descriptor: &BufferDescriptor,
146 data: &[u8],
147 ) -> Result<BufferId, ResourceError>;
148
149 /// Destroys a GPU buffer, releasing its memory.
150 fn destroy_buffer(&self, id: BufferId) -> Result<(), ResourceError>;
151
152 /// Writes data to a specific region of a GPU buffer.
153 ///
154 /// # Arguments
155 /// * `id`: The identifier of the buffer to write to.
156 /// * `offset`: The offset in bytes from the beginning of the buffer to start writing.
157 /// * `data`: The slice of bytes to write into the buffer.
158 fn write_buffer(&self, id: BufferId, offset: u64, data: &[u8]) -> Result<(), ResourceError>;
159
160 /// Asynchronously writes data to a GPU buffer.
161 /// This can be more performant for large data uploads by avoiding stalls.
162 fn write_buffer_async<'a>(
163 &'a self,
164 id: BufferId,
165 offset: u64,
166 data: &'a [u8],
167 ) -> Box<dyn Future<Output = Result<(), ResourceError>> + Send + 'static>;
168
169 // --- Texture & Sampler Management ---
170
171 /// Creates a new GPU texture.
172 fn create_texture(&self, descriptor: &TextureDescriptor) -> Result<TextureId, ResourceError>;
173
174 /// Destroys a GPU texture, releasing its memory.
175 fn destroy_texture(&self, id: TextureId) -> Result<(), ResourceError>;
176
177 /// Writes data to a specific region of a GPU texture.
178 ///
179 /// # Arguments
180 /// * `texture_id`: The identifier of the texture to write to.
181 /// * `data`: The raw image data to write.
182 /// * `bytes_per_row`: The number of bytes for a single row of texels in `data`.
183 /// * `offset`: The 3D offset (x, y, z/layer) in the texture to start writing.
184 /// * `size`: The 3D extent (width, height, depth/layers) of the data to write.
185 fn write_texture(
186 &self,
187 texture_id: TextureId,
188 data: &[u8],
189 bytes_per_row: Option<u32>,
190 offset: dimension::Origin3D,
191 size: dimension::Extent3D,
192 ) -> Result<(), ResourceError>;
193
194 /// Creates a new texture view for a given texture.
195 /// A view describes how a shader will interpret a texture's data (e.g., its format, mip levels).
196 fn create_texture_view(
197 &self,
198 texture_id: TextureId,
199 descriptor: &TextureViewDescriptor,
200 ) -> Result<TextureViewId, ResourceError>;
201
202 /// Destroys a texture view.
203 fn destroy_texture_view(&self, id: TextureViewId) -> Result<(), ResourceError>;
204
205 /// Creates a new sampler.
206 /// A sampler defines how a shader will sample from a texture (e.g., filtering, wrapping).
207 fn create_sampler(&self, descriptor: &SamplerDescriptor) -> Result<SamplerId, ResourceError>;
208
209 /// Destroys a sampler.
210 fn destroy_sampler(&self, id: SamplerId) -> Result<(), ResourceError>;
211
212 // --- Command Management ---
213
214 /// Creates a new command encoder to record GPU commands.
215 ///
216 /// # Arguments
217 /// * `label`: An optional debug label for the command encoder.
218 fn create_command_encoder(&self, label: Option<&str>) -> Box<dyn CommandEncoder>;
219
220 /// Submits a previously recorded command buffer to the GPU's command queue for execution.
221 fn submit_command_buffer(&self, command_buffer: CommandBufferId);
222
223 // --- Device Introspection ---
224
225 /// Gets the texture format of the primary render surface.
226 fn get_surface_format(&self) -> Option<TextureFormat>;
227
228 /// Gets information about the active graphics adapter (GPU).
229 fn get_adapter_info(&self) -> GraphicsAdapterInfo;
230
231 /// Checks if a specific, optional rendering feature is supported by the backend.
232 fn supports_feature(&self, feature_name: &str) -> bool;
233}