diff --git a/src/dawn_native/Format.cpp b/src/dawn_native/Format.cpp index 3848cccd11..12198b2bb6 100644 --- a/src/dawn_native/Format.cpp +++ b/src/dawn_native/Format.cpp @@ -158,7 +158,7 @@ namespace dawn_native { auto AddColorFormat = [&AddFormat](wgpu::TextureFormat format, bool renderable, bool supportsStorageUsage, uint32_t byteSize, - SampleTypeBit sampleTypes) { + SampleTypeBit sampleTypes, uint8_t componentCount) { Format internalFormat; internalFormat.format = format; internalFormat.isRenderable = renderable; @@ -166,6 +166,7 @@ namespace dawn_native { internalFormat.isSupported = true; internalFormat.supportsStorageUsage = supportsStorageUsage; internalFormat.aspects = Aspect::Color; + internalFormat.componentCount = componentCount; AspectInfo* firstAspect = internalFormat.aspectInfo.data(); firstAspect->block.byteSize = byteSize; firstAspect->block.width = 1; @@ -202,6 +203,7 @@ namespace dawn_native { internalFormat.isSupported = true; internalFormat.supportsStorageUsage = false; internalFormat.aspects = Aspect::Depth; + internalFormat.componentCount = 1; AspectInfo* firstAspect = internalFormat.aspectInfo.data(); firstAspect->block.byteSize = byteSize; firstAspect->block.width = 1; @@ -220,6 +222,7 @@ namespace dawn_native { internalFormat.isSupported = false; internalFormat.supportsStorageUsage = false; internalFormat.aspects = Aspect::Stencil; + internalFormat.componentCount = 1; AspectInfo* firstAspect = internalFormat.aspectInfo.data(); firstAspect->block.byteSize = 1; firstAspect->block.width = 1; @@ -231,7 +234,8 @@ namespace dawn_native { }; auto AddCompressedFormat = [&AddFormat](wgpu::TextureFormat format, uint32_t byteSize, - uint32_t width, uint32_t height, bool isSupported) { + uint32_t width, uint32_t height, bool isSupported, + uint8_t componentCount) { Format internalFormat; internalFormat.format = format; internalFormat.isRenderable = false; @@ -239,6 +243,7 @@ namespace dawn_native { internalFormat.isSupported = isSupported; internalFormat.supportsStorageUsage = false; internalFormat.aspects = Aspect::Color; + internalFormat.componentCount = componentCount; AspectInfo* firstAspect = internalFormat.aspectInfo.data(); firstAspect->block.byteSize = byteSize; firstAspect->block.width = width; @@ -249,74 +254,74 @@ namespace dawn_native { AddFormat(internalFormat); }; - auto AddMultiAspectFormat = [&AddFormat, &table](wgpu::TextureFormat format, Aspect aspects, - wgpu::TextureFormat firstFormat, - wgpu::TextureFormat secondFormat, - bool isRenderable, bool isSupported) { - Format internalFormat; - internalFormat.format = format; - internalFormat.isRenderable = isRenderable; - internalFormat.isCompressed = false; - internalFormat.isSupported = isSupported; - internalFormat.supportsStorageUsage = false; - internalFormat.aspects = aspects; + auto AddMultiAspectFormat = + [&AddFormat, &table](wgpu::TextureFormat format, Aspect aspects, + wgpu::TextureFormat firstFormat, wgpu::TextureFormat secondFormat, + bool isRenderable, bool isSupported, uint8_t componentCount) { + Format internalFormat; + internalFormat.format = format; + internalFormat.isRenderable = isRenderable; + internalFormat.isCompressed = false; + internalFormat.isSupported = isSupported; + internalFormat.supportsStorageUsage = false; + internalFormat.aspects = aspects; + internalFormat.componentCount = componentCount; + const size_t firstFormatIndex = ComputeFormatIndex(firstFormat); + const size_t secondFormatIndex = ComputeFormatIndex(secondFormat); - const size_t firstFormatIndex = ComputeFormatIndex(firstFormat); - const size_t secondFormatIndex = ComputeFormatIndex(secondFormat); + internalFormat.aspectInfo[0] = table[firstFormatIndex].aspectInfo[0]; + internalFormat.aspectInfo[1] = table[secondFormatIndex].aspectInfo[0]; - internalFormat.aspectInfo[0] = table[firstFormatIndex].aspectInfo[0]; - internalFormat.aspectInfo[1] = table[secondFormatIndex].aspectInfo[0]; - - AddFormat(internalFormat); - }; + AddFormat(internalFormat); + }; // clang-format off // 1 byte color formats - AddColorFormat(wgpu::TextureFormat::R8Unorm, true, false, 1, kAnyFloat); - AddColorFormat(wgpu::TextureFormat::R8Snorm, false, false, 1, kAnyFloat); - AddColorFormat(wgpu::TextureFormat::R8Uint, true, false, 1, SampleTypeBit::Uint); - AddColorFormat(wgpu::TextureFormat::R8Sint, true, false, 1, SampleTypeBit::Sint); + AddColorFormat(wgpu::TextureFormat::R8Unorm, true, false, 1, kAnyFloat, 1); + AddColorFormat(wgpu::TextureFormat::R8Snorm, false, false, 1, kAnyFloat, 1); + AddColorFormat(wgpu::TextureFormat::R8Uint, true, false, 1, SampleTypeBit::Uint, 1); + AddColorFormat(wgpu::TextureFormat::R8Sint, true, false, 1, SampleTypeBit::Sint, 1); // 2 bytes color formats - AddColorFormat(wgpu::TextureFormat::R16Uint, true, false, 2, SampleTypeBit::Uint); - AddColorFormat(wgpu::TextureFormat::R16Sint, true, false, 2, SampleTypeBit::Sint); - AddColorFormat(wgpu::TextureFormat::R16Float, true, false, 2, kAnyFloat); - AddColorFormat(wgpu::TextureFormat::RG8Unorm, true, false, 2, kAnyFloat); - AddColorFormat(wgpu::TextureFormat::RG8Snorm, false, false, 2, kAnyFloat); - AddColorFormat(wgpu::TextureFormat::RG8Uint, true, false, 2, SampleTypeBit::Uint); - AddColorFormat(wgpu::TextureFormat::RG8Sint, true, false, 2, SampleTypeBit::Sint); + AddColorFormat(wgpu::TextureFormat::R16Uint, true, false, 2, SampleTypeBit::Uint, 1); + AddColorFormat(wgpu::TextureFormat::R16Sint, true, false, 2, SampleTypeBit::Sint, 1); + AddColorFormat(wgpu::TextureFormat::R16Float, true, false, 2, kAnyFloat, 1); + AddColorFormat(wgpu::TextureFormat::RG8Unorm, true, false, 2, kAnyFloat, 2); + AddColorFormat(wgpu::TextureFormat::RG8Snorm, false, false, 2, kAnyFloat, 2); + AddColorFormat(wgpu::TextureFormat::RG8Uint, true, false, 2, SampleTypeBit::Uint, 2); + AddColorFormat(wgpu::TextureFormat::RG8Sint, true, false, 2, SampleTypeBit::Sint, 2); // 4 bytes color formats - AddColorFormat(wgpu::TextureFormat::R32Uint, true, true, 4, SampleTypeBit::Uint); - AddColorFormat(wgpu::TextureFormat::R32Sint, true, true, 4, SampleTypeBit::Sint); - AddColorFormat(wgpu::TextureFormat::R32Float, true, true, 4, SampleTypeBit::UnfilterableFloat); - AddColorFormat(wgpu::TextureFormat::RG16Uint, true, false, 4, SampleTypeBit::Uint); - AddColorFormat(wgpu::TextureFormat::RG16Sint, true, false, 4, SampleTypeBit::Sint); - AddColorFormat(wgpu::TextureFormat::RG16Float, true, false, 4, kAnyFloat); - AddColorFormat(wgpu::TextureFormat::RGBA8Unorm, true, true, 4, kAnyFloat); - AddColorFormat(wgpu::TextureFormat::RGBA8UnormSrgb, true, false, 4, kAnyFloat); - AddColorFormat(wgpu::TextureFormat::RGBA8Snorm, false, true, 4, kAnyFloat); - AddColorFormat(wgpu::TextureFormat::RGBA8Uint, true, true, 4, SampleTypeBit::Uint); - AddColorFormat(wgpu::TextureFormat::RGBA8Sint, true, true, 4, SampleTypeBit::Sint); - AddColorFormat(wgpu::TextureFormat::BGRA8Unorm, true, false, 4, kAnyFloat); - AddColorFormat(wgpu::TextureFormat::BGRA8UnormSrgb, true, false, 4, kAnyFloat); - AddColorFormat(wgpu::TextureFormat::RGB10A2Unorm, true, false, 4, kAnyFloat); + AddColorFormat(wgpu::TextureFormat::R32Uint, true, true, 4, SampleTypeBit::Uint, 1); + AddColorFormat(wgpu::TextureFormat::R32Sint, true, true, 4, SampleTypeBit::Sint, 1); + AddColorFormat(wgpu::TextureFormat::R32Float, true, true, 4, SampleTypeBit::UnfilterableFloat, 1); + AddColorFormat(wgpu::TextureFormat::RG16Uint, true, false, 4, SampleTypeBit::Uint, 2); + AddColorFormat(wgpu::TextureFormat::RG16Sint, true, false, 4, SampleTypeBit::Sint, 2); + AddColorFormat(wgpu::TextureFormat::RG16Float, true, false, 4, kAnyFloat, 2); + AddColorFormat(wgpu::TextureFormat::RGBA8Unorm, true, true, 4, kAnyFloat, 4); + AddColorFormat(wgpu::TextureFormat::RGBA8UnormSrgb, true, false, 4, kAnyFloat, 4); + AddColorFormat(wgpu::TextureFormat::RGBA8Snorm, false, true, 4, kAnyFloat, 4); + AddColorFormat(wgpu::TextureFormat::RGBA8Uint, true, true, 4, SampleTypeBit::Uint, 4); + AddColorFormat(wgpu::TextureFormat::RGBA8Sint, true, true, 4, SampleTypeBit::Sint, 4); + AddColorFormat(wgpu::TextureFormat::BGRA8Unorm, true, false, 4, kAnyFloat, 4); + AddColorFormat(wgpu::TextureFormat::BGRA8UnormSrgb, true, false, 4, kAnyFloat, 4); + AddColorFormat(wgpu::TextureFormat::RGB10A2Unorm, true, false, 4, kAnyFloat, 4); - AddColorFormat(wgpu::TextureFormat::RG11B10Ufloat, false, false, 4, kAnyFloat); - AddColorFormat(wgpu::TextureFormat::RGB9E5Ufloat, false, false, 4, kAnyFloat); + AddColorFormat(wgpu::TextureFormat::RG11B10Ufloat, false, false, 4, kAnyFloat, 3); + AddColorFormat(wgpu::TextureFormat::RGB9E5Ufloat, false, false, 4, kAnyFloat, 3); // 8 bytes color formats - AddColorFormat(wgpu::TextureFormat::RG32Uint, true, true, 8, SampleTypeBit::Uint); - AddColorFormat(wgpu::TextureFormat::RG32Sint, true, true, 8, SampleTypeBit::Sint); - AddColorFormat(wgpu::TextureFormat::RG32Float, true, true, 8, SampleTypeBit::UnfilterableFloat); - AddColorFormat(wgpu::TextureFormat::RGBA16Uint, true, true, 8, SampleTypeBit::Uint); - AddColorFormat(wgpu::TextureFormat::RGBA16Sint, true, true, 8, SampleTypeBit::Sint); - AddColorFormat(wgpu::TextureFormat::RGBA16Float, true, true, 8, kAnyFloat); + AddColorFormat(wgpu::TextureFormat::RG32Uint, true, true, 8, SampleTypeBit::Uint, 2); + AddColorFormat(wgpu::TextureFormat::RG32Sint, true, true, 8, SampleTypeBit::Sint, 2); + AddColorFormat(wgpu::TextureFormat::RG32Float, true, true, 8, SampleTypeBit::UnfilterableFloat, 2); + AddColorFormat(wgpu::TextureFormat::RGBA16Uint, true, true, 8, SampleTypeBit::Uint, 4); + AddColorFormat(wgpu::TextureFormat::RGBA16Sint, true, true, 8, SampleTypeBit::Sint, 4); + AddColorFormat(wgpu::TextureFormat::RGBA16Float, true, true, 8, kAnyFloat, 4); // 16 bytes color formats - AddColorFormat(wgpu::TextureFormat::RGBA32Uint, true, true, 16, SampleTypeBit::Uint); - AddColorFormat(wgpu::TextureFormat::RGBA32Sint, true, true, 16, SampleTypeBit::Sint); - AddColorFormat(wgpu::TextureFormat::RGBA32Float, true, true, 16, SampleTypeBit::UnfilterableFloat); + AddColorFormat(wgpu::TextureFormat::RGBA32Uint, true, true, 16, SampleTypeBit::Uint, 4); + AddColorFormat(wgpu::TextureFormat::RGBA32Sint, true, true, 16, SampleTypeBit::Sint, 4); + AddColorFormat(wgpu::TextureFormat::RGBA32Float, true, true, 16, SampleTypeBit::UnfilterableFloat, 4); // Depth-stencil formats AddDepthFormat(wgpu::TextureFormat::Depth32Float, 4); @@ -327,30 +332,30 @@ namespace dawn_native { // TODO(dawn:666): Implement the stencil8 format AddStencilFormat(wgpu::TextureFormat::Stencil8); AddMultiAspectFormat(wgpu::TextureFormat::Depth24PlusStencil8, - Aspect::Depth | Aspect::Stencil, wgpu::TextureFormat::Depth24Plus, wgpu::TextureFormat::Stencil8, true, true); + Aspect::Depth | Aspect::Stencil, wgpu::TextureFormat::Depth24Plus, wgpu::TextureFormat::Stencil8, true, true, 2); // TODO(dawn:690): Implement Depth16Unorm, Depth24UnormStencil8, Depth32FloatStencil8. // BC compressed formats bool isBCFormatSupported = device->IsExtensionEnabled(Extension::TextureCompressionBC); - AddCompressedFormat(wgpu::TextureFormat::BC1RGBAUnorm, 8, 4, 4, isBCFormatSupported); - AddCompressedFormat(wgpu::TextureFormat::BC1RGBAUnormSrgb, 8, 4, 4, isBCFormatSupported); - AddCompressedFormat(wgpu::TextureFormat::BC4RSnorm, 8, 4, 4, isBCFormatSupported); - AddCompressedFormat(wgpu::TextureFormat::BC4RUnorm, 8, 4, 4, isBCFormatSupported); - AddCompressedFormat(wgpu::TextureFormat::BC2RGBAUnorm, 16, 4, 4, isBCFormatSupported); - AddCompressedFormat(wgpu::TextureFormat::BC2RGBAUnormSrgb, 16, 4, 4, isBCFormatSupported); - AddCompressedFormat(wgpu::TextureFormat::BC3RGBAUnorm, 16, 4, 4, isBCFormatSupported); - AddCompressedFormat(wgpu::TextureFormat::BC3RGBAUnormSrgb, 16, 4, 4, isBCFormatSupported); - AddCompressedFormat(wgpu::TextureFormat::BC5RGSnorm, 16, 4, 4, isBCFormatSupported); - AddCompressedFormat(wgpu::TextureFormat::BC5RGUnorm, 16, 4, 4, isBCFormatSupported); - AddCompressedFormat(wgpu::TextureFormat::BC6HRGBFloat, 16, 4, 4, isBCFormatSupported); - AddCompressedFormat(wgpu::TextureFormat::BC6HRGBUfloat, 16, 4, 4, isBCFormatSupported); - AddCompressedFormat(wgpu::TextureFormat::BC7RGBAUnorm, 16, 4, 4, isBCFormatSupported); - AddCompressedFormat(wgpu::TextureFormat::BC7RGBAUnormSrgb, 16, 4, 4, isBCFormatSupported); + AddCompressedFormat(wgpu::TextureFormat::BC1RGBAUnorm, 8, 4, 4, isBCFormatSupported, 4); + AddCompressedFormat(wgpu::TextureFormat::BC1RGBAUnormSrgb, 8, 4, 4, isBCFormatSupported, 4); + AddCompressedFormat(wgpu::TextureFormat::BC4RSnorm, 8, 4, 4, isBCFormatSupported, 1); + AddCompressedFormat(wgpu::TextureFormat::BC4RUnorm, 8, 4, 4, isBCFormatSupported, 1); + AddCompressedFormat(wgpu::TextureFormat::BC2RGBAUnorm, 16, 4, 4, isBCFormatSupported, 4); + AddCompressedFormat(wgpu::TextureFormat::BC2RGBAUnormSrgb, 16, 4, 4, isBCFormatSupported, 4); + AddCompressedFormat(wgpu::TextureFormat::BC3RGBAUnorm, 16, 4, 4, isBCFormatSupported, 4); + AddCompressedFormat(wgpu::TextureFormat::BC3RGBAUnormSrgb, 16, 4, 4, isBCFormatSupported, 4); + AddCompressedFormat(wgpu::TextureFormat::BC5RGSnorm, 16, 4, 4, isBCFormatSupported, 2); + AddCompressedFormat(wgpu::TextureFormat::BC5RGUnorm, 16, 4, 4, isBCFormatSupported, 2); + AddCompressedFormat(wgpu::TextureFormat::BC6HRGBFloat, 16, 4, 4, isBCFormatSupported, 3); + AddCompressedFormat(wgpu::TextureFormat::BC6HRGBUfloat, 16, 4, 4, isBCFormatSupported, 3); + AddCompressedFormat(wgpu::TextureFormat::BC7RGBAUnorm, 16, 4, 4, isBCFormatSupported, 4); + AddCompressedFormat(wgpu::TextureFormat::BC7RGBAUnormSrgb, 16, 4, 4, isBCFormatSupported, 4); // multi-planar formats const bool isMultiPlanarFormatSupported = device->IsExtensionEnabled(Extension::MultiPlanarFormats); AddMultiAspectFormat(wgpu::TextureFormat::R8BG8Biplanar420Unorm, Aspect::Plane0 | Aspect::Plane1, - wgpu::TextureFormat::R8Unorm, wgpu::TextureFormat::RG8Unorm, false, isMultiPlanarFormatSupported); + wgpu::TextureFormat::R8Unorm, wgpu::TextureFormat::RG8Unorm, false, isMultiPlanarFormatSupported, 3); // clang-format on diff --git a/src/dawn_native/Format.h b/src/dawn_native/Format.h index 2708d04ba6..f03f37d35f 100644 --- a/src/dawn_native/Format.h +++ b/src/dawn_native/Format.h @@ -91,6 +91,8 @@ namespace dawn_native { bool isSupported; bool supportsStorageUsage; Aspect aspects; + // Only used for renderable color formats, number of color channels. + uint8_t componentCount; bool IsColor() const; bool HasDepth() const; diff --git a/src/dawn_native/RenderPipeline.cpp b/src/dawn_native/RenderPipeline.cpp index 9b18105955..8ad0b8f070 100644 --- a/src/dawn_native/RenderPipeline.cpp +++ b/src/dawn_native/RenderPipeline.cpp @@ -253,10 +253,22 @@ namespace dawn_native { return {}; } - MaybeError ValidateColorTargetState(DeviceBase* device, - const ColorTargetState* descriptor, - bool fragmentWritten, - wgpu::TextureComponentType fragmentOutputBaseType) { + bool BlendFactorContainsSrcAlpha(const wgpu::BlendFactor& blendFactor) { + return blendFactor == wgpu::BlendFactor::SrcAlpha || + blendFactor == wgpu::BlendFactor::OneMinusSrcAlpha || + blendFactor == wgpu::BlendFactor::SrcAlphaSaturated; + } + + bool BlendFactorContainsSrc(const wgpu::BlendFactor& blendFactor) { + return blendFactor == wgpu::BlendFactor::Src || + blendFactor == wgpu::BlendFactor::OneMinusSrc; + } + + MaybeError ValidateColorTargetState( + DeviceBase* device, + const ColorTargetState* descriptor, + bool fragmentWritten, + const EntryPointMetadata::FragmentOutputVariableInfo& fragmentOutputVariable) { if (descriptor->nextInChain != nullptr) { return DAWN_VALIDATION_ERROR("nextInChain must be nullptr"); } @@ -278,10 +290,37 @@ namespace dawn_native { "Color format must be blendable when blending is enabled"); } if (fragmentWritten) { - if (fragmentOutputBaseType != format->GetAspectInfo(Aspect::Color).baseType) { + if (fragmentOutputVariable.baseType != + format->GetAspectInfo(Aspect::Color).baseType) { return DAWN_VALIDATION_ERROR( "Color format must match the fragment stage output type"); } + + if (fragmentOutputVariable.componentCount < format->componentCount) { + return DAWN_VALIDATION_ERROR( + "The fragment stage output components count must be no fewer than the " + "color format channel count"); + } + + if (descriptor->blend) { + if (fragmentOutputVariable.componentCount < 4u) { + // No alpha channel output + // Make sure there's no alpha involved in the blending operation + if (BlendFactorContainsSrcAlpha(descriptor->blend->color.srcFactor) || + BlendFactorContainsSrcAlpha(descriptor->blend->color.dstFactor)) { + return DAWN_VALIDATION_ERROR( + "Color blending factor is reading alpha but it is missing from " + "fragment output"); + } + if (descriptor->blend->alpha.srcFactor != wgpu::BlendFactor::Zero || + BlendFactorContainsSrc(descriptor->blend->alpha.dstFactor) || + BlendFactorContainsSrcAlpha(descriptor->blend->alpha.dstFactor)) { + return DAWN_VALIDATION_ERROR( + "Alpha blending factor is reading alpha but it is missing from " + "fragment output"); + } + } + } } else { if (descriptor->writeMask != wgpu::ColorWriteMask::None) { return DAWN_VALIDATION_ERROR( @@ -311,10 +350,10 @@ namespace dawn_native { descriptor->module->GetEntryPoint(descriptor->entryPoint); for (ColorAttachmentIndex i(uint8_t(0)); i < ColorAttachmentIndex(static_cast(descriptor->targetCount)); ++i) { - DAWN_TRY( - ValidateColorTargetState(device, &descriptor->targets[static_cast(i)], - fragmentMetadata.fragmentOutputsWritten[i], - fragmentMetadata.fragmentOutputFormatBaseTypes[i])); + DAWN_TRY(ValidateColorTargetState(device, + &descriptor->targets[static_cast(i)], + fragmentMetadata.fragmentOutputsWritten[i], + fragmentMetadata.fragmentOutputVariables[i])); } return {}; diff --git a/src/dawn_native/ShaderModule.cpp b/src/dawn_native/ShaderModule.cpp index 96ae1ec1e8..cf8a3ccf04 100644 --- a/src/dawn_native/ShaderModule.cpp +++ b/src/dawn_native/ShaderModule.cpp @@ -875,8 +875,16 @@ namespace dawn_native { ColorAttachmentIndex attachment( static_cast(unsanitizedAttachment)); DAWN_TRY_ASSIGN( - metadata->fragmentOutputFormatBaseTypes[attachment], + metadata->fragmentOutputVariables[attachment].baseType, TintComponentTypeToTextureComponentType(output_var.component_type)); + uint32_t componentCount; + DAWN_TRY_ASSIGN(componentCount, + TintCompositionTypeToInterStageComponentCount( + output_var.composition_type)); + // componentCount should be no larger than 4u + ASSERT(componentCount <= 4u); + metadata->fragmentOutputVariables[attachment].componentCount = + componentCount; metadata->fragmentOutputsWritten.set(attachment); } } diff --git a/src/dawn_native/ShaderModule.h b/src/dawn_native/ShaderModule.h index 3f17ceb27d..83c0a8d732 100644 --- a/src/dawn_native/ShaderModule.h +++ b/src/dawn_native/ShaderModule.h @@ -173,8 +173,12 @@ namespace dawn_native { ityp::bitset usedVertexInputs; // An array to record the basic types (float, int and uint) of the fragment shader outputs. - ityp::array - fragmentOutputFormatBaseTypes; + struct FragmentOutputVariableInfo { + wgpu::TextureComponentType baseType; + uint8_t componentCount; + }; + ityp::array + fragmentOutputVariables; ityp::bitset fragmentOutputsWritten; struct InterStageVariableInfo { diff --git a/src/dawn_native/opengl/ShaderModuleGL.cpp b/src/dawn_native/opengl/ShaderModuleGL.cpp index a5a3dd0251..24a9adbcb3 100644 --- a/src/dawn_native/opengl/ShaderModuleGL.cpp +++ b/src/dawn_native/opengl/ShaderModuleGL.cpp @@ -288,8 +288,9 @@ namespace dawn_native { namespace opengl { spirv_cross::SPIRType::BaseType shaderFragmentOutputBaseType = compiler.get_type(fragmentOutput.base_type_id).basetype; - metadata->fragmentOutputFormatBaseTypes[attachment] = - SpirvBaseTypeToTextureComponentType(shaderFragmentOutputBaseType); + // spriv path so temporarily always set to 4u to always pass validation + metadata->fragmentOutputVariables[attachment] = { + SpirvBaseTypeToTextureComponentType(shaderFragmentOutputBaseType), 4u}; metadata->fragmentOutputsWritten.set(attachment); } } diff --git a/src/tests/unittests/validation/RenderPipelineValidationTests.cpp b/src/tests/unittests/validation/RenderPipelineValidationTests.cpp index d6e60ede4e..0809fe4c23 100644 --- a/src/tests/unittests/validation/RenderPipelineValidationTests.cpp +++ b/src/tests/unittests/validation/RenderPipelineValidationTests.cpp @@ -47,6 +47,14 @@ class RenderPipelineValidationTest : public ValidationTest { wgpu::ShaderModule fsModuleUint; }; +namespace { + bool BlendFactorContainsSrcAlpha(const wgpu::BlendFactor& blendFactor) { + return blendFactor == wgpu::BlendFactor::SrcAlpha || + blendFactor == wgpu::BlendFactor::OneMinusSrcAlpha || + blendFactor == wgpu::BlendFactor::SrcAlphaSaturated; + } +} // namespace + // Test cases where creation should succeed TEST_F(RenderPipelineValidationTest, CreationSuccess) { { @@ -216,28 +224,26 @@ TEST_F(RenderPipelineValidationTest, NonBlendableFormat) { // Tests that the format of the color state descriptor must match the output of the fragment shader. TEST_F(RenderPipelineValidationTest, FragmentOutputFormatCompatibility) { - constexpr uint32_t kNumTextureFormatBaseType = 3u; - std::array kScalarTypes = {{"f32", "i32", "u32"}}; - std::array kColorFormats = { - {wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureFormat::RGBA8Sint, - wgpu::TextureFormat::RGBA8Uint}}; + std::array kScalarTypes = {{"f32", "i32", "u32"}}; + std::array kColorFormats = {{wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureFormat::RGBA8Sint, + wgpu::TextureFormat::RGBA8Uint}}; - for (size_t i = 0; i < kNumTextureFormatBaseType; ++i) { - for (size_t j = 0; j < kNumTextureFormatBaseType; ++j) { - utils::ComboRenderPipelineDescriptor descriptor; - descriptor.vertex.module = vsModule; + for (size_t i = 0; i < kScalarTypes.size(); ++i) { + utils::ComboRenderPipelineDescriptor descriptor; + descriptor.vertex.module = vsModule; + std::ostringstream stream; + stream << R"( + [[stage(fragment)]] fn main() -> [[location(0)]] vec4<)" + << kScalarTypes[i] << R"(> { + var result : vec4<)" + << kScalarTypes[i] << R"(>; + return result; + })"; + descriptor.cFragment.module = utils::CreateShaderModule(device, stream.str().c_str()); + + for (size_t j = 0; j < kColorFormats.size(); ++j) { descriptor.cTargets[0].format = kColorFormats[j]; - - std::ostringstream stream; - stream << R"( - [[stage(fragment)]] fn main() -> [[location(0)]] vec4<)" - << kScalarTypes[i] << R"(> { - var result : vec4<)" - << kScalarTypes[i] << R"(>; - return result; - })"; - descriptor.cFragment.module = utils::CreateShaderModule(device, stream.str().c_str()); - if (i == j) { device.CreateRenderPipeline(&descriptor); } else { @@ -247,6 +253,110 @@ TEST_F(RenderPipelineValidationTest, FragmentOutputFormatCompatibility) { } } +// Tests that the component count of the color state target format must be fewer than that of the +// fragment shader output. +TEST_F(RenderPipelineValidationTest, FragmentOutputComponentCountCompatibility) { + std::array kColorFormats = {wgpu::TextureFormat::R8Unorm, + wgpu::TextureFormat::RG8Unorm, + wgpu::TextureFormat::RGBA8Unorm}; + + std::array kBlendFactors = {wgpu::BlendFactor::Zero, + wgpu::BlendFactor::One, + wgpu::BlendFactor::SrcAlpha, + wgpu::BlendFactor::OneMinusSrcAlpha, + wgpu::BlendFactor::Src, + wgpu::BlendFactor::DstAlpha, + wgpu::BlendFactor::OneMinusDstAlpha, + wgpu::BlendFactor::Dst}; + + for (size_t componentCount = 1; componentCount <= 4; ++componentCount) { + utils::ComboRenderPipelineDescriptor descriptor; + descriptor.vertex.module = vsModule; + + std::ostringstream stream; + stream << R"( + [[stage(fragment)]] fn main() -> [[location(0)]] )"; + switch (componentCount) { + case 1: + stream << R"(f32 { + return 1.0; + })"; + break; + case 2: + stream << R"(vec2 { + return vec2(1.0, 1.0); + })"; + break; + case 3: + stream << R"(vec3 { + return vec3(1.0, 1.0, 1.0); + })"; + break; + case 4: + stream << R"(vec4 { + return vec4(1.0, 1.0, 1.0, 1.0); + })"; + break; + default: + UNREACHABLE(); + } + descriptor.cFragment.module = utils::CreateShaderModule(device, stream.str().c_str()); + + for (auto colorFormat : kColorFormats) { + descriptor.cTargets[0].format = colorFormat; + + descriptor.cTargets[0].blend = nullptr; + if (componentCount >= utils::GetWGSLRenderableColorTextureComponentCount(colorFormat)) { + device.CreateRenderPipeline(&descriptor); + } else { + ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor)); + } + + descriptor.cTargets[0].blend = &descriptor.cBlends[0]; + + for (auto colorSrcFactor : kBlendFactors) { + descriptor.cBlends[0].color.srcFactor = colorSrcFactor; + for (auto colorDstFactor : kBlendFactors) { + descriptor.cBlends[0].color.dstFactor = colorDstFactor; + for (auto alphaSrcFactor : kBlendFactors) { + descriptor.cBlends[0].alpha.srcFactor = alphaSrcFactor; + for (auto alphaDstFactor : kBlendFactors) { + descriptor.cBlends[0].alpha.dstFactor = alphaDstFactor; + + bool valid = true; + if (componentCount >= + utils::GetWGSLRenderableColorTextureComponentCount(colorFormat)) { + if (BlendFactorContainsSrcAlpha( + descriptor.cTargets[0].blend->color.srcFactor) || + BlendFactorContainsSrcAlpha( + descriptor.cTargets[0].blend->color.dstFactor) || + descriptor.cTargets[0].blend->alpha.srcFactor != + wgpu::BlendFactor::Zero || + descriptor.cTargets[0].blend->alpha.dstFactor == + wgpu::BlendFactor::Src || + descriptor.cTargets[0].blend->alpha.dstFactor == + wgpu::BlendFactor::OneMinusSrc || + BlendFactorContainsSrcAlpha( + descriptor.cTargets[0].blend->alpha.dstFactor)) { + valid = componentCount == 4; + } + } else { + valid = false; + } + + if (valid) { + device.CreateRenderPipeline(&descriptor); + } else { + ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor)); + } + } + } + } + } + } + } +} + /// Tests that the sample count of the render pipeline must be valid. TEST_F(RenderPipelineValidationTest, SampleCount) { { diff --git a/src/tests/white_box/D3D12DescriptorHeapTests.cpp b/src/tests/white_box/D3D12DescriptorHeapTests.cpp index 2bd94ed0a3..77e6b805d9 100644 --- a/src/tests/white_box/D3D12DescriptorHeapTests.cpp +++ b/src/tests/white_box/D3D12DescriptorHeapTests.cpp @@ -449,8 +449,8 @@ TEST_P(D3D12DescriptorHeapTests, EncodeManyUBO) { }; [[group(0), binding(0)]] var buffer0 : U; - [[stage(fragment)]] fn main() -> [[location(0)]] f32 { - return buffer0.heapSize; + [[stage(fragment)]] fn main() -> [[location(0)]] vec4 { + return vec4(buffer0.heapSize, 0.0, 0.0, 1.0); })"); wgpu::BlendState blend; diff --git a/src/utils/TextureUtils.cpp b/src/utils/TextureUtils.cpp index fd359a2e86..ab9e6c8f2d 100644 --- a/src/utils/TextureUtils.cpp +++ b/src/utils/TextureUtils.cpp @@ -304,6 +304,47 @@ namespace utils { } } + uint32_t GetWGSLRenderableColorTextureComponentCount(wgpu::TextureFormat textureFormat) { + switch (textureFormat) { + case wgpu::TextureFormat::R8Unorm: + case wgpu::TextureFormat::R8Uint: + case wgpu::TextureFormat::R8Sint: + case wgpu::TextureFormat::R16Uint: + case wgpu::TextureFormat::R16Sint: + case wgpu::TextureFormat::R16Float: + case wgpu::TextureFormat::R32Float: + case wgpu::TextureFormat::R32Uint: + case wgpu::TextureFormat::R32Sint: + return 1u; + case wgpu::TextureFormat::RG8Unorm: + case wgpu::TextureFormat::RG8Uint: + case wgpu::TextureFormat::RG8Sint: + case wgpu::TextureFormat::RG16Uint: + case wgpu::TextureFormat::RG16Sint: + case wgpu::TextureFormat::RG16Float: + case wgpu::TextureFormat::RG32Float: + case wgpu::TextureFormat::RG32Uint: + case wgpu::TextureFormat::RG32Sint: + return 2u; + case wgpu::TextureFormat::RGBA8Unorm: + case wgpu::TextureFormat::RGBA8UnormSrgb: + case wgpu::TextureFormat::RGBA8Uint: + case wgpu::TextureFormat::RGBA8Sint: + case wgpu::TextureFormat::BGRA8Unorm: + case wgpu::TextureFormat::BGRA8UnormSrgb: + case wgpu::TextureFormat::RGB10A2Unorm: + case wgpu::TextureFormat::RGBA16Uint: + case wgpu::TextureFormat::RGBA16Sint: + case wgpu::TextureFormat::RGBA16Float: + case wgpu::TextureFormat::RGBA32Float: + case wgpu::TextureFormat::RGBA32Uint: + case wgpu::TextureFormat::RGBA32Sint: + return 4u; + default: + UNREACHABLE(); + } + } + const char* GetWGSLImageFormatQualifier(wgpu::TextureFormat textureFormat) { switch (textureFormat) { case wgpu::TextureFormat::RGBA8Unorm: diff --git a/src/utils/TextureUtils.h b/src/utils/TextureUtils.h index 51713f1f99..97d24f03a1 100644 --- a/src/utils/TextureUtils.h +++ b/src/utils/TextureUtils.h @@ -95,6 +95,7 @@ namespace utils { const char* GetWGSLColorTextureComponentType(wgpu::TextureFormat textureFormat); const char* GetWGSLImageFormatQualifier(wgpu::TextureFormat textureFormat); + uint32_t GetWGSLRenderableColorTextureComponentCount(wgpu::TextureFormat textureFormat); wgpu::TextureDimension ViewDimensionToTextureDimension( const wgpu::TextureViewDimension dimension);