mirror of
https://github.com/gfx-rs/wgpu.git
synced 2026-04-22 03:02:01 -04:00
Support dual source blending (#2427)
Co-authored-by: teoxoy <28601907+teoxoy@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
3bd2834b4f
commit
0491d39232
@@ -41,6 +41,8 @@ bitflags::bitflags! {
|
||||
const TEXTURE_LEVELS = 1 << 19;
|
||||
/// Image size query
|
||||
const IMAGE_SIZE = 1 << 20;
|
||||
/// Dual source blending
|
||||
const DUAL_SOURCE_BLENDING = 1 << 21;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,6 +106,7 @@ impl FeaturesManager {
|
||||
check_feature!(CULL_DISTANCE, 450, 300 /* with extension */);
|
||||
check_feature!(SAMPLE_VARIABLES, 400, 300);
|
||||
check_feature!(DYNAMIC_ARRAY_SIZE, 430, 310);
|
||||
check_feature!(DUAL_SOURCE_BLENDING, 330, 300 /* with extension */);
|
||||
match version {
|
||||
Version::Embedded { is_webgl: true, .. } => check_feature!(MULTI_VIEW, 140, 300),
|
||||
_ => check_feature!(MULTI_VIEW, 140, 310),
|
||||
@@ -233,6 +236,10 @@ impl FeaturesManager {
|
||||
// https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_texture_query_levels.txt
|
||||
writeln!(out, "#extension GL_ARB_texture_query_levels : require")?;
|
||||
}
|
||||
if self.0.contains(Features::DUAL_SOURCE_BLENDING) && version.is_es() {
|
||||
// https://registry.khronos.org/OpenGL/extensions/EXT/EXT_blend_func_extended.txt
|
||||
writeln!(out, "#extension GL_EXT_blend_func_extended : require")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -497,6 +504,7 @@ impl<'a, W> Writer<'a, W> {
|
||||
location: _,
|
||||
interpolation,
|
||||
sampling,
|
||||
second_blend_source,
|
||||
} => {
|
||||
if interpolation == Some(Interpolation::Linear) {
|
||||
self.features.request(Features::NOPERSPECTIVE_QUALIFIER);
|
||||
@@ -504,6 +512,9 @@ impl<'a, W> Writer<'a, W> {
|
||||
if sampling == Some(Sampling::Sample) {
|
||||
self.features.request(Features::SAMPLE_QUALIFIER);
|
||||
}
|
||||
if second_blend_source {
|
||||
self.features.request(Features::DUAL_SOURCE_BLENDING);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -333,6 +333,12 @@ struct VaryingName<'a> {
|
||||
impl fmt::Display for VaryingName<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self.binding {
|
||||
crate::Binding::Location {
|
||||
second_blend_source: true,
|
||||
..
|
||||
} => {
|
||||
write!(f, "_fs2p_location1",)
|
||||
}
|
||||
crate::Binding::Location { location, .. } => {
|
||||
let prefix = match (self.stage, self.output) {
|
||||
(ShaderStage::Compute, _) => unreachable!(),
|
||||
@@ -1235,12 +1241,13 @@ impl<'a, W: Write> Writer<'a, W> {
|
||||
Some(binding) => binding,
|
||||
};
|
||||
|
||||
let (location, interpolation, sampling) = match *binding {
|
||||
let (location, interpolation, sampling, second_blend_source) = match *binding {
|
||||
crate::Binding::Location {
|
||||
location,
|
||||
interpolation,
|
||||
sampling,
|
||||
} => (location, interpolation, sampling),
|
||||
second_blend_source,
|
||||
} => (location, interpolation, sampling, second_blend_source),
|
||||
crate::Binding::BuiltIn(built_in) => {
|
||||
if let crate::BuiltIn::Position { invariant: true } = built_in {
|
||||
match (self.options.version, self.entry_point.stage) {
|
||||
@@ -1281,7 +1288,11 @@ impl<'a, W: Write> Writer<'a, W> {
|
||||
|
||||
// Write the I/O locations, if allowed
|
||||
if self.options.version.supports_explicit_locations() || !emit_interpolation_and_auxiliary {
|
||||
write!(self.out, "layout(location = {location}) ")?;
|
||||
if second_blend_source {
|
||||
write!(self.out, "layout(location = {location}, index = 1) ")?;
|
||||
} else {
|
||||
write!(self.out, "layout(location = {location}) ")?;
|
||||
}
|
||||
}
|
||||
|
||||
// Write the interpolation qualifier.
|
||||
@@ -1318,6 +1329,7 @@ impl<'a, W: Write> Writer<'a, W> {
|
||||
location,
|
||||
interpolation: None,
|
||||
sampling: None,
|
||||
second_blend_source,
|
||||
},
|
||||
stage: self.entry_point.stage,
|
||||
output,
|
||||
|
||||
@@ -416,7 +416,17 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
|
||||
let builtin_str = builtin.to_hlsl_str()?;
|
||||
write!(self.out, " : {builtin_str}")?;
|
||||
}
|
||||
crate::Binding::Location { location, .. } => {
|
||||
crate::Binding::Location {
|
||||
second_blend_source: true,
|
||||
..
|
||||
} => {
|
||||
write!(self.out, " : SV_Target1")?;
|
||||
}
|
||||
crate::Binding::Location {
|
||||
location,
|
||||
second_blend_source: false,
|
||||
..
|
||||
} => {
|
||||
if stage == Some((crate::ShaderStage::Fragment, Io::Output)) {
|
||||
write!(self.out, " : SV_Target{location}")?;
|
||||
} else {
|
||||
|
||||
@@ -82,7 +82,10 @@ pub type EntryPointResourceMap = std::collections::BTreeMap<String, EntryPointRe
|
||||
enum ResolvedBinding {
|
||||
BuiltIn(crate::BuiltIn),
|
||||
Attribute(u32),
|
||||
Color(u32),
|
||||
Color {
|
||||
location: u32,
|
||||
second_blend_source: bool,
|
||||
},
|
||||
User {
|
||||
prefix: &'static str,
|
||||
index: u32,
|
||||
@@ -245,9 +248,20 @@ impl Options {
|
||||
location,
|
||||
interpolation,
|
||||
sampling,
|
||||
second_blend_source,
|
||||
} => match mode {
|
||||
LocationMode::VertexInput => Ok(ResolvedBinding::Attribute(location)),
|
||||
LocationMode::FragmentOutput => Ok(ResolvedBinding::Color(location)),
|
||||
LocationMode::FragmentOutput => {
|
||||
if second_blend_source && self.lang_version < (1, 2) {
|
||||
return Err(Error::UnsupportedAttribute(
|
||||
"second_blend_source".to_string(),
|
||||
));
|
||||
}
|
||||
Ok(ResolvedBinding::Color {
|
||||
location,
|
||||
second_blend_source,
|
||||
})
|
||||
}
|
||||
LocationMode::VertexOutput | LocationMode::FragmentInput => {
|
||||
Ok(ResolvedBinding::User {
|
||||
prefix: if self.spirv_cross_compatibility {
|
||||
@@ -404,7 +418,16 @@ impl ResolvedBinding {
|
||||
write!(out, "{name}")?;
|
||||
}
|
||||
Self::Attribute(index) => write!(out, "attribute({index})")?,
|
||||
Self::Color(index) => write!(out, "color({index})")?,
|
||||
Self::Color {
|
||||
location,
|
||||
second_blend_source,
|
||||
} => {
|
||||
if second_blend_source {
|
||||
write!(out, "color({location}) index(1)")?
|
||||
} else {
|
||||
write!(out, "color({location})")?
|
||||
}
|
||||
}
|
||||
Self::User {
|
||||
prefix,
|
||||
index,
|
||||
|
||||
@@ -1434,6 +1434,7 @@ impl Writer {
|
||||
location,
|
||||
interpolation,
|
||||
sampling,
|
||||
second_blend_source,
|
||||
} => {
|
||||
self.decorate(id, Decoration::Location, &[location]);
|
||||
|
||||
@@ -1473,6 +1474,9 @@ impl Writer {
|
||||
}
|
||||
}
|
||||
}
|
||||
if second_blend_source {
|
||||
self.decorate(id, Decoration::Index, &[1]);
|
||||
}
|
||||
}
|
||||
crate::Binding::BuiltIn(built_in) => {
|
||||
use crate::BuiltIn as Bi;
|
||||
|
||||
@@ -17,6 +17,7 @@ enum Attribute {
|
||||
Invariant,
|
||||
Interpolate(Option<crate::Interpolation>, Option<crate::Sampling>),
|
||||
Location(u32),
|
||||
SecondBlendSource,
|
||||
Stage(ShaderStage),
|
||||
WorkGroupSize([u32; 3]),
|
||||
}
|
||||
@@ -319,6 +320,7 @@ impl<W: Write> Writer<W> {
|
||||
for attribute in attributes {
|
||||
match *attribute {
|
||||
Attribute::Location(id) => write!(self.out, "@location({id}) ")?,
|
||||
Attribute::SecondBlendSource => write!(self.out, "@second_blend_source ")?,
|
||||
Attribute::BuiltIn(builtin_attrib) => {
|
||||
let builtin = builtin_str(builtin_attrib)?;
|
||||
write!(self.out, "@builtin({builtin}) ")?;
|
||||
@@ -1917,9 +1919,20 @@ fn map_binding_to_attribute(binding: &crate::Binding) -> Vec<Attribute> {
|
||||
location,
|
||||
interpolation,
|
||||
sampling,
|
||||
second_blend_source: false,
|
||||
} => vec![
|
||||
Attribute::Location(location),
|
||||
Attribute::Interpolate(interpolation, sampling),
|
||||
],
|
||||
crate::Binding::Location {
|
||||
location,
|
||||
interpolation,
|
||||
sampling,
|
||||
second_blend_source: true,
|
||||
} => vec![
|
||||
Attribute::Location(location),
|
||||
Attribute::SecondBlendSource,
|
||||
Attribute::Interpolate(interpolation, sampling),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1294,6 +1294,7 @@ impl Frontend {
|
||||
location,
|
||||
interpolation,
|
||||
sampling: None,
|
||||
second_blend_source: false,
|
||||
};
|
||||
location += 1;
|
||||
|
||||
@@ -1336,6 +1337,7 @@ impl Frontend {
|
||||
location,
|
||||
interpolation,
|
||||
sampling: None,
|
||||
second_blend_source: false,
|
||||
};
|
||||
location += 1;
|
||||
binding
|
||||
|
||||
@@ -475,6 +475,7 @@ impl Frontend {
|
||||
location,
|
||||
interpolation,
|
||||
sampling,
|
||||
second_blend_source: false,
|
||||
},
|
||||
handle,
|
||||
storage,
|
||||
|
||||
@@ -43,6 +43,7 @@ impl crate::Binding {
|
||||
location: _,
|
||||
interpolation: ref mut interpolation @ None,
|
||||
ref mut sampling,
|
||||
second_blend_source: _,
|
||||
} = *self
|
||||
{
|
||||
match ty.scalar_kind() {
|
||||
|
||||
@@ -255,6 +255,7 @@ impl Decoration {
|
||||
location,
|
||||
interpolation,
|
||||
sampling,
|
||||
second_blend_source: false,
|
||||
}),
|
||||
_ => Err(Error::MissingDecoration(spirv::Decoration::Location)),
|
||||
}
|
||||
|
||||
@@ -143,6 +143,7 @@ impl<T> ParsedAttribute<T> {
|
||||
#[derive(Default)]
|
||||
struct BindingParser {
|
||||
location: ParsedAttribute<u32>,
|
||||
second_blend_source: ParsedAttribute<bool>,
|
||||
built_in: ParsedAttribute<crate::BuiltIn>,
|
||||
interpolation: ParsedAttribute<crate::Interpolation>,
|
||||
sampling: ParsedAttribute<crate::Sampling>,
|
||||
@@ -182,6 +183,9 @@ impl BindingParser {
|
||||
}
|
||||
lexer.expect(Token::Paren(')'))?;
|
||||
}
|
||||
"second_blend_source" => {
|
||||
self.second_blend_source.set(true, name_span)?;
|
||||
}
|
||||
"invariant" => {
|
||||
self.invariant.set(true, name_span)?;
|
||||
}
|
||||
@@ -208,6 +212,7 @@ impl BindingParser {
|
||||
location,
|
||||
interpolation,
|
||||
sampling,
|
||||
second_blend_source: self.second_blend_source.value.unwrap_or(false),
|
||||
}))
|
||||
}
|
||||
(None, Some(crate::BuiltIn::Position { .. }), None, None, invariant) => {
|
||||
|
||||
@@ -922,6 +922,8 @@ pub enum Binding {
|
||||
/// [`Fragment`]: crate::ShaderStage::Fragment
|
||||
Location {
|
||||
location: u32,
|
||||
/// Indicates the 2nd input to the blender when dual-source blending.
|
||||
second_blend_source: bool,
|
||||
interpolation: Option<Interpolation>,
|
||||
sampling: Option<Sampling>,
|
||||
},
|
||||
|
||||
@@ -256,6 +256,9 @@ pub struct FunctionInfo {
|
||||
///
|
||||
/// [`GlobalVariable`]: crate::GlobalVariable
|
||||
sampling: crate::FastHashSet<Sampling>,
|
||||
|
||||
/// Indicates that the function is using dual source blending.
|
||||
pub dual_source_blending: bool,
|
||||
}
|
||||
|
||||
impl FunctionInfo {
|
||||
@@ -999,6 +1002,7 @@ impl ModuleInfo {
|
||||
global_uses: vec![GlobalUse::empty(); module.global_variables.len()].into_boxed_slice(),
|
||||
expressions: vec![ExpressionInfo::new(); fun.expressions.len()].into_boxed_slice(),
|
||||
sampling: crate::FastHashSet::default(),
|
||||
dual_source_blending: false,
|
||||
};
|
||||
let resolve_context =
|
||||
ResolveContext::with_locals(module, &fun.local_variables, &fun.arguments);
|
||||
@@ -1108,6 +1112,7 @@ fn uniform_control_flow() {
|
||||
global_uses: vec![GlobalUse::empty(); global_var_arena.len()].into_boxed_slice(),
|
||||
expressions: vec![ExpressionInfo::new(); expressions.len()].into_boxed_slice(),
|
||||
sampling: crate::FastHashSet::default(),
|
||||
dual_source_blending: false,
|
||||
};
|
||||
let resolve_context = ResolveContext {
|
||||
constants: &Arena::new(),
|
||||
|
||||
@@ -61,6 +61,17 @@ pub enum VaryingError {
|
||||
DuplicateBuiltIn(crate::BuiltIn),
|
||||
#[error("Capability {0:?} is not supported")]
|
||||
UnsupportedCapability(Capabilities),
|
||||
#[error("The attribute {0:?} is only valid as an output for stage {1:?}")]
|
||||
InvalidInputAttributeInStage(&'static str, crate::ShaderStage),
|
||||
#[error("The attribute {0:?} is not valid for stage {1:?}")]
|
||||
InvalidAttributeInStage(&'static str, crate::ShaderStage),
|
||||
#[error(
|
||||
"The location index {location} cannot be used together with the attribute {attribute:?}"
|
||||
)]
|
||||
InvalidLocationAttributeCombination {
|
||||
location: u32,
|
||||
attribute: &'static str,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, thiserror::Error)]
|
||||
@@ -89,6 +100,10 @@ pub enum EntryPointError {
|
||||
InvalidIntegerInterpolation { location: u32 },
|
||||
#[error(transparent)]
|
||||
Function(#[from] FunctionError),
|
||||
#[error(
|
||||
"Invalid locations {location_mask:?} are set while dual source blending. Only location 0 may be set."
|
||||
)]
|
||||
InvalidLocationsWhileDualSourceBlending { location_mask: BitSet },
|
||||
}
|
||||
|
||||
#[cfg(feature = "validate")]
|
||||
@@ -106,6 +121,7 @@ fn storage_usage(access: crate::StorageAccess) -> GlobalUse {
|
||||
struct VaryingContext<'a> {
|
||||
stage: crate::ShaderStage,
|
||||
output: bool,
|
||||
second_blend_source: bool,
|
||||
types: &'a UniqueArena<crate::Type>,
|
||||
type_info: &'a Vec<super::r#type::TypeInfo>,
|
||||
location_mask: &'a mut BitSet,
|
||||
@@ -293,6 +309,7 @@ impl VaryingContext<'_> {
|
||||
location,
|
||||
interpolation,
|
||||
sampling,
|
||||
second_blend_source,
|
||||
} => {
|
||||
// Only IO-shareable types may be stored in locations.
|
||||
if !self.type_info[ty.index()]
|
||||
@@ -301,7 +318,37 @@ impl VaryingContext<'_> {
|
||||
{
|
||||
return Err(VaryingError::NotIOShareableType(ty));
|
||||
}
|
||||
if !self.location_mask.insert(location as usize) {
|
||||
|
||||
if second_blend_source {
|
||||
if !self
|
||||
.capabilities
|
||||
.contains(Capabilities::DUAL_SOURCE_BLENDING)
|
||||
{
|
||||
return Err(VaryingError::UnsupportedCapability(
|
||||
Capabilities::DUAL_SOURCE_BLENDING,
|
||||
));
|
||||
}
|
||||
if self.stage != crate::ShaderStage::Fragment {
|
||||
return Err(VaryingError::InvalidAttributeInStage(
|
||||
"second_blend_source",
|
||||
self.stage,
|
||||
));
|
||||
}
|
||||
if !self.output {
|
||||
return Err(VaryingError::InvalidInputAttributeInStage(
|
||||
"second_blend_source",
|
||||
self.stage,
|
||||
));
|
||||
}
|
||||
if location != 0 {
|
||||
return Err(VaryingError::InvalidLocationAttributeCombination {
|
||||
location,
|
||||
attribute: "second_blend_source",
|
||||
});
|
||||
}
|
||||
|
||||
self.second_blend_source = true;
|
||||
} else if !self.location_mask.insert(location as usize) {
|
||||
#[cfg(feature = "validate")]
|
||||
if self.flags.contains(super::ValidationFlags::BINDINGS) {
|
||||
return Err(VaryingError::BindingCollision { location });
|
||||
@@ -567,7 +614,7 @@ impl super::Validator {
|
||||
return Err(EntryPointError::UnexpectedWorkgroupSize.with_span());
|
||||
}
|
||||
|
||||
let info = self
|
||||
let mut info = self
|
||||
.validate_function(&ep.function, module, mod_info, true)
|
||||
.map_err(WithSpan::into_other)?;
|
||||
|
||||
@@ -593,6 +640,7 @@ impl super::Validator {
|
||||
let mut ctx = VaryingContext {
|
||||
stage: ep.stage,
|
||||
output: false,
|
||||
second_blend_source: false,
|
||||
types: &module.types,
|
||||
type_info: &self.types,
|
||||
location_mask: &mut self.location_mask,
|
||||
@@ -612,6 +660,7 @@ impl super::Validator {
|
||||
let mut ctx = VaryingContext {
|
||||
stage: ep.stage,
|
||||
output: true,
|
||||
second_blend_source: false,
|
||||
types: &module.types,
|
||||
type_info: &self.types,
|
||||
location_mask: &mut self.location_mask,
|
||||
@@ -623,6 +672,18 @@ impl super::Validator {
|
||||
};
|
||||
ctx.validate(fr.ty, fr.binding.as_ref())
|
||||
.map_err_inner(|e| EntryPointError::Result(e).with_span())?;
|
||||
#[cfg(feature = "validate")]
|
||||
if ctx.second_blend_source {
|
||||
// Only the first location may be used whhen dual source blending
|
||||
if ctx.location_mask.len() == 1 && ctx.location_mask.contains(0) {
|
||||
info.dual_source_blending = true;
|
||||
} else {
|
||||
return Err(EntryPointError::InvalidLocationsWhileDualSourceBlending {
|
||||
location_mask: self.location_mask.clone(),
|
||||
}
|
||||
.with_span());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "validate")]
|
||||
if ep.stage == crate::ShaderStage::Vertex
|
||||
|
||||
@@ -112,6 +112,8 @@ bitflags::bitflags! {
|
||||
const MULTISAMPLED_SHADING = 0x800;
|
||||
/// Support for ray queries and acceleration structures.
|
||||
const RAY_QUERY = 0x1000;
|
||||
/// Support for generating two sources for blending from fragement shaders
|
||||
const DUAL_SOURCE_BLENDING = 0x2000;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
13
tests/in/dualsource.param.ron
Normal file
13
tests/in/dualsource.param.ron
Normal file
@@ -0,0 +1,13 @@
|
||||
(
|
||||
god_mode: true,
|
||||
vertex:[
|
||||
],
|
||||
fragment:[
|
||||
(
|
||||
entry_point:"main",
|
||||
target_profile:"ps_5_1",
|
||||
),
|
||||
],
|
||||
compute:[
|
||||
],
|
||||
)
|
||||
11
tests/in/dualsource.wgsl
Normal file
11
tests/in/dualsource.wgsl
Normal file
@@ -0,0 +1,11 @@
|
||||
/* Simple test for multiple output sources from fragment shaders */
|
||||
struct FragmentOutput{
|
||||
@location(0) color: vec4<f32>,
|
||||
@location(0) @second_blend_source mask: vec4<f32>,
|
||||
}
|
||||
@fragment
|
||||
fn main(@builtin(position) position: vec4<f32>) -> FragmentOutput {
|
||||
var color = vec4(0.4,0.3,0.2,0.1);
|
||||
var mask = vec4(0.9,0.8,0.7,0.6);
|
||||
return FragmentOutput(color, mask);
|
||||
}
|
||||
@@ -1251,6 +1251,7 @@
|
||||
),
|
||||
],
|
||||
sampling: [],
|
||||
dual_source_blending: false,
|
||||
),
|
||||
(
|
||||
flags: ("EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS"),
|
||||
@@ -2808,6 +2809,7 @@
|
||||
),
|
||||
],
|
||||
sampling: [],
|
||||
dual_source_blending: false,
|
||||
),
|
||||
(
|
||||
flags: ("EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS"),
|
||||
@@ -2847,6 +2849,7 @@
|
||||
),
|
||||
],
|
||||
sampling: [],
|
||||
dual_source_blending: false,
|
||||
),
|
||||
(
|
||||
flags: ("EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS"),
|
||||
@@ -2919,6 +2922,7 @@
|
||||
),
|
||||
],
|
||||
sampling: [],
|
||||
dual_source_blending: false,
|
||||
),
|
||||
(
|
||||
flags: ("EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS"),
|
||||
@@ -2961,6 +2965,7 @@
|
||||
),
|
||||
],
|
||||
sampling: [],
|
||||
dual_source_blending: false,
|
||||
),
|
||||
(
|
||||
flags: ("EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS"),
|
||||
@@ -3050,6 +3055,7 @@
|
||||
),
|
||||
],
|
||||
sampling: [],
|
||||
dual_source_blending: false,
|
||||
),
|
||||
],
|
||||
entry_points: [
|
||||
@@ -3727,6 +3733,7 @@
|
||||
),
|
||||
],
|
||||
sampling: [],
|
||||
dual_source_blending: false,
|
||||
),
|
||||
(
|
||||
flags: ("EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS"),
|
||||
@@ -4184,6 +4191,7 @@
|
||||
),
|
||||
],
|
||||
sampling: [],
|
||||
dual_source_blending: false,
|
||||
),
|
||||
(
|
||||
flags: ("EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS"),
|
||||
@@ -4288,6 +4296,7 @@
|
||||
),
|
||||
],
|
||||
sampling: [],
|
||||
dual_source_blending: false,
|
||||
),
|
||||
],
|
||||
const_expression_types: [
|
||||
|
||||
@@ -273,6 +273,7 @@
|
||||
),
|
||||
],
|
||||
sampling: [],
|
||||
dual_source_blending: false,
|
||||
),
|
||||
],
|
||||
entry_points: [
|
||||
@@ -426,6 +427,7 @@
|
||||
),
|
||||
],
|
||||
sampling: [],
|
||||
dual_source_blending: false,
|
||||
),
|
||||
],
|
||||
const_expression_types: [],
|
||||
|
||||
@@ -766,6 +766,7 @@
|
||||
),
|
||||
],
|
||||
sampling: [],
|
||||
dual_source_blending: false,
|
||||
),
|
||||
(
|
||||
flags: ("EXPRESSIONS | BLOCKS | CONTROL_FLOW_UNIFORMITY | STRUCT_LAYOUTS | CONSTANTS | BINDINGS"),
|
||||
@@ -2091,6 +2092,7 @@
|
||||
),
|
||||
],
|
||||
sampling: [],
|
||||
dual_source_blending: false,
|
||||
),
|
||||
],
|
||||
entry_points: [
|
||||
@@ -2183,6 +2185,7 @@
|
||||
),
|
||||
],
|
||||
sampling: [],
|
||||
dual_source_blending: false,
|
||||
),
|
||||
],
|
||||
const_expression_types: [
|
||||
|
||||
27
tests/out/glsl/dualsource.main.Fragment.glsl
Normal file
27
tests/out/glsl/dualsource.main.Fragment.glsl
Normal file
@@ -0,0 +1,27 @@
|
||||
#version 310 es
|
||||
#extension GL_EXT_blend_func_extended : require
|
||||
|
||||
precision highp float;
|
||||
precision highp int;
|
||||
|
||||
struct FragmentOutput {
|
||||
vec4 color;
|
||||
vec4 mask;
|
||||
};
|
||||
layout(location = 0) out vec4 _fs2p_location0;
|
||||
layout(location = 0, index = 1) out vec4 _fs2p_location1;
|
||||
|
||||
void main() {
|
||||
vec4 position = gl_FragCoord;
|
||||
vec4 color = vec4(0.0);
|
||||
vec4 mask = vec4(0.0);
|
||||
color = vec4(0.4, 0.3, 0.2, 0.1);
|
||||
mask = vec4(0.9, 0.8, 0.7, 0.6);
|
||||
vec4 _e13 = color;
|
||||
vec4 _e14 = mask;
|
||||
FragmentOutput _tmp_return = FragmentOutput(_e13, _e14);
|
||||
_fs2p_location0 = _tmp_return.color;
|
||||
_fs2p_location1 = _tmp_return.mask;
|
||||
return;
|
||||
}
|
||||
|
||||
29
tests/out/hlsl/dualsource.hlsl
Normal file
29
tests/out/hlsl/dualsource.hlsl
Normal file
@@ -0,0 +1,29 @@
|
||||
struct FragmentOutput {
|
||||
float4 color : SV_Target0;
|
||||
float4 mask : SV_Target1;
|
||||
};
|
||||
|
||||
struct FragmentInput_main {
|
||||
float4 position_1 : SV_Position;
|
||||
};
|
||||
|
||||
FragmentOutput ConstructFragmentOutput(float4 arg0, float4 arg1) {
|
||||
FragmentOutput ret = (FragmentOutput)0;
|
||||
ret.color = arg0;
|
||||
ret.mask = arg1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
FragmentOutput main(FragmentInput_main fragmentinput_main)
|
||||
{
|
||||
float4 position = fragmentinput_main.position_1;
|
||||
float4 color = (float4)0;
|
||||
float4 mask = (float4)0;
|
||||
|
||||
color = float4(0.4, 0.3, 0.2, 0.1);
|
||||
mask = float4(0.9, 0.8, 0.7, 0.6);
|
||||
float4 _expr13 = color;
|
||||
float4 _expr14 = mask;
|
||||
const FragmentOutput fragmentoutput = ConstructFragmentOutput(_expr13, _expr14);
|
||||
return fragmentoutput;
|
||||
}
|
||||
12
tests/out/hlsl/dualsource.ron
Normal file
12
tests/out/hlsl/dualsource.ron
Normal file
@@ -0,0 +1,12 @@
|
||||
(
|
||||
vertex:[
|
||||
],
|
||||
fragment:[
|
||||
(
|
||||
entry_point:"main",
|
||||
target_profile:"ps_5_1",
|
||||
),
|
||||
],
|
||||
compute:[
|
||||
],
|
||||
)
|
||||
@@ -2052,6 +2052,7 @@
|
||||
ty: 26,
|
||||
binding: Some(Location(
|
||||
location: 0,
|
||||
second_blend_source: false,
|
||||
interpolation: Some(Perspective),
|
||||
sampling: Some(Center),
|
||||
)),
|
||||
|
||||
@@ -1264,6 +1264,7 @@
|
||||
ty: 2,
|
||||
binding: Some(Location(
|
||||
location: 0,
|
||||
second_blend_source: false,
|
||||
interpolation: Some(Perspective),
|
||||
sampling: Some(Center),
|
||||
)),
|
||||
@@ -1273,6 +1274,7 @@
|
||||
ty: 4,
|
||||
binding: Some(Location(
|
||||
location: 1,
|
||||
second_blend_source: false,
|
||||
interpolation: Some(Perspective),
|
||||
sampling: Some(Center),
|
||||
)),
|
||||
@@ -1282,6 +1284,7 @@
|
||||
ty: 4,
|
||||
binding: Some(Location(
|
||||
location: 0,
|
||||
second_blend_source: false,
|
||||
interpolation: None,
|
||||
sampling: None,
|
||||
)),
|
||||
|
||||
29
tests/out/msl/dualsource.msl
Normal file
29
tests/out/msl/dualsource.msl
Normal file
@@ -0,0 +1,29 @@
|
||||
// language: metal2.0
|
||||
#include <metal_stdlib>
|
||||
#include <simd/simd.h>
|
||||
|
||||
using metal::uint;
|
||||
|
||||
struct FragmentOutput {
|
||||
metal::float4 color;
|
||||
metal::float4 mask;
|
||||
};
|
||||
|
||||
struct main_Input {
|
||||
};
|
||||
struct main_Output {
|
||||
metal::float4 color [[color(0)]];
|
||||
metal::float4 mask [[color(0) index(1)]];
|
||||
};
|
||||
fragment main_Output main_(
|
||||
metal::float4 position [[position]]
|
||||
) {
|
||||
metal::float4 color = {};
|
||||
metal::float4 mask = {};
|
||||
color = metal::float4(0.4, 0.3, 0.2, 0.1);
|
||||
mask = metal::float4(0.9, 0.8, 0.7, 0.6);
|
||||
metal::float4 _e13 = color;
|
||||
metal::float4 _e14 = mask;
|
||||
const auto _tmp = FragmentOutput {_e13, _e14};
|
||||
return main_Output { _tmp.color, _tmp.mask };
|
||||
}
|
||||
55
tests/out/spv/dualsource.spvasm
Normal file
55
tests/out/spv/dualsource.spvasm
Normal file
@@ -0,0 +1,55 @@
|
||||
; SPIR-V
|
||||
; Version: 1.1
|
||||
; Generator: rspirv
|
||||
; Bound: 35
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %17 "main" %11 %14 %16
|
||||
OpExecutionMode %17 OriginUpperLeft
|
||||
OpMemberDecorate %5 0 Offset 0
|
||||
OpMemberDecorate %5 1 Offset 16
|
||||
OpDecorate %11 BuiltIn FragCoord
|
||||
OpDecorate %14 Location 0
|
||||
OpDecorate %16 Location 0
|
||||
OpDecorate %16 Index 1
|
||||
%2 = OpTypeVoid
|
||||
%4 = OpTypeFloat 32
|
||||
%3 = OpTypeVector %4 4
|
||||
%5 = OpTypeStruct %3 %3
|
||||
%7 = OpTypePointer Function %3
|
||||
%8 = OpConstantNull %3
|
||||
%12 = OpTypePointer Input %3
|
||||
%11 = OpVariable %12 Input
|
||||
%15 = OpTypePointer Output %3
|
||||
%14 = OpVariable %15 Output
|
||||
%16 = OpVariable %15 Output
|
||||
%18 = OpTypeFunction %2
|
||||
%19 = OpConstant %4 0.4
|
||||
%20 = OpConstant %4 0.3
|
||||
%21 = OpConstant %4 0.2
|
||||
%22 = OpConstant %4 0.1
|
||||
%23 = OpConstant %4 0.9
|
||||
%24 = OpConstant %4 0.8
|
||||
%25 = OpConstant %4 0.7
|
||||
%26 = OpConstant %4 0.6
|
||||
%17 = OpFunction %2 None %18
|
||||
%10 = OpLabel
|
||||
%6 = OpVariable %7 Function %8
|
||||
%9 = OpVariable %7 Function %8
|
||||
%13 = OpLoad %3 %11
|
||||
OpBranch %27
|
||||
%27 = OpLabel
|
||||
%28 = OpCompositeConstruct %3 %19 %20 %21 %22
|
||||
OpStore %6 %28
|
||||
%29 = OpCompositeConstruct %3 %23 %24 %25 %26
|
||||
OpStore %9 %29
|
||||
%30 = OpLoad %3 %6
|
||||
%31 = OpLoad %3 %9
|
||||
%32 = OpCompositeConstruct %5 %30 %31
|
||||
%33 = OpCompositeExtract %3 %32 0
|
||||
OpStore %14 %33
|
||||
%34 = OpCompositeExtract %3 %32 1
|
||||
OpStore %16 %34
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
16
tests/out/wgsl/dualsource.wgsl
Normal file
16
tests/out/wgsl/dualsource.wgsl
Normal file
@@ -0,0 +1,16 @@
|
||||
struct FragmentOutput {
|
||||
@location(0) color: vec4<f32>,
|
||||
@location(0) @second_blend_source mask: vec4<f32>,
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn main(@builtin(position) position: vec4<f32>) -> FragmentOutput {
|
||||
var color: vec4<f32>;
|
||||
var mask: vec4<f32>;
|
||||
|
||||
color = vec4<f32>(0.4, 0.3, 0.2, 0.1);
|
||||
mask = vec4<f32>(0.9, 0.8, 0.7, 0.6);
|
||||
let _e13 = color;
|
||||
let _e14 = mask;
|
||||
return FragmentOutput(_e13, _e14);
|
||||
}
|
||||
@@ -512,6 +512,10 @@ fn convert_wgsl() {
|
||||
"fragment-output",
|
||||
Targets::SPIRV | Targets::METAL | Targets::GLSL | Targets::HLSL | Targets::WGSL,
|
||||
),
|
||||
(
|
||||
"dualsource",
|
||||
Targets::SPIRV | Targets::METAL | Targets::GLSL | Targets::HLSL | Targets::WGSL,
|
||||
),
|
||||
("functions-webgl", Targets::GLSL),
|
||||
(
|
||||
"interpolate",
|
||||
|
||||
Reference in New Issue
Block a user