Improve validation errors in RenderPipeline

Updates all validation messages in RenderPipeline.cpp to give them
better contextual information.

Bug: dawn:563
Change-Id: Iccf2714c781c2e1d52eaf00bf81f1d5643635cf7
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/65484
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Brandon Jones <bajones@chromium.org>
This commit is contained in:
Brandon Jones 2021-09-30 01:30:12 +00:00 committed by Dawn LUCI CQ
parent 56f3807741
commit 4d2bc396ea
1 changed files with 257 additions and 156 deletions

View File

@ -28,9 +28,91 @@
#include <sstream> #include <sstream>
namespace dawn_native { namespace dawn_native {
absl::FormatConvertResult<absl::FormatConversionCharSet::kString> AbslFormatConvert(
VertexFormatBaseType value,
const absl::FormatConversionSpec& spec,
absl::FormatSink* s) {
switch (value) {
case VertexFormatBaseType::Float:
s->Append("Float");
break;
case VertexFormatBaseType::Uint:
s->Append("Uint");
break;
case VertexFormatBaseType::Sint:
s->Append("Sint");
break;
default:
UNREACHABLE();
}
return {true};
}
absl::FormatConvertResult<absl::FormatConversionCharSet::kString> AbslFormatConvert(
InterStageComponentType value,
const absl::FormatConversionSpec& spec,
absl::FormatSink* s) {
switch (value) {
case InterStageComponentType::Float:
s->Append("Float");
break;
case InterStageComponentType::Uint:
s->Append("Uint");
break;
case InterStageComponentType::Sint:
s->Append("Sint");
break;
default:
UNREACHABLE();
}
return {true};
}
absl::FormatConvertResult<absl::FormatConversionCharSet::kString> AbslFormatConvert(
InterpolationType value,
const absl::FormatConversionSpec& spec,
absl::FormatSink* s) {
switch (value) {
case InterpolationType::Perspective:
s->Append("Perspective");
break;
case InterpolationType::Linear:
s->Append("Linear");
break;
case InterpolationType::Flat:
s->Append("Flat");
break;
default:
UNREACHABLE();
}
return {true};
}
absl::FormatConvertResult<absl::FormatConversionCharSet::kString> AbslFormatConvert(
InterpolationSampling value,
const absl::FormatConversionSpec& spec,
absl::FormatSink* s) {
switch (value) {
case InterpolationSampling::None:
s->Append("None");
break;
case InterpolationSampling::Center:
s->Append("Center");
break;
case InterpolationSampling::Centroid:
s->Append("Centroid");
break;
case InterpolationSampling::Sample:
s->Append("Sample");
break;
default:
UNREACHABLE();
}
return {true};
}
// Helper functions // Helper functions
namespace { namespace {
MaybeError ValidateVertexAttribute( MaybeError ValidateVertexAttribute(
DeviceBase* device, DeviceBase* device,
const VertexAttribute* attribute, const VertexAttribute* attribute,
@ -40,40 +122,48 @@ namespace dawn_native {
DAWN_TRY(ValidateVertexFormat(attribute->format)); DAWN_TRY(ValidateVertexFormat(attribute->format));
const VertexFormatInfo& formatInfo = GetVertexFormatInfo(attribute->format); const VertexFormatInfo& formatInfo = GetVertexFormatInfo(attribute->format);
if (attribute->shaderLocation >= kMaxVertexAttributes) { DAWN_INVALID_IF(
return DAWN_VALIDATION_ERROR("Setting attribute out of bounds"); attribute->shaderLocation >= kMaxVertexAttributes,
} "Attribute shader location (%u) exceeds the maximum number of vertex attributes "
"(%u).",
attribute->shaderLocation, kMaxVertexAttributes);
VertexAttributeLocation location(static_cast<uint8_t>(attribute->shaderLocation)); VertexAttributeLocation location(static_cast<uint8_t>(attribute->shaderLocation));
// No underflow is possible because the max vertex format size is smaller than // No underflow is possible because the max vertex format size is smaller than
// kMaxVertexBufferArrayStride. // kMaxVertexBufferArrayStride.
ASSERT(kMaxVertexBufferArrayStride >= formatInfo.byteSize); ASSERT(kMaxVertexBufferArrayStride >= formatInfo.byteSize);
if (attribute->offset > kMaxVertexBufferArrayStride - formatInfo.byteSize) { DAWN_INVALID_IF(
return DAWN_VALIDATION_ERROR("Setting attribute offset out of bounds"); attribute->offset > kMaxVertexBufferArrayStride - formatInfo.byteSize,
} "Attribute offset (%u) with format %s (size: %u) doesn't fit in the maximum vertex "
"buffer stride (%u).",
attribute->offset, attribute->format, formatInfo.byteSize,
kMaxVertexBufferArrayStride);
// No overflow is possible because the offset is already validated to be less // No overflow is possible because the offset is already validated to be less
// than kMaxVertexBufferArrayStride. // than kMaxVertexBufferArrayStride.
ASSERT(attribute->offset < kMaxVertexBufferArrayStride); ASSERT(attribute->offset < kMaxVertexBufferArrayStride);
if (vertexBufferStride > 0 && DAWN_INVALID_IF(
attribute->offset + formatInfo.byteSize > vertexBufferStride) { vertexBufferStride > 0 &&
return DAWN_VALIDATION_ERROR("Setting attribute offset out of bounds"); attribute->offset + formatInfo.byteSize > vertexBufferStride,
} "Attribute offset (%u) with format %s (size: %u) doesn't fit in the vertex buffer "
"stride (%u).",
attribute->offset, attribute->format, formatInfo.byteSize, vertexBufferStride);
if (attribute->offset % std::min(4u, formatInfo.byteSize) != 0) { DAWN_INVALID_IF(attribute->offset % std::min(4u, formatInfo.byteSize) != 0,
return DAWN_VALIDATION_ERROR( "Attribute offset (%u) in not a multiple of %u.", attribute->offset,
"Attribute offset needs to be a multiple of min(4, formatSize)"); std::min(4u, formatInfo.byteSize));
}
if (metadata.usedVertexInputs[location] && DAWN_INVALID_IF(metadata.usedVertexInputs[location] &&
formatInfo.baseType != metadata.vertexInputBaseTypes[location]) { formatInfo.baseType != metadata.vertexInputBaseTypes[location],
return DAWN_VALIDATION_ERROR( "Attribute base type (%s) does not match the "
"Attribute base type must match the base type in the shader."); "shader's base type (%s) in location (%u).",
} formatInfo.baseType, metadata.vertexInputBaseTypes[location],
attribute->shaderLocation);
if ((*attributesSetMask)[location]) { DAWN_INVALID_IF((*attributesSetMask)[location],
return DAWN_VALIDATION_ERROR("Setting already set attribute"); "Attribute shader location (%u) is used more than once.",
} attribute->shaderLocation);
attributesSetMask->set(location); attributesSetMask->set(location);
return {}; return {};
@ -85,18 +175,19 @@ namespace dawn_native {
const EntryPointMetadata& metadata, const EntryPointMetadata& metadata,
ityp::bitset<VertexAttributeLocation, kMaxVertexAttributes>* attributesSetMask) { ityp::bitset<VertexAttributeLocation, kMaxVertexAttributes>* attributesSetMask) {
DAWN_TRY(ValidateVertexStepMode(buffer->stepMode)); DAWN_TRY(ValidateVertexStepMode(buffer->stepMode));
if (buffer->arrayStride > kMaxVertexBufferArrayStride) { DAWN_INVALID_IF(
return DAWN_VALIDATION_ERROR("Setting arrayStride out of bounds"); buffer->arrayStride > kMaxVertexBufferArrayStride,
} "Vertex buffer arrayStride (%u) is larger than the maximum array stride (%u).",
buffer->arrayStride, kMaxVertexBufferArrayStride);
if (buffer->arrayStride % 4 != 0) { DAWN_INVALID_IF(buffer->arrayStride % 4 != 0,
return DAWN_VALIDATION_ERROR( "Vertex buffer arrayStride (%u) is not a multiple of 4.",
"arrayStride of Vertex buffer needs to be a multiple of 4 bytes"); buffer->arrayStride);
}
for (uint32_t i = 0; i < buffer->attributeCount; ++i) { for (uint32_t i = 0; i < buffer->attributeCount; ++i) {
DAWN_TRY(ValidateVertexAttribute(device, &buffer->attributes[i], metadata, DAWN_TRY_CONTEXT(ValidateVertexAttribute(device, &buffer->attributes[i], metadata,
buffer->arrayStride, attributesSetMask)); buffer->arrayStride, attributesSetMask),
"validating attributes[%u].", i);
} }
return {}; return {};
@ -105,25 +196,28 @@ namespace dawn_native {
MaybeError ValidateVertexState(DeviceBase* device, MaybeError ValidateVertexState(DeviceBase* device,
const VertexState* descriptor, const VertexState* descriptor,
const PipelineLayoutBase* layout) { const PipelineLayoutBase* layout) {
if (descriptor->nextInChain != nullptr) { DAWN_INVALID_IF(descriptor->nextInChain != nullptr, "nextInChain must be nullptr.");
return DAWN_VALIDATION_ERROR("nextInChain must be nullptr");
}
if (descriptor->bufferCount > kMaxVertexBuffers) { DAWN_INVALID_IF(
return DAWN_VALIDATION_ERROR("Vertex buffer count exceeds maximum"); descriptor->bufferCount > kMaxVertexBuffers,
} "Vertex buffer count (%u) exceeds the maximum number of vertex buffers (%u).",
descriptor->bufferCount, kMaxVertexBuffers);
DAWN_TRY(ValidateProgrammableStage(device, descriptor->module, descriptor->entryPoint, DAWN_TRY_CONTEXT(
descriptor->constantCount, descriptor->constants, ValidateProgrammableStage(device, descriptor->module, descriptor->entryPoint,
layout, SingleShaderStage::Vertex)); descriptor->constantCount, descriptor->constants, layout,
SingleShaderStage::Vertex),
"validating vertex stage (module: %s, entryPoint: %s).", descriptor->module,
descriptor->entryPoint);
const EntryPointMetadata& vertexMetadata = const EntryPointMetadata& vertexMetadata =
descriptor->module->GetEntryPoint(descriptor->entryPoint); descriptor->module->GetEntryPoint(descriptor->entryPoint);
ityp::bitset<VertexAttributeLocation, kMaxVertexAttributes> attributesSetMask; ityp::bitset<VertexAttributeLocation, kMaxVertexAttributes> attributesSetMask;
uint32_t totalAttributesNum = 0; uint32_t totalAttributesNum = 0;
for (uint32_t i = 0; i < descriptor->bufferCount; ++i) { for (uint32_t i = 0; i < descriptor->bufferCount; ++i) {
DAWN_TRY(ValidateVertexBufferLayout(device, &descriptor->buffers[i], vertexMetadata, DAWN_TRY_CONTEXT(ValidateVertexBufferLayout(device, &descriptor->buffers[i],
&attributesSetMask)); vertexMetadata, &attributesSetMask),
"validating buffers[%u].", i);
totalAttributesNum += descriptor->buffers[i].attributeCount; totalAttributesNum += descriptor->buffers[i].attributeCount;
} }
@ -133,10 +227,9 @@ namespace dawn_native {
// attribute number never exceed kMaxVertexAttributes. // attribute number never exceed kMaxVertexAttributes.
ASSERT(totalAttributesNum <= kMaxVertexAttributes); ASSERT(totalAttributesNum <= kMaxVertexAttributes);
if (!IsSubset(vertexMetadata.usedVertexInputs, attributesSetMask)) { // TODO(dawn:563): Specify which inputs were not used in error message.
return DAWN_VALIDATION_ERROR( DAWN_INVALID_IF(!IsSubset(vertexMetadata.usedVertexInputs, attributesSetMask),
"Pipeline vertex stage uses vertex buffers not in the vertex state"); "Pipeline vertex stage uses vertex buffers not in the vertex state");
}
return {}; return {};
} }
@ -158,14 +251,16 @@ namespace dawn_native {
// Pipeline descriptors must have stripIndexFormat != undefined IFF they are using strip // Pipeline descriptors must have stripIndexFormat != undefined IFF they are using strip
// topologies. // topologies.
if (IsStripPrimitiveTopology(descriptor->topology)) { if (IsStripPrimitiveTopology(descriptor->topology)) {
if (descriptor->stripIndexFormat == wgpu::IndexFormat::Undefined) { DAWN_INVALID_IF(
return DAWN_VALIDATION_ERROR( descriptor->stripIndexFormat == wgpu::IndexFormat::Undefined,
"stripIndexFormat must not be undefined when using strip primitive " "StripIndexFormat is undefined when using a strip primitive topology (%s).",
"topologies"); descriptor->topology);
} } else {
} else if (descriptor->stripIndexFormat != wgpu::IndexFormat::Undefined) { DAWN_INVALID_IF(
return DAWN_VALIDATION_ERROR( descriptor->stripIndexFormat != wgpu::IndexFormat::Undefined,
"stripIndexFormat must be undefined when using non-strip primitive topologies"); "StripIndexFormat (%s) is not undefined when using a non-strip primitive "
"topology (%s).",
descriptor->stripIndexFormat, descriptor->topology);
} }
return {}; return {};
@ -189,31 +284,27 @@ namespace dawn_native {
const Format* format; const Format* format;
DAWN_TRY_ASSIGN(format, device->GetInternalFormat(descriptor->format)); DAWN_TRY_ASSIGN(format, device->GetInternalFormat(descriptor->format));
if (!format->HasDepthOrStencil() || !format->isRenderable) { DAWN_INVALID_IF(!format->HasDepthOrStencil() || !format->isRenderable,
return DAWN_VALIDATION_ERROR( "Depth stencil format (%s) is not depth-stencil renderable.",
"Depth stencil format must be depth-stencil renderable"); descriptor->format);
}
if (std::isnan(descriptor->depthBiasSlopeScale) || DAWN_INVALID_IF(std::isnan(descriptor->depthBiasSlopeScale) ||
std::isnan(descriptor->depthBiasClamp)) { std::isnan(descriptor->depthBiasClamp),
return DAWN_VALIDATION_ERROR("Depth bias parameters must not be NaN."); "Either depthBiasSlopeScale (%f) or depthBiasClamp (%f) is NaN.",
} descriptor->depthBiasSlopeScale, descriptor->depthBiasClamp);
return {}; return {};
} }
MaybeError ValidateMultisampleState(const MultisampleState* descriptor) { MaybeError ValidateMultisampleState(const MultisampleState* descriptor) {
if (descriptor->nextInChain != nullptr) { DAWN_INVALID_IF(descriptor->nextInChain != nullptr, "nextInChain must be nullptr.");
return DAWN_VALIDATION_ERROR("nextInChain must be nullptr");
}
if (!IsValidSampleCount(descriptor->count)) { DAWN_INVALID_IF(!IsValidSampleCount(descriptor->count),
return DAWN_VALIDATION_ERROR("Multisample count is not supported"); "Multisample count (%u) is not supported.", descriptor->count);
}
if (descriptor->alphaToCoverageEnabled && descriptor->count <= 1) { DAWN_INVALID_IF(descriptor->alphaToCoverageEnabled && descriptor->count <= 1,
return DAWN_VALIDATION_ERROR("Enabling alphaToCoverage requires sample count > 1"); "Multisample count (%u) must be > 1 when alphaToCoverage is enabled.",
} descriptor->count);
return {}; return {};
} }
@ -239,57 +330,58 @@ namespace dawn_native {
const ColorTargetState* descriptor, const ColorTargetState* descriptor,
bool fragmentWritten, bool fragmentWritten,
const EntryPointMetadata::FragmentOutputVariableInfo& fragmentOutputVariable) { const EntryPointMetadata::FragmentOutputVariableInfo& fragmentOutputVariable) {
if (descriptor->nextInChain != nullptr) { DAWN_INVALID_IF(descriptor->nextInChain != nullptr, "nextInChain must be nullptr.");
return DAWN_VALIDATION_ERROR("nextInChain must be nullptr");
}
if (descriptor->blend) { if (descriptor->blend) {
DAWN_TRY(ValidateBlendState(device, descriptor->blend)); DAWN_TRY_CONTEXT(ValidateBlendState(device, descriptor->blend),
"validating blend state.");
} }
DAWN_TRY(ValidateColorWriteMask(descriptor->writeMask)); DAWN_TRY(ValidateColorWriteMask(descriptor->writeMask));
const Format* format; const Format* format;
DAWN_TRY_ASSIGN(format, device->GetInternalFormat(descriptor->format)); DAWN_TRY_ASSIGN(format, device->GetInternalFormat(descriptor->format));
if (!format->IsColor() || !format->isRenderable) { DAWN_INVALID_IF(!format->IsColor() || !format->isRenderable,
return DAWN_VALIDATION_ERROR("Color format must be color renderable"); "Color format (%s) is not color renderable.", descriptor->format);
}
if (descriptor->blend && !(format->GetAspectInfo(Aspect::Color).supportedSampleTypes &
SampleTypeBit::Float)) {
return DAWN_VALIDATION_ERROR(
"Color format must be blendable when blending is enabled");
}
if (fragmentWritten) {
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) { DAWN_INVALID_IF(
return DAWN_VALIDATION_ERROR( descriptor->blend && !(format->GetAspectInfo(Aspect::Color).supportedSampleTypes &
"The fragment stage output components count must be no fewer than the " SampleTypeBit::Float),
"color format channel count"); "Blending is enabled but color format (%s) is not blendable.", descriptor->format);
}
if (fragmentWritten) {
DAWN_INVALID_IF(fragmentOutputVariable.baseType !=
format->GetAspectInfo(Aspect::Color).baseType,
"Color format (%s) base type (%s) doesn't match the fragment "
"module output type (%s).",
descriptor->format, format->GetAspectInfo(Aspect::Color).baseType,
fragmentOutputVariable.baseType);
DAWN_INVALID_IF(
fragmentOutputVariable.componentCount < format->componentCount,
"The fragment stage has fewer output components (%u) than the color format "
"(%s) component count (%u).",
fragmentOutputVariable.componentCount, descriptor->format,
format->componentCount);
if (descriptor->blend) { if (descriptor->blend) {
if (fragmentOutputVariable.componentCount < 4u) { if (fragmentOutputVariable.componentCount < 4u) {
// No alpha channel output // No alpha channel output
// Make sure there's no alpha involved in the blending operation // Make sure there's no alpha involved in the blending operation
if (BlendFactorContainsSrcAlpha(descriptor->blend->color.srcFactor) || DAWN_INVALID_IF(
BlendFactorContainsSrcAlpha(descriptor->blend->color.dstFactor)) { BlendFactorContainsSrcAlpha(descriptor->blend->color.srcFactor) ||
return DAWN_VALIDATION_ERROR( BlendFactorContainsSrcAlpha(descriptor->blend->color.dstFactor),
"Color blending factor is reading alpha but it is missing from " "Color blending srcfactor (%s) or dstFactor (%s) is reading alpha "
"fragment output"); "but it is missing from fragment output.",
} descriptor->blend->color.srcFactor, descriptor->blend->color.dstFactor);
} }
} }
} else { } else {
if (descriptor->writeMask != wgpu::ColorWriteMask::None) { DAWN_INVALID_IF(
return DAWN_VALIDATION_ERROR( descriptor->writeMask != wgpu::ColorWriteMask::None,
"writeMask must be zero for color targets with no corresponding fragment " "Color target has no corresponding fragment stage output but writeMask (%s) is "
"stage output"); "not zero.",
} descriptor->writeMask);
} }
return {}; return {};
@ -298,26 +390,28 @@ namespace dawn_native {
MaybeError ValidateFragmentState(DeviceBase* device, MaybeError ValidateFragmentState(DeviceBase* device,
const FragmentState* descriptor, const FragmentState* descriptor,
const PipelineLayoutBase* layout) { const PipelineLayoutBase* layout) {
if (descriptor->nextInChain != nullptr) { DAWN_INVALID_IF(descriptor->nextInChain != nullptr, "nextInChain must be nullptr.");
return DAWN_VALIDATION_ERROR("nextInChain must be nullptr");
}
DAWN_TRY(ValidateProgrammableStage(device, descriptor->module, descriptor->entryPoint, DAWN_TRY_CONTEXT(
descriptor->constantCount, descriptor->constants, ValidateProgrammableStage(device, descriptor->module, descriptor->entryPoint,
layout, SingleShaderStage::Fragment)); descriptor->constantCount, descriptor->constants, layout,
SingleShaderStage::Fragment),
"validating fragment stage (module: %s, entryPoint: %s).", descriptor->module,
descriptor->entryPoint);
if (descriptor->targetCount > kMaxColorAttachments) { DAWN_INVALID_IF(descriptor->targetCount > kMaxColorAttachments,
return DAWN_VALIDATION_ERROR("Number of color targets exceeds maximum"); "Number of targets (%u) exceeds the maximum (%u).",
} descriptor->targetCount, kMaxColorAttachments);
const EntryPointMetadata& fragmentMetadata = const EntryPointMetadata& fragmentMetadata =
descriptor->module->GetEntryPoint(descriptor->entryPoint); descriptor->module->GetEntryPoint(descriptor->entryPoint);
for (ColorAttachmentIndex i(uint8_t(0)); for (ColorAttachmentIndex i(uint8_t(0));
i < ColorAttachmentIndex(static_cast<uint8_t>(descriptor->targetCount)); ++i) { i < ColorAttachmentIndex(static_cast<uint8_t>(descriptor->targetCount)); ++i) {
DAWN_TRY(ValidateColorTargetState(device, DAWN_TRY_CONTEXT(
&descriptor->targets[static_cast<uint8_t>(i)], ValidateColorTargetState(device, &descriptor->targets[static_cast<uint8_t>(i)],
fragmentMetadata.fragmentOutputsWritten[i], fragmentMetadata.fragmentOutputsWritten[i],
fragmentMetadata.fragmentOutputVariables[i])); fragmentMetadata.fragmentOutputVariables[i]),
"validating targets[%u].", static_cast<uint8_t>(i));
} }
return {}; return {};
@ -331,36 +425,41 @@ namespace dawn_native {
const EntryPointMetadata& fragmentMetadata = const EntryPointMetadata& fragmentMetadata =
fragmentState.module->GetEntryPoint(fragmentState.entryPoint); fragmentState.module->GetEntryPoint(fragmentState.entryPoint);
if (vertexMetadata.usedInterStageVariables != // TODO(dawn:563): Can this message give more details?
fragmentMetadata.usedInterStageVariables) { DAWN_INVALID_IF(
return DAWN_VALIDATION_ERROR( vertexMetadata.usedInterStageVariables != fragmentMetadata.usedInterStageVariables,
"One or more fragment inputs and vertex outputs are not one-to-one matching"); "One or more fragment inputs and vertex outputs are not one-to-one matching");
}
auto generateErrorString = [](const char* interStageAttribute, size_t location) {
std::ostringstream stream;
stream << "The " << interStageAttribute << " of the vertex output at location "
<< location
<< " is different from the one of the fragment input at the same location";
return stream.str();
};
// TODO(dawn:802): Validate interpolation types and interpolition sampling types // TODO(dawn:802): Validate interpolation types and interpolition sampling types
for (size_t i : IterateBitSet(vertexMetadata.usedInterStageVariables)) { for (size_t i : IterateBitSet(vertexMetadata.usedInterStageVariables)) {
const auto& vertexOutputInfo = vertexMetadata.interStageVariables[i]; const auto& vertexOutputInfo = vertexMetadata.interStageVariables[i];
const auto& fragmentInputInfo = fragmentMetadata.interStageVariables[i]; const auto& fragmentInputInfo = fragmentMetadata.interStageVariables[i];
if (vertexOutputInfo.baseType != fragmentInputInfo.baseType) { DAWN_INVALID_IF(
return DAWN_VALIDATION_ERROR(generateErrorString("base type", i)); vertexOutputInfo.baseType != fragmentInputInfo.baseType,
} "The base type (%s) of the vertex output at location %u is different from the "
if (vertexOutputInfo.componentCount != fragmentInputInfo.componentCount) { "base type (%s) of the fragment input at location %u.",
return DAWN_VALIDATION_ERROR(generateErrorString("componentCount", i)); vertexOutputInfo.baseType, i, fragmentInputInfo.baseType, i);
}
if (vertexOutputInfo.interpolationType != fragmentInputInfo.interpolationType) { DAWN_INVALID_IF(
return DAWN_VALIDATION_ERROR(generateErrorString("interpolation type", i)); vertexOutputInfo.componentCount != fragmentInputInfo.componentCount,
} "The component count (%u) of the vertex output at location %u is different "
if (vertexOutputInfo.interpolationSampling != "from the component count (%u) of the fragment input at location %u.",
fragmentInputInfo.interpolationSampling) { vertexOutputInfo.componentCount, i, fragmentInputInfo.componentCount, i);
return DAWN_VALIDATION_ERROR(generateErrorString("interpolation sampling", i));
} DAWN_INVALID_IF(
vertexOutputInfo.interpolationType != fragmentInputInfo.interpolationType,
"The interpolation type (%s) of the vertex output at location %u is different "
"from the interpolation type (%s) of the fragment input at location %u.",
vertexOutputInfo.interpolationType, i, fragmentInputInfo.interpolationType, i);
DAWN_INVALID_IF(
vertexOutputInfo.interpolationSampling !=
fragmentInputInfo.interpolationSampling,
"The interpolation sampling (%s) of the vertex output at location %u is "
"different from the interpolation sampling (%s) of the fragment input at "
"location %u.",
vertexOutputInfo.interpolationSampling, i,
fragmentInputInfo.interpolationSampling, i);
} }
return {}; return {};
@ -387,31 +486,33 @@ namespace dawn_native {
MaybeError ValidateRenderPipelineDescriptor(DeviceBase* device, MaybeError ValidateRenderPipelineDescriptor(DeviceBase* device,
const RenderPipelineDescriptor* descriptor) { const RenderPipelineDescriptor* descriptor) {
if (descriptor->nextInChain != nullptr) { DAWN_INVALID_IF(descriptor->nextInChain != nullptr, "nextInChain must be nullptr.");
return DAWN_VALIDATION_ERROR("nextInChain must be nullptr");
}
if (descriptor->layout != nullptr) { if (descriptor->layout != nullptr) {
DAWN_TRY(device->ValidateObject(descriptor->layout)); DAWN_TRY(device->ValidateObject(descriptor->layout));
} }
DAWN_TRY(ValidateVertexState(device, &descriptor->vertex, descriptor->layout)); DAWN_TRY_CONTEXT(ValidateVertexState(device, &descriptor->vertex, descriptor->layout),
"validating vertex state.");
DAWN_TRY(ValidatePrimitiveState(device, &descriptor->primitive)); DAWN_TRY_CONTEXT(ValidatePrimitiveState(device, &descriptor->primitive),
"validating primitive state.");
if (descriptor->depthStencil) { if (descriptor->depthStencil) {
DAWN_TRY(ValidateDepthStencilState(device, descriptor->depthStencil)); DAWN_TRY_CONTEXT(ValidateDepthStencilState(device, descriptor->depthStencil),
"validating depthStencil state.");
} }
DAWN_TRY(ValidateMultisampleState(&descriptor->multisample)); DAWN_TRY_CONTEXT(ValidateMultisampleState(&descriptor->multisample),
"validating multisample state.");
if (descriptor->fragment != nullptr) { if (descriptor->fragment != nullptr) {
DAWN_TRY(ValidateFragmentState(device, descriptor->fragment, descriptor->layout)); DAWN_TRY_CONTEXT(
ValidateFragmentState(device, descriptor->fragment, descriptor->layout),
"validating fragment state.");
if (descriptor->fragment->targetCount == 0 && !descriptor->depthStencil) { DAWN_INVALID_IF(descriptor->fragment->targetCount == 0 && !descriptor->depthStencil,
return DAWN_VALIDATION_ERROR( "Must have at least one color or depthStencil target.");
"Should have at least one color target or a depthStencil");
}
DAWN_TRY( DAWN_TRY(
ValidateInterStageMatching(device, descriptor->vertex, *(descriptor->fragment))); ValidateInterStageMatching(device, descriptor->vertex, *(descriptor->fragment)));