diff --git a/src/dawn/native/RenderPipeline.cpp b/src/dawn/native/RenderPipeline.cpp index 1f0b263770..da32d2d73d 100644 --- a/src/dawn/native/RenderPipeline.cpp +++ b/src/dawn/native/RenderPipeline.cpp @@ -335,6 +335,7 @@ MaybeError ValidateColorTargetState( MaybeError ValidateFragmentState(DeviceBase* device, const FragmentState* descriptor, const PipelineLayoutBase* layout, + const DepthStencilState* depthStencil, bool alphaToCoverageEnabled) { DAWN_INVALID_IF(descriptor->nextInChain != nullptr, "nextInChain must be nullptr."); @@ -351,6 +352,22 @@ MaybeError ValidateFragmentState(DeviceBase* device, const EntryPointMetadata& fragmentMetadata = descriptor->module->GetEntryPoint(descriptor->entryPoint); + + if (fragmentMetadata.usesFragDepth) { + DAWN_INVALID_IF( + depthStencil == nullptr, + "Depth stencil state is not present when fragment stage (%s, entryPoint: %s) is " + "writing to frag_depth.", + descriptor->module, descriptor->entryPoint); + const Format* depthStencilFormat; + DAWN_TRY_ASSIGN(depthStencilFormat, device->GetInternalFormat(depthStencil->format)); + DAWN_INVALID_IF(!depthStencilFormat->HasDepth(), + "Depth stencil state format (%s) has no depth aspect when fragment stage " + "(%s, entryPoint: %s) is " + "writing to frag_depth.", + depthStencil->format, descriptor->module, descriptor->entryPoint); + } + ColorAttachmentFormats colorAttachmentFormats; for (ColorAttachmentIndex i(uint8_t(0)); i < ColorAttachmentIndex(static_cast(descriptor->targetCount)); ++i) { @@ -477,6 +494,7 @@ MaybeError ValidateRenderPipelineDescriptor(DeviceBase* device, if (descriptor->fragment != nullptr) { DAWN_TRY_CONTEXT(ValidateFragmentState(device, descriptor->fragment, descriptor->layout, + descriptor->depthStencil, descriptor->multisample.alphaToCoverageEnabled), "validating fragment state."); diff --git a/src/dawn/tests/unittests/validation/RenderPipelineValidationTests.cpp b/src/dawn/tests/unittests/validation/RenderPipelineValidationTests.cpp index 9e081af6e7..6764bfb99b 100644 --- a/src/dawn/tests/unittests/validation/RenderPipelineValidationTests.cpp +++ b/src/dawn/tests/unittests/validation/RenderPipelineValidationTests.cpp @@ -193,6 +193,52 @@ TEST_F(RenderPipelineValidationTest, DepthStencilAspectRequirement) { // enabled when Stencil8 format is implemented } +// Tests that depth attachment is required when frag_depth is written in fragment stage. +TEST_F(RenderPipelineValidationTest, DepthAttachmentRequiredWhenFragDepthIsWritten) { + wgpu::ShaderModule fsModuleFragDepthOutput = utils::CreateShaderModule(device, R"( + struct Output { + @builtin(frag_depth) depth_out: f32, + @location(0) color : vec4, + } + @fragment fn main() -> Output { + var o: Output; + // We need to make sure this frag_depth isn't optimized out even its value equals "no op". + o.depth_out = 0.5; + o.color = vec4(1.0, 1.0, 1.0, 1.0); + return o; + } + )"); + + { + // Succeeds because there is depth stencil state. + utils::ComboRenderPipelineDescriptor descriptor; + descriptor.vertex.module = vsModule; + descriptor.cFragment.module = fsModuleFragDepthOutput; + descriptor.EnableDepthStencil(wgpu::TextureFormat::Depth24PlusStencil8); + + device.CreateRenderPipeline(&descriptor); + } + + { + // Fails because there is no depth stencil state. + utils::ComboRenderPipelineDescriptor descriptor; + descriptor.vertex.module = vsModule; + descriptor.cFragment.module = fsModuleFragDepthOutput; + + ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor)); + } + + { + // Fails because there is depth stencil state but no depth aspect. + utils::ComboRenderPipelineDescriptor descriptor; + descriptor.vertex.module = vsModule; + descriptor.cFragment.module = fsModuleFragDepthOutput; + descriptor.EnableDepthStencil(wgpu::TextureFormat::Stencil8); + + ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor)); + } +} + // Tests that at least one color target state is required. TEST_F(RenderPipelineValidationTest, ColorTargetStateRequired) { {