Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for separate image samplers #74

Merged
merged 5 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading