From 080ac92fbb58131975da45e5366f9a9d3f456109 Mon Sep 17 00:00:00 2001 From: tolelom <98kimsungmin@naver.com> Date: Tue, 24 Mar 2026 21:38:45 +0900 Subject: [PATCH] fix(renderer): merge IBL into group(3) to stay within max_bind_groups limit of 4 wgpu's default max_bind_groups is 4 (groups 0-3), but the PBR shader was using group(4) for BRDF LUT bindings. This merges IBL bindings into the shadow bind group (group 3) at binding slots 3-4, removes the standalone IBL bind group layout/creation, and updates all examples accordingly. Co-Authored-By: Claude Opus 4.6 (1M context) --- crates/voltex_renderer/src/ibl.rs | 47 ---------------------- crates/voltex_renderer/src/pbr_pipeline.rs | 3 +- crates/voltex_renderer/src/pbr_shader.wgsl | 5 +-- crates/voltex_renderer/src/shadow.rs | 28 +++++++++++++ examples/ibl_demo/src/main.rs | 8 +--- examples/multi_light_demo/src/main.rs | 8 +--- examples/pbr_demo/src/main.rs | 8 +--- examples/shadow_demo/src/main.rs | 8 +--- 8 files changed, 39 insertions(+), 76 deletions(-) diff --git a/crates/voltex_renderer/src/ibl.rs b/crates/voltex_renderer/src/ibl.rs index 4b42ea6..5ae2a34 100644 --- a/crates/voltex_renderer/src/ibl.rs +++ b/crates/voltex_renderer/src/ibl.rs @@ -79,51 +79,4 @@ impl IblResources { } } - /// Bind group layout for group(4): - /// binding 0 — texture_2d (filterable) - /// binding 1 — sampler (filtering) - pub fn bind_group_layout(device: &wgpu::Device) -> wgpu::BindGroupLayout { - device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: Some("IblBindGroupLayout"), - entries: &[ - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Texture { - multisampled: false, - view_dimension: wgpu::TextureViewDimension::D2, - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), - count: None, - }, - ], - }) - } - - pub fn create_bind_group( - &self, - device: &wgpu::Device, - layout: &wgpu::BindGroupLayout, - ) -> wgpu::BindGroup { - device.create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("IblBindGroup"), - layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::TextureView(&self.brdf_lut_view), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::Sampler(&self.brdf_lut_sampler), - }, - ], - }) - } } diff --git a/crates/voltex_renderer/src/pbr_pipeline.rs b/crates/voltex_renderer/src/pbr_pipeline.rs index a147b24..1cd82f3 100644 --- a/crates/voltex_renderer/src/pbr_pipeline.rs +++ b/crates/voltex_renderer/src/pbr_pipeline.rs @@ -8,7 +8,6 @@ pub fn create_pbr_pipeline( texture_layout: &wgpu::BindGroupLayout, material_layout: &wgpu::BindGroupLayout, shadow_layout: &wgpu::BindGroupLayout, - ibl_layout: &wgpu::BindGroupLayout, ) -> wgpu::RenderPipeline { let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { label: Some("PBR Shader"), @@ -17,7 +16,7 @@ pub fn create_pbr_pipeline( let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("PBR Pipeline Layout"), - bind_group_layouts: &[camera_light_layout, texture_layout, material_layout, shadow_layout, ibl_layout], + bind_group_layouts: &[camera_light_layout, texture_layout, material_layout, shadow_layout], immediate_size: 0, }); diff --git a/crates/voltex_renderer/src/pbr_shader.wgsl b/crates/voltex_renderer/src/pbr_shader.wgsl index ab29559..66830fc 100644 --- a/crates/voltex_renderer/src/pbr_shader.wgsl +++ b/crates/voltex_renderer/src/pbr_shader.wgsl @@ -48,9 +48,8 @@ struct ShadowUniform { @group(3) @binding(0) var t_shadow: texture_depth_2d; @group(3) @binding(1) var s_shadow: sampler_comparison; @group(3) @binding(2) var shadow: ShadowUniform; - -@group(4) @binding(0) var t_brdf_lut: texture_2d; -@group(4) @binding(1) var s_brdf_lut: sampler; +@group(3) @binding(3) var t_brdf_lut: texture_2d; +@group(3) @binding(4) var s_brdf_lut: sampler; struct VertexInput { @location(0) position: vec3, diff --git a/crates/voltex_renderer/src/shadow.rs b/crates/voltex_renderer/src/shadow.rs index 5e57507..05a9f75 100644 --- a/crates/voltex_renderer/src/shadow.rs +++ b/crates/voltex_renderer/src/shadow.rs @@ -78,6 +78,24 @@ impl ShadowMap { }, count: None, }, + // binding 3: BRDF LUT texture + wgpu::BindGroupLayoutEntry { + binding: 3, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Texture { + multisampled: false, + view_dimension: wgpu::TextureViewDimension::D2, + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + }, + count: None, + }, + // binding 4: BRDF LUT sampler + wgpu::BindGroupLayoutEntry { + binding: 4, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), + count: None, + }, ], }) } @@ -87,6 +105,8 @@ impl ShadowMap { device: &wgpu::Device, layout: &wgpu::BindGroupLayout, shadow_uniform_buffer: &wgpu::Buffer, + brdf_lut_view: &wgpu::TextureView, + brdf_lut_sampler: &wgpu::Sampler, ) -> wgpu::BindGroup { device.create_bind_group(&wgpu::BindGroupDescriptor { label: Some("Shadow Bind Group"), @@ -104,6 +124,14 @@ impl ShadowMap { binding: 2, resource: shadow_uniform_buffer.as_entire_binding(), }, + wgpu::BindGroupEntry { + binding: 3, + resource: wgpu::BindingResource::TextureView(brdf_lut_view), + }, + wgpu::BindGroupEntry { + binding: 4, + resource: wgpu::BindingResource::Sampler(brdf_lut_sampler), + }, ], }) } diff --git a/examples/ibl_demo/src/main.rs b/examples/ibl_demo/src/main.rs index ff475c7..ee5e180 100644 --- a/examples/ibl_demo/src/main.rs +++ b/examples/ibl_demo/src/main.rs @@ -41,7 +41,6 @@ struct AppState { shadow_bind_group: wgpu::BindGroup, _shadow_map: ShadowMap, _ibl: IblResources, - ibl_bind_group: wgpu::BindGroup, input: InputState, timer: GameTimer, cam_aligned_size: u32, @@ -179,8 +178,6 @@ impl ApplicationHandler for IblDemoApp { // IBL resources let ibl = IblResources::new(&gpu.device, &gpu.queue); - let ibl_layout = IblResources::bind_group_layout(&gpu.device); - let ibl_bind_group = ibl.create_bind_group(&gpu.device, &ibl_layout); // Material bind group let material_bind_group = gpu.device.create_bind_group(&wgpu::BindGroupDescriptor { @@ -216,6 +213,8 @@ impl ApplicationHandler for IblDemoApp { &gpu.device, &shadow_layout, &shadow_uniform_buffer, + &ibl.brdf_lut_view, + &ibl.brdf_lut_sampler, ); // PBR pipeline @@ -226,7 +225,6 @@ impl ApplicationHandler for IblDemoApp { &pbr_tex_layout, &mat_layout, &shadow_layout, - &ibl_layout, ); self.state = Some(AppState { @@ -247,7 +245,6 @@ impl ApplicationHandler for IblDemoApp { shadow_bind_group, _shadow_map: shadow_map, _ibl: ibl, - ibl_bind_group, input: InputState::new(), timer: GameTimer::new(60), cam_aligned_size, @@ -490,7 +487,6 @@ impl ApplicationHandler for IblDemoApp { render_pass.set_pipeline(&state.pipeline); render_pass.set_bind_group(1, &state.pbr_texture_bind_group, &[]); render_pass.set_bind_group(3, &state.shadow_bind_group, &[]); - render_pass.set_bind_group(4, &state.ibl_bind_group, &[]); render_pass.set_vertex_buffer(0, state.mesh.vertex_buffer.slice(..)); render_pass.set_index_buffer( state.mesh.index_buffer.slice(..), diff --git a/examples/multi_light_demo/src/main.rs b/examples/multi_light_demo/src/main.rs index 199d9c8..7d96b1e 100644 --- a/examples/multi_light_demo/src/main.rs +++ b/examples/multi_light_demo/src/main.rs @@ -40,7 +40,6 @@ struct AppState { shadow_bind_group: wgpu::BindGroup, _shadow_map: ShadowMap, _ibl: IblResources, - ibl_bind_group: wgpu::BindGroup, input: InputState, timer: GameTimer, cam_aligned_size: u32, @@ -190,8 +189,6 @@ impl ApplicationHandler for MultiLightApp { // IBL resources let ibl = IblResources::new(&gpu.device, &gpu.queue); - let ibl_layout = IblResources::bind_group_layout(&gpu.device); - let ibl_bind_group = ibl.create_bind_group(&gpu.device, &ibl_layout); // Material bind group let material_bind_group = gpu.device.create_bind_group(&wgpu::BindGroupDescriptor { @@ -227,6 +224,8 @@ impl ApplicationHandler for MultiLightApp { &gpu.device, &shadow_layout, &shadow_uniform_buffer, + &ibl.brdf_lut_view, + &ibl.brdf_lut_sampler, ); // PBR pipeline @@ -237,7 +236,6 @@ impl ApplicationHandler for MultiLightApp { &pbr_tex_layout, &mat_layout, &shadow_layout, - &ibl_layout, ); self.state = Some(AppState { @@ -259,7 +257,6 @@ impl ApplicationHandler for MultiLightApp { shadow_bind_group, _shadow_map: shadow_map, _ibl: ibl, - ibl_bind_group, input: InputState::new(), timer: GameTimer::new(60), cam_aligned_size, @@ -545,7 +542,6 @@ impl ApplicationHandler for MultiLightApp { render_pass.set_pipeline(&state.pipeline); render_pass.set_bind_group(1, &state.pbr_texture_bind_group, &[]); render_pass.set_bind_group(3, &state.shadow_bind_group, &[]); - render_pass.set_bind_group(4, &state.ibl_bind_group, &[]); // Draw 5 spheres (objects 0..4) render_pass.set_vertex_buffer(0, state.sphere_mesh.vertex_buffer.slice(..)); diff --git a/examples/pbr_demo/src/main.rs b/examples/pbr_demo/src/main.rs index f7ab06e..6ab0efc 100644 --- a/examples/pbr_demo/src/main.rs +++ b/examples/pbr_demo/src/main.rs @@ -41,7 +41,6 @@ struct AppState { shadow_bind_group: wgpu::BindGroup, _shadow_map: ShadowMap, _ibl: IblResources, - ibl_bind_group: wgpu::BindGroup, input: InputState, timer: GameTimer, cam_aligned_size: u32, @@ -175,8 +174,6 @@ impl ApplicationHandler for PbrDemoApp { // IBL resources let ibl = IblResources::new(&gpu.device, &gpu.queue); - let ibl_layout = IblResources::bind_group_layout(&gpu.device); - let ibl_bind_group = ibl.create_bind_group(&gpu.device, &ibl_layout); // Material bind group let material_bind_group = gpu.device.create_bind_group(&wgpu::BindGroupDescriptor { @@ -212,6 +209,8 @@ impl ApplicationHandler for PbrDemoApp { &gpu.device, &shadow_layout, &shadow_uniform_buffer, + &ibl.brdf_lut_view, + &ibl.brdf_lut_sampler, ); // PBR pipeline @@ -222,7 +221,6 @@ impl ApplicationHandler for PbrDemoApp { &pbr_tex_layout, &mat_layout, &shadow_layout, - &ibl_layout, ); self.state = Some(AppState { @@ -243,7 +241,6 @@ impl ApplicationHandler for PbrDemoApp { shadow_bind_group, _shadow_map: shadow_map, _ibl: ibl, - ibl_bind_group, input: InputState::new(), timer: GameTimer::new(60), cam_aligned_size, @@ -481,7 +478,6 @@ impl ApplicationHandler for PbrDemoApp { render_pass.set_pipeline(&state.pipeline); render_pass.set_bind_group(1, &state.pbr_texture_bind_group, &[]); render_pass.set_bind_group(3, &state.shadow_bind_group, &[]); - render_pass.set_bind_group(4, &state.ibl_bind_group, &[]); render_pass.set_vertex_buffer(0, state.mesh.vertex_buffer.slice(..)); render_pass.set_index_buffer( state.mesh.index_buffer.slice(..), diff --git a/examples/shadow_demo/src/main.rs b/examples/shadow_demo/src/main.rs index c8b8b5e..7cc4f26 100644 --- a/examples/shadow_demo/src/main.rs +++ b/examples/shadow_demo/src/main.rs @@ -48,7 +48,6 @@ struct AppState { shadow_pass_buffer: wgpu::Buffer, shadow_pass_bind_group: wgpu::BindGroup, _ibl: IblResources, - ibl_bind_group: wgpu::BindGroup, // Misc input: InputState, timer: GameTimer, @@ -227,8 +226,6 @@ impl ApplicationHandler for ShadowDemoApp { // IBL resources let ibl = IblResources::new(&gpu.device, &gpu.queue); - let ibl_layout = IblResources::bind_group_layout(&gpu.device); - let ibl_bind_group = ibl.create_bind_group(&gpu.device, &ibl_layout); // Material bind group let material_bind_group = gpu.device.create_bind_group(&wgpu::BindGroupDescriptor { @@ -263,6 +260,8 @@ impl ApplicationHandler for ShadowDemoApp { &gpu.device, &shadow_layout, &shadow_uniform_buffer, + &ibl.brdf_lut_view, + &ibl.brdf_lut_sampler, ); // Shadow pass dynamic UBO (one ShadowPassUniform per object) @@ -295,7 +294,6 @@ impl ApplicationHandler for ShadowDemoApp { &pbr_tex_layout, &mat_layout, &shadow_layout, - &ibl_layout, ); self.state = Some(AppState { @@ -321,7 +319,6 @@ impl ApplicationHandler for ShadowDemoApp { shadow_pass_buffer, shadow_pass_bind_group, _ibl: ibl, - ibl_bind_group, input: InputState::new(), timer: GameTimer::new(60), cam_aligned_size, @@ -584,7 +581,6 @@ impl ApplicationHandler for ShadowDemoApp { render_pass.set_pipeline(&state.pbr_pipeline); render_pass.set_bind_group(1, &state.pbr_texture_bind_group, &[]); render_pass.set_bind_group(3, &state.shadow_bind_group, &[]); - render_pass.set_bind_group(4, &state.ibl_bind_group, &[]); for i in 0..NUM_OBJECTS { let cam_offset = (i as u32) * state.cam_aligned_size;