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}