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

Support dual source blending #4022

Merged
merged 6 commits into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from 5 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ By @Valaphee in [#3402](https://github.com/gfx-rs/wgpu/pull/3402)
- Add validation in accordance with WebGPU `setViewport` valid usage for `x`, `y` and `this.[[attachment_size]]`. By @James2022-rgb in [#4058](https://github.com/gfx-rs/wgpu/pull/4058)
- `wgpu::CreateSurfaceError` and `wgpu::RequestDeviceError` now give details of the failure, but no longer implement `PartialEq` and cannot be constructed. By @kpreid in [#4066](https://github.com/gfx-rs/wgpu/pull/4066) and [#4145](https://github.com/gfx-rs/wgpu/pull/4145)
- Make `WGPU_POWER_PREF=none` a valid value. By @fornwall in [4076](https://github.com/gfx-rs/wgpu/pull/4076)
- Support dual source blending in OpenGL ES, Metal, Vulkan & DX12. By @freqmod in [4022](https://github.com/gfx-rs/wgpu/pull/4022)

#### Vulkan

Expand Down
50 changes: 49 additions & 1 deletion wgpu-core/src/device/resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1276,6 +1276,10 @@ impl<A: HalApi> Device<A> {
.flags
.contains(wgt::DownlevelFlags::MULTISAMPLED_SHADING),
);
caps.set(
Caps::DUAL_SOURCE_BLENDING,
self.features.contains(wgt::Features::DUAL_SOURCE_BLENDING),
);

let info = naga::valid::Validator::new(naga::valid::ValidationFlags::all(), caps)
.validate(&module)
Expand Down Expand Up @@ -2560,6 +2564,8 @@ impl<A: HalApi> Device<A> {
let mut vertex_steps = Vec::with_capacity(desc.vertex.buffers.len());
let mut vertex_buffers = Vec::with_capacity(desc.vertex.buffers.len());
let mut total_attributes = 0;
let mut shader_expects_dual_source_blending = false;
let mut pipeline_expects_dual_source_blending = false;
for (i, vb_state) in desc.vertex.buffers.iter().enumerate() {
vertex_steps.push(pipeline::VertexStep {
stride: vb_state.array_stride,
Expand Down Expand Up @@ -2700,7 +2706,25 @@ impl<A: HalApi> Device<A> {
{
break Some(pipeline::ColorStateError::FormatNotMultisampled(cs.format));
}

if let Some(blend_mode) = cs.blend {
for factor in [
blend_mode.color.src_factor,
blend_mode.color.dst_factor,
blend_mode.alpha.src_factor,
blend_mode.alpha.dst_factor,
] {
if factor.ref_second_blend_source() {
if i == 0 {
self.require_features(wgt::Features::DUAL_SOURCE_BLENDING)?;
pipeline_expects_dual_source_blending = true;
break;
} else {
return Err(crate::pipeline::CreateRenderPipelineError
::BlendFactorOnUnsupportedTarget { factor, target: i as u32 });
}
}
}
}
break None;
};
if let Some(e) = error {
Expand Down Expand Up @@ -2807,6 +2831,14 @@ impl<A: HalApi> Device<A> {
error,
})?;
validated_stages |= flag;

let dual_source = interface.has_dual_source_blending_entry_point();
if !pipeline_expects_dual_source_blending && dual_source {
return Err(pipeline::CreateRenderPipelineError::ShaderExpectsPipelineToUseDualSourceBlending);
}
if pipeline_expects_dual_source_blending && !dual_source {
return Err(pipeline::CreateRenderPipelineError::PipelineExpectsShaderToUseDualSourceBlending);
}
}

hal::ProgrammableStage {
Expand Down Expand Up @@ -2857,6 +2889,11 @@ impl<A: HalApi> Device<A> {
}
}

if let Some(ref interface) = shader_module.interface {
shader_expects_dual_source_blending =
interface.is_fragment_entry_dual_source(fragment).expect("Internal error: Fragment entrypoint should not be set in function if not present in shader interface");
teoxoy marked this conversation as resolved.
Show resolved Hide resolved
}

Some(hal::ProgrammableStage {
module: &shader_module.raw,
entry_point: fragment.stage.entry_point.as_ref(),
Expand All @@ -2865,6 +2902,17 @@ impl<A: HalApi> Device<A> {
None => None,
};

if !pipeline_expects_dual_source_blending && shader_expects_dual_source_blending {
return Err(
pipeline::CreateRenderPipelineError::ShaderExpectsPipelineToUseDualSourceBlending,
);
}
if pipeline_expects_dual_source_blending && !shader_expects_dual_source_blending {
return Err(
pipeline::CreateRenderPipelineError::PipelineExpectsShaderToUseDualSourceBlending,
);
}

if validated_stages.contains(wgt::ShaderStages::FRAGMENT) {
for (i, output) in io.iter() {
match color_targets.get(*i as usize) {
Expand Down
9 changes: 9 additions & 0 deletions wgpu-core/src/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,15 @@ pub enum CreateRenderPipelineError {
},
#[error("In the provided shader, the type given for group {group} binding {binding} has a size of {size}. As the device does not support `DownlevelFlags::BUFFER_BINDINGS_NOT_16_BYTE_ALIGNED`, the type must have a size that is a multiple of 16 bytes.")]
UnalignedShader { group: u32, binding: u32, size: u64 },
#[error("Using the blend factor {factor:?} for render target {target} is not possible. Only the first render target may be used when dual-source blending.")]
BlendFactorOnUnsupportedTarget {
factor: wgt::BlendFactor,
target: u32,
},
#[error("Pipeline expects the shader entry point to make use of dual-source blending.")]
PipelineExpectsShaderToUseDualSourceBlending,
#[error("Shader entry point expects the pipeline to make use of dual-source blending.")]
ShaderExpectsPipelineToUseDualSourceBlending,
}

bitflags::bitflags! {
Expand Down
24 changes: 23 additions & 1 deletion wgpu-core/src/validation.rs
freqmod marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ struct EntryPoint {
spec_constants: Vec<SpecializationConstant>,
sampling_pairs: FastHashSet<(naga::Handle<Resource>, naga::Handle<Resource>)>,
workgroup_size: [u32; 3],
dual_source_blending: bool,
}

#[derive(Debug)]
Expand Down Expand Up @@ -903,7 +904,7 @@ impl Interface {
ep.sampling_pairs
.insert((resource_mapping[&key.image], resource_mapping[&key.sampler]));
}

ep.dual_source_blending = info.dual_source_blending;
ep.workgroup_size = entry_point.workgroup_size;

entry_points.insert((entry_point.stage, entry_point.name.clone()), ep);
Expand Down Expand Up @@ -1177,4 +1178,25 @@ impl Interface {
.collect();
Ok(outputs)
}

pub fn has_dual_source_blending_entry_point(&self) -> bool {
self.entry_points
.values()
.any(|point| point.dual_source_blending)
}
pub fn is_fragment_entry_dual_source<'a>(
&self,
fragment: &crate::pipeline::FragmentState<'a>,
) -> Result<bool, StageError> {
if let Some(entry_point) = self.entry_points.get(&(
naga::ShaderStage::Fragment,
String::from(fragment.stage.entry_point.as_ref()),
)) {
Ok(entry_point.dual_source_blending)
} else {
Err(StageError::MissingEntryPoint(String::from(
fragment.stage.entry_point.as_ref(),
)))
}
}
}
4 changes: 3 additions & 1 deletion wgpu-hal/src/dx12/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,9 @@ impl super::Adapter {
| wgt::Features::TEXTURE_FORMAT_16BIT_NORM
| wgt::Features::PUSH_CONSTANTS
| wgt::Features::SHADER_PRIMITIVE_INDEX
| wgt::Features::RG11B10UFLOAT_RENDERABLE;
| wgt::Features::RG11B10UFLOAT_RENDERABLE
| wgt::Features::DUAL_SOURCE_BLENDING;

//TODO: in order to expose this, we need to run a compute shader
// that extract the necessary statistics out of the D3D12 result.
// Alternatively, we could allocate a buffer for the query set,
Expand Down
12 changes: 6 additions & 6 deletions wgpu-hal/src/dx12/conv.rs
freqmod marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -246,12 +246,12 @@ fn map_blend_factor(factor: wgt::BlendFactor, is_alpha: bool) -> d3d12_ty::D3D12
Bf::Constant => d3d12_ty::D3D12_BLEND_BLEND_FACTOR,
Bf::OneMinusConstant => d3d12_ty::D3D12_BLEND_INV_BLEND_FACTOR,
Bf::SrcAlphaSaturated => d3d12_ty::D3D12_BLEND_SRC_ALPHA_SAT,
//Bf::Src1Color if is_alpha => d3d12_ty::D3D12_BLEND_SRC1_ALPHA,
//Bf::Src1Color => d3d12_ty::D3D12_BLEND_SRC1_COLOR,
//Bf::OneMinusSrc1Color if is_alpha => d3d12_ty::D3D12_BLEND_INV_SRC1_ALPHA,
//Bf::OneMinusSrc1Color => d3d12_ty::D3D12_BLEND_INV_SRC1_COLOR,
//Bf::Src1Alpha => d3d12_ty::D3D12_BLEND_SRC1_ALPHA,
//Bf::OneMinusSrc1Alpha => d3d12_ty::D3D12_BLEND_INV_SRC1_ALPHA,
Bf::Src1 if is_alpha => d3d12_ty::D3D12_BLEND_SRC1_ALPHA,
Bf::Src1 => d3d12_ty::D3D12_BLEND_SRC1_COLOR,
Bf::OneMinusSrc1 if is_alpha => d3d12_ty::D3D12_BLEND_INV_SRC1_ALPHA,
Bf::OneMinusSrc1 => d3d12_ty::D3D12_BLEND_INV_SRC1_COLOR,
Bf::Src1Alpha => d3d12_ty::D3D12_BLEND_SRC1_ALPHA,
Bf::OneMinusSrc1Alpha => d3d12_ty::D3D12_BLEND_INV_SRC1_ALPHA,
}
}

Expand Down
4 changes: 4 additions & 0 deletions wgpu-hal/src/gles/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,10 @@ impl super::Adapter {
wgt::Features::MULTIVIEW,
extensions.contains("OVR_multiview2"),
);
features.set(
wgt::Features::DUAL_SOURCE_BLENDING,
extensions.contains("GL_EXT_blend_func_extended"),
);
features.set(
wgt::Features::SHADER_PRIMITIVE_INDEX,
ver >= (3, 2) || extensions.contains("OES_geometry_shader"),
Expand Down
4 changes: 4 additions & 0 deletions wgpu-hal/src/gles/conv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,10 @@ fn map_blend_factor(factor: wgt::BlendFactor) -> u32 {
Bf::Constant => glow::CONSTANT_COLOR,
Bf::OneMinusConstant => glow::ONE_MINUS_CONSTANT_COLOR,
Bf::SrcAlphaSaturated => glow::SRC_ALPHA_SATURATE,
Bf::Src1 => glow::SRC1_COLOR,
Bf::OneMinusSrc1 => glow::ONE_MINUS_SRC1_COLOR,
Bf::Src1Alpha => glow::SRC1_ALPHA,
Bf::OneMinusSrc1Alpha => glow::ONE_MINUS_SRC1_ALPHA,
}
}

Expand Down
5 changes: 5 additions & 0 deletions wgpu-hal/src/metal/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -796,6 +796,7 @@ impl super::PrivateCapabilities {
None
},
timestamp_query_support,
dual_source_blending: version.at_least((11, 0), (14, 0), os_is_mac),
}
}

Expand Down Expand Up @@ -833,6 +834,10 @@ impl super::PrivateCapabilities {
self.timestamp_query_support
.contains(TimestampQuerySupport::INSIDE_WGPU_PASSES),
);
features.set(
F::DUAL_SOURCE_BLENDING,
self.msl_version >= MTLLanguageVersion::V1_2,
);
features.set(F::TEXTURE_COMPRESSION_ASTC, self.format_astc);
features.set(F::TEXTURE_COMPRESSION_ASTC_HDR, self.format_astc_hdr);
features.set(F::TEXTURE_COMPRESSION_BC, self.format_bc);
Expand Down
8 changes: 4 additions & 4 deletions wgpu-hal/src/metal/conv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,10 @@ pub fn map_blend_factor(factor: wgt::BlendFactor) -> metal::MTLBlendFactor {
//Bf::ConstantAlpha => BlendAlpha,
//Bf::OneMinusConstantAlpha => OneMinusBlendAlpha,
Bf::SrcAlphaSaturated => SourceAlphaSaturated,
//Bf::Src1 => Source1Color,
//Bf::OneMinusSrc1 => OneMinusSource1Color,
//Bf::Src1Alpha => Source1Alpha,
//Bf::OneMinusSrc1Alpha => OneMinusSource1Alpha,
Bf::Src1 => Source1Color,
Bf::OneMinusSrc1 => OneMinusSource1Color,
Bf::Src1Alpha => Source1Alpha,
Bf::OneMinusSrc1Alpha => OneMinusSource1Alpha,
}
}

Expand Down
2 changes: 2 additions & 0 deletions wgpu-hal/src/vulkan/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ impl PhysicalDeviceFeatures {
//.shader_resource_residency(requested_features.contains(wgt::Features::SHADER_RESOURCE_RESIDENCY))
.geometry_shader(requested_features.contains(wgt::Features::SHADER_PRIMITIVE_INDEX))
.depth_clamp(requested_features.contains(wgt::Features::DEPTH_CLIP_CONTROL))
.dual_src_blend(requested_features.contains(wgt::Features::DUAL_SOURCE_BLENDING))
.build(),
descriptor_indexing: if requested_features.intersects(indexing_features()) {
Some(
Expand Down Expand Up @@ -460,6 +461,7 @@ impl PhysicalDeviceFeatures {
}

features.set(F::DEPTH_CLIP_CONTROL, self.core.depth_clamp != 0);
features.set(F::DUAL_SOURCE_BLENDING, self.core.dual_src_blend != 0);

if let Some(ref multiview) = self.multiview {
features.set(F::MULTIVIEW, multiview.multiview != 0);
Expand Down
4 changes: 4 additions & 0 deletions wgpu-hal/src/vulkan/conv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,10 @@ fn map_blend_factor(factor: wgt::BlendFactor) -> vk::BlendFactor {
Bf::SrcAlphaSaturated => vk::BlendFactor::SRC_ALPHA_SATURATE,
Bf::Constant => vk::BlendFactor::CONSTANT_COLOR,
Bf::OneMinusConstant => vk::BlendFactor::ONE_MINUS_CONSTANT_COLOR,
Bf::Src1 => vk::BlendFactor::SRC1_COLOR,
Bf::OneMinusSrc1 => vk::BlendFactor::ONE_MINUS_SRC1_COLOR,
Bf::Src1Alpha => vk::BlendFactor::SRC1_ALPHA,
Bf::OneMinusSrc1Alpha => vk::BlendFactor::ONE_MINUS_SRC1_ALPHA,
}
}

Expand Down
39 changes: 38 additions & 1 deletion wgpu-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -781,7 +781,19 @@ bitflags::bitflags! {
/// This is a native only feature.
const SHADER_EARLY_DEPTH_TEST = 1 << 62;

// 62..64 available
/// Allows two outputs from a shader to be used for blending.
/// Note that dual-source blending doesn't support multiple render targets.
///
/// For more info see the OpenGL ES extension GL_EXT_blend_func_extended.
///
/// Supported platforms:
/// - OpenGL ES (with GL_EXT_blend_func_extended)
/// - Metal (with MSL 1.2+)
/// - Vulkan (with dualSrcBlend)
/// - DX12
const DUAL_SOURCE_BLENDING = 1 << 63;

// no more space left
}
}

Expand Down Expand Up @@ -1549,6 +1561,8 @@ impl TextureViewDimension {
///
/// Corresponds to [WebGPU `GPUBlendFactor`](
/// https://gpuweb.github.io/gpuweb/#enumdef-gpublendfactor).
/// Values using S1 requires [`Features::DUAL_SOURCE_BLENDING`] and can only be
/// used with the first render target.
#[repr(C)]
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
#[cfg_attr(feature = "trace", derive(Serialize))]
Expand Down Expand Up @@ -1581,6 +1595,29 @@ pub enum BlendFactor {
Constant = 11,
/// 1.0 - Constant
OneMinusConstant = 12,
/// S1.component
Src1 = 13,
/// 1.0 - S1.component
OneMinusSrc1 = 14,
/// S1.alpha
Src1Alpha = 15,
/// 1.0 - S1.alpha
OneMinusSrc1Alpha = 16,
freqmod marked this conversation as resolved.
Show resolved Hide resolved
}

impl BlendFactor {
/// Returns `true` if the blend factor references the second blend source.
///
/// Note that the usage of those blend factors require [`Features::DUAL_SOURCE_BLENDING`].
pub fn ref_second_blend_source(&self) -> bool {
match self {
BlendFactor::Src1
| BlendFactor::OneMinusSrc1
| BlendFactor::Src1Alpha
| BlendFactor::OneMinusSrc1Alpha => true,
_ => false,
}
}
}

/// Alpha blend operation.
Expand Down
9 changes: 9 additions & 0 deletions wgpu/src/backend/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,15 @@ fn map_blend_factor(factor: wgt::BlendFactor) -> web_sys::GpuBlendFactor {
BlendFactor::SrcAlphaSaturated => bf::SrcAlphaSaturated,
BlendFactor::Constant => bf::Constant,
BlendFactor::OneMinusConstant => bf::OneMinusConstant,
BlendFactor::Src1
| BlendFactor::OneMinusSrc1
| BlendFactor::Src1Alpha
| BlendFactor::OneMinusSrc1Alpha => {
panic!(
"{:?} is not enabled for this backend",
wgt::Features::DUAL_SOURCE_BLENDING
)
}
}
}

Expand Down
Loading