Skip to content

Commit

Permalink
Support alternate color blending source
Browse files Browse the repository at this point in the history
  • Loading branch information
freqmod committed Sep 18, 2023
1 parent 8adab25 commit d5732d7
Show file tree
Hide file tree
Showing 14 changed files with 129 additions and 13 deletions.
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
32 changes: 32 additions & 0 deletions 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,7 @@ 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 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 @@ -2701,6 +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
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
9 changes: 8 additions & 1 deletion wgpu-core/src/validation.rs
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,10 @@ 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)
}
}
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::BLEND_FUNC_EXTENDED;

//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
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,
blend_func_extended: 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,
}

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

0 comments on commit d5732d7

Please sign in to comment.