summaryrefslogtreecommitdiff
path: root/engine/src/renderer.rs
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2026-03-20 14:22:19 +0100
committerHampusM <hampus@hampusmat.com>2026-03-20 14:22:19 +0100
commitf285f82072b491b1f3cc92db8e08485f26779d5a (patch)
treebf6c6c61cdfb3a12550e55966c8552957ade9e71 /engine/src/renderer.rs
parent0546d575c11d3668d0f95933697ae4f670fe2a55 (diff)
feat(engine): use slang for shadersHEADmaster
Diffstat (limited to 'engine/src/renderer.rs')
-rw-r--r--engine/src/renderer.rs299
1 files changed, 147 insertions, 152 deletions
diff --git a/engine/src/renderer.rs b/engine/src/renderer.rs
index 2a66a68..6c20102 100644
--- a/engine/src/renderer.rs
+++ b/engine/src/renderer.rs
@@ -1,5 +1,7 @@
use std::any::type_name;
use std::collections::VecDeque;
+use std::path::Path;
+use std::sync::LazyLock;
use std::sync::atomic::{AtomicU64, Ordering};
use bitflags::bitflags;
@@ -10,18 +12,28 @@ use ecs::query::term::Without;
use ecs::sole::Single;
use ecs::{Component, Query, declare_entity};
-use crate::asset::{Assets, Handle as AssetHandle};
+use crate::asset::{Assets, Handle as AssetHandle, Label as AssetLabel};
use crate::builder;
-use crate::camera::{Active as ActiveCamera, Camera};
+use crate::color::Color;
use crate::data_types::dimens::Dimens;
use crate::draw_flags::{DrawFlags, NoDraw, PolygonModeConfig};
-use crate::lighting::{DirectionalLight, GlobalLight, PointLight};
-use crate::material::{Flags as MaterialFlags, Material};
+use crate::image::Image;
use crate::mesh::Mesh;
-use crate::model::{Materials as ModelMaterials, Model, Spec as ModelSpec};
+use crate::model::{MaterialSearchResult, Model};
use crate::renderer::object::{Id as ObjectId, Store as ObjectStore};
-use crate::texture::Texture;
-use crate::transform::{Scale, Transform, WorldPosition};
+use crate::shader::cursor::{
+ BindingLocation as ShaderBindingLocation,
+ BindingValue as ShaderBindingValue,
+ Cursor as ShaderCursor,
+};
+use crate::shader::default::ASSET_LABEL as DEFAULT_SHADER_ASSET_LABEL;
+use crate::shader::{
+ Context as ShaderContext,
+ ModuleSource as ShaderModuleSource,
+ Program as ShaderProgram,
+ Shader,
+};
+use crate::texture::{Properties as TextureProperties, Texture};
use crate::windowing::window::Window;
pub mod object;
@@ -29,8 +41,14 @@ pub mod opengl;
static NEXT_SURFACE_ID: AtomicU64 = AtomicU64::new(0);
+pub static DEFAULT_TEXTURE_ASSET_LABEL: LazyLock<AssetLabel> =
+ LazyLock::new(|| AssetLabel {
+ path: Path::new("").into(),
+ name: Some("default_texture".into()),
+ });
+
declare_entity!(
- pub RENDER_PHASE,
+ pub PRE_RENDER_PHASE,
(
Phase,
Pair::builder()
@@ -40,6 +58,17 @@ declare_entity!(
)
);
+declare_entity!(
+ pub RENDER_PHASE,
+ (
+ Phase,
+ Pair::builder()
+ .relation::<ChildOf>()
+ .target_id(*PRE_RENDER_PHASE)
+ .build()
+ )
+);
+
builder! {
/// Window graphics properties.
#[builder(name=GraphicsPropertiesBuilder, derives=(Debug, Clone))]
@@ -131,23 +160,10 @@ pub enum Command
MakeCurrent(SurfaceId),
ClearBuffers(BufferClearMask),
SwapBuffers(SurfaceId),
- UseShader, // TODO: Add ability to specify shader
- ActivateShader,
- UseCamera(Camera, WorldPosition),
- ApplyTransform
- {
- transform: Transform,
- window_size: Dimens<u32>,
- },
- SetShaderDirectionalLights(Vec<DirectionalLight>),
- SetShaderPointLights(Vec<(PointLight, WorldPosition)>),
+ CreateShaderProgram(ObjectId, ShaderProgram),
+ ActivateShader(ObjectId),
+ SetShaderBinding(ShaderBindingLocation, ShaderBindingValue),
CreateTexture(Texture),
- UseMaterial
- {
- material_asset: Option<AssetHandle<Material>>,
- material_flags: MaterialFlags,
- global_light: GlobalLight,
- },
DrawMesh
{
mesh_asset: AssetHandle<Mesh>,
@@ -202,12 +218,19 @@ pub struct CtxUsedByWindow;
type RenderableEntity<'a> = (
&'a Model,
- Option<&'a MaterialFlags>,
- Option<&'a WorldPosition>,
- Option<&'a Scale>,
Option<&'a DrawFlags>,
+ Option<&'a Shader>,
+ Option<&'a mut PendingShaderBindings>,
);
+pub fn init(mut assets: Single<Assets>)
+{
+ assets.store_with_label(
+ DEFAULT_TEXTURE_ASSET_LABEL.clone(),
+ Image::from_color(Dimens { width: 1, height: 1 }, Color::WHITE_U8),
+ );
+}
+
#[tracing::instrument(skip_all)]
pub fn enqueue_commands(
renderer_ctx_query: Query<(
@@ -216,16 +239,15 @@ pub fn enqueue_commands(
&[Pair<CtxUsedByWindow, Wildcard>],
)>,
renderable_query: Query<RenderableEntity<'_>, (Without<NoDraw>,)>,
- point_light_query: Query<(&PointLight, &WorldPosition)>,
- directional_light_query: Query<(&DirectionalLight,)>,
- camera_query: Query<(&Camera, &WorldPosition, &ActiveCamera)>,
- global_light: Single<GlobalLight>,
assets: Single<Assets>,
+ shader_context: Single<ShaderContext>,
mut actions: Actions,
)
{
- let Some((camera, camera_world_pos, _)) = camera_query.iter().next() else {
- tracing::warn!("No current camera. Nothing will be rendered");
+ let Some(default_shader_asset) = assets
+ .get_handle_to_loaded::<ShaderModuleSource>(DEFAULT_SHADER_ASSET_LABEL.clone())
+ else {
+ tracing::error!("Default shader asset is not loaded");
return;
};
@@ -240,7 +262,7 @@ pub fn enqueue_commands(
continue;
};
- let Some(window) = window_ent.get::<Window>() else {
+ if window_ent.get::<Window>().is_none() {
tracing::debug!(
window_entity_id=%window_ent_id,
"Window entity does not have a {} component",
@@ -270,15 +292,38 @@ pub fn enqueue_commands(
command_queue.push(Command::MakeCurrent(surface_spec.id));
- command_queue
- .push(Command::UseCamera(camera.clone(), camera_world_pos.clone()));
+ let default_texture_asset = assets
+ .get_handle_to_loaded::<Image>(DEFAULT_TEXTURE_ASSET_LABEL.clone())
+ .expect("Not possible");
+
+ if !object_store
+ .contains_with_id(&ObjectId::Asset(default_texture_asset.id()))
+ {
+ command_queue.push(Command::CreateTexture(Texture {
+ asset_handle: default_texture_asset,
+ properties: TextureProperties::default(),
+ }));
+ }
command_queue.push(Command::ClearBuffers(
BufferClearMask::COLOR | BufferClearMask::DEPTH,
));
- for (model, material_flags, world_pos, scale, draw_flags) in &renderable_query
+ for (model, draw_flags, shader, mut pending_shader_bindings) in
+ &renderable_query
{
+ let shader_asset = match &shader {
+ Some(shader) => &shader.asset_handle,
+ None => &default_shader_asset,
+ };
+
+ if pending_shader_bindings.as_ref().map_or_else(
+ || true,
+ |pending_shader_bindings| pending_shader_bindings.bindings.is_empty(),
+ ) {
+ continue;
+ }
+
let Some(model_spec) = assets.get(&model.spec_asset) else {
continue;
};
@@ -289,103 +334,69 @@ pub fn enqueue_commands(
debug_assert!(model_spec.material_names.len() <= 1);
- let model_material_asset =
- match find_first_model_material(model_spec, &assets) {
- MaterialSearchResult::Found(model_material_asset) => {
- Some(model_material_asset.clone())
- }
- MaterialSearchResult::NotFound => {
- continue;
- }
- MaterialSearchResult::NoMaterials => None,
- };
+ let model_material_asset = match model_spec.find_first_material(&assets) {
+ MaterialSearchResult::Found(model_material_asset) => {
+ model_material_asset.clone()
+ // Some(model_material_asset.clone())
+ }
+ MaterialSearchResult::NotFound
+ | MaterialSearchResult::NoMaterials => {
+ // MaterialSearchResult::NotFound => {
+ continue;
+ } // MaterialSearchResult::NoMaterials => None,
+ };
- if let Some(model_material_asset) = &model_material_asset {
- let Some(model_material) = assets.get(model_material_asset) else {
- unreachable!();
+ if !object_store.contains_with_id(&ObjectId::Asset(shader_asset.id())) {
+ let Some(shader_program) =
+ shader_context.get_program(&shader_asset.id())
+ else {
+ tracing::error!(
+ "Shader context doesn't have a program for shader asset {:?}",
+ assets.get_label(&shader_asset)
+ );
+ continue;
};
- if let Some(ambient_map) = &model_material.ambient_map {
- if assets.get(&ambient_map.asset_handle).is_none() {
- continue;
- }
- }
+ command_queue.push(Command::CreateShaderProgram(
+ ObjectId::Asset(shader_asset.id()),
+ shader_program.clone(),
+ ));
+ }
- if let Some(diffuse_map) = &model_material.diffuse_map {
- if assets.get(&diffuse_map.asset_handle).is_none() {
- continue;
- }
- }
+ command_queue
+ .push(Command::ActivateShader(ObjectId::Asset(shader_asset.id())));
- if let Some(specular_map) = &model_material.specular_map {
- if assets.get(&specular_map.asset_handle).is_none() {
- continue;
- }
+ let Some(model_material) = assets.get(&model_material_asset) else {
+ // TODO: Handle this case since it may occur
+ unreachable!();
+ };
+
+ for texture in [
+ &model_material.ambient_map,
+ &model_material.diffuse_map,
+ &model_material.specular_map,
+ ]
+ .into_iter()
+ .flatten()
+ {
+ if !object_store
+ .contains_with_id(&ObjectId::Asset(texture.asset_handle.id()))
+ {
+ command_queue.push(Command::CreateTexture(texture.clone()));
}
}
- command_queue.push(Command::UseShader);
-
- command_queue.push(Command::ApplyTransform {
- transform: Transform {
- position: world_pos
- .as_deref()
- .cloned()
- .unwrap_or_default()
- .position,
- scale: scale.as_deref().cloned().unwrap_or_default().scale,
- },
- window_size: *window.inner_size(),
- });
-
- command_queue.push(Command::SetShaderDirectionalLights(
- directional_light_query
- .iter()
- .map(|(dir_light,)| dir_light.clone())
- .collect::<Vec<_>>(),
- ));
-
- command_queue.push(Command::SetShaderPointLights(
- point_light_query
- .iter()
- .map(|(point_light, point_light_world_pos)| {
- (point_light.clone(), point_light_world_pos.clone())
- })
- .collect::<Vec<_>>(),
- ));
-
- if let Some(model_material_asset) = &model_material_asset {
- let Some(model_material) = assets.get(model_material_asset) else {
- unreachable!();
- };
-
- for texture in [
- &model_material.specular_map,
- &model_material.diffuse_map,
- &model_material.specular_map,
- ]
- .into_iter()
- .flatten()
+ if let Some(pending_shader_bindings) = &mut pending_shader_bindings {
+ for (shader_binding_loc, shader_binding_val) in
+ pending_shader_bindings.bindings.drain(..)
{
- if !object_store
- .contains_with_id(&ObjectId::Asset(texture.asset_handle.id()))
- {
- command_queue.push(Command::CreateTexture(texture.clone()));
- }
+ command_queue.push(Command::SetShaderBinding(
+ shader_binding_loc,
+ shader_binding_val,
+ ));
}
}
- command_queue.push(Command::UseMaterial {
- material_asset: model_material_asset,
- material_flags: material_flags
- .as_deref()
- .cloned()
- .unwrap_or_default(),
- global_light: global_light.clone(),
- });
-
- command_queue.push(Command::ActivateShader);
-
if let Some(draw_flags) = draw_flags.as_deref()
&& draw_flags.polygon_mode_config != PolygonModeConfig::default()
{
@@ -410,39 +421,23 @@ pub fn enqueue_commands(
}
}
-enum MaterialSearchResult<'a>
+#[derive(Default, Clone, Component)]
+pub struct PendingShaderBindings
{
- Found(&'a AssetHandle<Material>),
- NotFound,
- NoMaterials,
+ pub bindings: Vec<(ShaderBindingLocation, ShaderBindingValue)>,
}
-fn find_first_model_material<'assets>(
- model_spec: &'assets ModelSpec,
- assets: &'assets Assets,
-) -> MaterialSearchResult<'assets>
+impl<'a> Extend<(ShaderCursor<'a>, ShaderBindingValue)> for PendingShaderBindings
{
- let Some(material_name) = model_spec.material_names.first() else {
- return MaterialSearchResult::NoMaterials;
- };
-
- let Some(material_asset) = (match &model_spec.materials {
- ModelMaterials::Maps(material_asset_map_assets) => material_asset_map_assets
- .iter()
- .find_map(|mat_asset_map_asset| {
- let mat_asset_map = assets.get(mat_asset_map_asset)?;
-
- mat_asset_map.assets.get(material_name)
- }),
- ModelMaterials::Direct(material_assets) => material_assets.get(material_name),
- }) else {
- return MaterialSearchResult::NotFound;
- };
-
- if assets.get(material_asset).is_none() {
- tracing::trace!("Missing material asset");
- return MaterialSearchResult::NotFound;
+ fn extend<Iter: IntoIterator<Item = (ShaderCursor<'a>, ShaderBindingValue)>>(
+ &mut self,
+ iter: Iter,
+ )
+ {
+ self.bindings.extend(iter.into_iter().map(
+ |(shader_cursor, shader_binding_val)| {
+ (shader_cursor.into_binding_location(), shader_binding_val)
+ },
+ ))
}
-
- MaterialSearchResult::Found(material_asset)
}