Skip to content

Commit

Permalink
Add support for separate image samplers (#74)
Browse files Browse the repository at this point in the history
* Add support for separate image samplers
  • Loading branch information
attackgoat authored May 29, 2024
1 parent 71c1c05 commit 4a457fa
Show file tree
Hide file tree
Showing 10 changed files with 302 additions and 114 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- Support for separate image samplers (`SamplerState` in HLSL, `sampler` in GLSL)

### Changed

- Updated `egui` to v0.26
Expand Down
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,10 @@ ash-molten = "0.17"
anyhow = "1.0"
bmfont = { version = "0.3", default-features = false }
bytemuck = "1.14"
clap = { version = "4.5", features = ["derive"] }
glam = { version = "0.25", features = ["bytemuck"] }
half = { version = "2.3", features = ["bytemuck"] }
hassle-rs = "0.11"
image = "0.24"
inline-spirv = "0.2"
log = "0.4"
Expand Down
3 changes: 3 additions & 0 deletions contrib/rel-mgmt/run-all-examples
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ cargo run --example cpu_readback
cargo run --example subgroup_ops
cargo run --example bindless
cargo run --example image_sampler
cargo run --example image_sampler -- --hlsl
cargo run --example image_sampler -- --separate
cargo run --example image_sampler -- --hlsl --separate
cargo run --example vertex_layout
cargo run --example font_bmp
cargo run --example egui
Expand Down
147 changes: 129 additions & 18 deletions examples/image_sampler.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
mod profile_with_puffin;

use {
clap::Parser,
hassle_rs::compile_hlsl,
inline_spirv::inline_spirv,
screen_13::prelude::*,
std::{
Expand All @@ -16,6 +18,12 @@ use {
/// instead use use name suffixes such as _llr or _nne for linear/linear repeat or nearest/nearest
/// clamp-to-edge.
///
/// You may run this example program with either --hlsl or --separate arguments as follows:
///
/// cargo run --example image_sampler -- --hlsl --separate
///
/// Run with --help for more information.
///
/// See min_max.rs for more advanced image sampler usage.
fn main() -> anyhow::Result<()> {
pretty_env_logger::init();
Expand Down Expand Up @@ -74,6 +82,114 @@ fn create_pipeline(
device: &Arc<Device>,
sampler_info: impl Into<SamplerInfo>,
) -> anyhow::Result<Arc<GraphicPipeline>> {
let args = Args::parse();

let mut frag_shader = match (args.hlsl, args.separate) {
(true, true) => {
// HLSL separate image sampler
Shader::new_fragment(
inline_spirv!(
r#"
struct FullscreenVertexOutput
{
float4 position : SV_Position;
[[vk::location(0)]] float2 uv : TEXCOORD0;
};
[[vk::binding(0, 0)]] Texture2D screenTexture : register(t0);
[[vk::binding(1, 0)]] SamplerState textureSampler : register(s0);
float4 main(FullscreenVertexOutput input)
: SV_Target
{
return screenTexture.Sample(textureSampler, input.uv);
}
"#,
frag,
hlsl
)
.as_slice(),
)
}
(true, false) => {
// HLSL combined image sampler: inline_spirv uses shaderc which does not support this, so
// we are using hassle_rs which uses dxc. You must follow the instructions listed here to
// use hassle_rs:
// See: https://github.com/Traverse-Research/hassle-rs
// See: https://github.com/microsoft/DirectXShaderCompiler/wiki/Vulkan-combined-image-sampler-type
// See: https://github.com/google/shaderc/issues/1310
Shader::new_fragment(
compile_hlsl(
"fragment.hlsl",
r#"
struct FullscreenVertexOutput
{
float4 position : SV_Position;
[[vk::location(0)]] float2 uv : TEXCOORD0;
};
[[vk::combinedImageSampler]][[vk::binding(0, 0)]] Texture2D<float4> screenTexture : register(t0);
[[vk::combinedImageSampler]][[vk::binding(0, 0)]] SamplerState textureSampler : register(s0);
float4 main(FullscreenVertexOutput input)
: SV_Target
{
return screenTexture.Sample(textureSampler, input.uv);
}
"#,
"main", "ps_5_0", &["-spirv"], &[],
)?
.as_slice(),
)
}
(false, true) => {
// GLSL separate image sampler
Shader::new_fragment(
inline_spirv!(
r#"
#version 460 core
layout(binding = 0) uniform texture2D image;
layout(binding = 1) uniform sampler image_sampler;
layout(location = 0) in vec2 vk_TexCoord;
layout(location = 0) out vec4 vk_Color;
void main() {
vk_Color = texture(sampler2D(image, image_sampler), vk_TexCoord);
}
"#,
frag
)
.as_slice(),
)
}
(false, false) => {
// GLSL combined image sampler
Shader::new_fragment(
inline_spirv!(
r#"
#version 460 core
layout(binding = 0) uniform sampler2D image;
layout(location = 0) in vec2 vk_TexCoord;
layout(location = 0) out vec4 vk_Color;
void main() {
vk_Color = texture(image, vk_TexCoord);
}
"#,
frag
)
.as_slice(),
)
}
};

// Use the builder pattern to specify an image sampler at the combined binding index (0) or
// separate binding index (1).
let sampler_binding = args.separate as u32;
frag_shader = frag_shader.image_sampler(sampler_binding, sampler_info);

Ok(Arc::new(GraphicPipeline::create(
device,
GraphicPipelineInfo::default(),
Expand All @@ -100,24 +216,7 @@ fn create_pipeline(
)
.as_slice(),
),
Shader::new_fragment(
inline_spirv!(
r#"
#version 460 core
layout(binding = 0) uniform sampler2D image;
layout(location = 0) in vec2 vk_TexCoord;
layout(location = 0) out vec4 vk_Color;
void main() {
vk_Color = texture(image, vk_TexCoord);
}
"#,
frag
)
.as_slice(),
)
.image_sampler(0, sampler_info),
frag_shader,
],
)?))
}
Expand Down Expand Up @@ -153,3 +252,15 @@ fn read_image(device: &Arc<Device>, path: impl AsRef<Path>) -> anyhow::Result<Ar

Ok(image)
}

#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct Args {
/// Use HLSL fragment shaders instead of the default (GLSL).
#[arg(long)]
hlsl: bool,

/// Use separate image sampler objects instead of the default (combined image sampler objects).
#[arg(long)]
separate: bool,
}
10 changes: 10 additions & 0 deletions src/driver/descriptor_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ impl DescriptorPool {
pool_size_count += 1;
}

if info.sampler_count > 0 {
pool_sizes[pool_size_count] = vk::DescriptorPoolSize {
ty: vk::DescriptorType::SAMPLER,
descriptor_count: info.sampler_count,
};
pool_size_count += 1;
}

if info.storage_buffer_count > 0 {
pool_sizes[pool_size_count] = vk::DescriptorPoolSize {
ty: vk::DescriptorType::STORAGE_BUFFER,
Expand Down Expand Up @@ -214,6 +222,7 @@ pub struct DescriptorPoolInfo {
pub input_attachment_count: u32,
pub max_sets: u32,
pub sampled_image_count: u32,
pub sampler_count: u32,
pub storage_buffer_count: u32,
pub storage_buffer_dynamic_count: u32,
pub storage_image_count: u32,
Expand All @@ -229,6 +238,7 @@ impl DescriptorPoolInfo {
+ self.combined_image_sampler_count
+ self.input_attachment_count
+ self.sampled_image_count
+ self.sampler_count
+ self.storage_buffer_count
+ self.storage_buffer_dynamic_count
+ self.storage_image_count
Expand Down
16 changes: 14 additions & 2 deletions src/driver/graphic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ use {
device::Device,
image::SampleCount,
merge_push_constant_ranges,
shader::{DescriptorBindingMap, PipelineDescriptorInfo, Shader, SpecializationInfo},
DriverError,
shader::{
DescriptorBindingMap, DescriptorInfo, PipelineDescriptorInfo, Shader,
SpecializationInfo,
},
DescriptorBinding, DriverError,
},
ash::vk,
derive_builder::{Builder, UninitializedFieldError},
Expand Down Expand Up @@ -363,6 +366,7 @@ pub struct GraphicPipeline {
pub name: Option<String>,

pub(crate) push_constants: Vec<vk::PushConstantRange>,
pub(crate) separate_samplers: Box<[DescriptorBinding]>,
pub(crate) shader_modules: Vec<vk::ShaderModule>,
pub(super) state: GraphicPipelineState,
}
Expand Down Expand Up @@ -459,6 +463,13 @@ impl GraphicPipeline {
}
}

let separate_samplers = descriptor_bindings
.iter()
.filter_map(|(&descriptor_binding, (descriptor_info, _))| {
matches!(descriptor_info, DescriptorInfo::Sampler(..)).then_some(descriptor_binding)
})
.collect();

let descriptor_info = PipelineDescriptorInfo::create(&device, &descriptor_bindings)?;
let descriptor_sets_layouts = descriptor_info
.layouts
Expand Down Expand Up @@ -560,6 +571,7 @@ impl GraphicPipeline {
layout,
name: None,
push_constants,
separate_samplers,
shader_modules,
state: GraphicPipelineState {
layout,
Expand Down
Loading

0 comments on commit 4a457fa

Please sign in to comment.