Validate textures with filtering/non-filtering/comparison samplers

Renames dawn_native::ComponentTypeBit to SampleTypeBit and makes
the bitmask match wgpu::TextureSampleType. wgpu::TextureComponentType
should be removed in a follow-up CL.

The Format table is augmented with float/unfilterable-float information
so that textures can be validated against the BGLEntry's
TextureSampleType.

EntryPointMetadata::ShaderBindingInfo no longer inherits BindingInfo
because the two types are diverging further. Most notably, this CL
reflects from Tint the supported SampleTypeBits for texture bindings.
This bitset is validated against the bind group layout.

Adds an isFiltering getter to SamplerBase. A filtering sampler must
not be used with a non-filtering sampler binding.

Lastly, the CL reflects sampler/texture pairs from Tint and validates
an entrypoint against the pipeline layout that a filtering sampler is
not used with an unfilterable-float texture binding.

Bug: dawn:367
Change-Id: If9f2c0d8fbad5641c2ecc30615a3c68a6ed6150a
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/56521
Reviewed-by: Jiawei Shao <jiawei.shao@intel.com>
Commit-Queue: Austin Eng <enga@chromium.org>
This commit is contained in:
Austin Eng 2021-07-02 02:29:40 +00:00 committed by Dawn LUCI CQ
parent 8d72a7eec3
commit a5f24e590a
15 changed files with 712 additions and 242 deletions

View File

@ -136,10 +136,10 @@ namespace dawn_native {
TextureBase* texture = view->GetTexture();
switch (bindingInfo.bindingType) {
case BindingInfoType::Texture: {
ComponentTypeBit supportedTypes =
texture->GetFormat().GetAspectInfo(aspect).supportedComponentTypes;
ComponentTypeBit requiredType =
SampleTypeToComponentTypeBit(bindingInfo.texture.sampleType);
SampleTypeBit supportedTypes =
texture->GetFormat().GetAspectInfo(aspect).supportedSampleTypes;
SampleTypeBit requiredType =
SampleTypeToSampleTypeBit(bindingInfo.texture.sampleType);
if (!(texture->GetUsage() & wgpu::TextureUsage::Sampled)) {
return DAWN_VALIDATION_ERROR("Texture binding usage mismatch");
@ -193,15 +193,25 @@ namespace dawn_native {
ASSERT(bindingInfo.bindingType == BindingInfoType::Sampler);
switch (bindingInfo.sampler.type) {
case wgpu::SamplerBindingType::Filtering:
case wgpu::SamplerBindingType::NonFiltering:
if (entry.sampler->HasCompareFunction()) {
return DAWN_VALIDATION_ERROR("Did not expect comparison sampler");
if (entry.sampler->IsFiltering()) {
return DAWN_VALIDATION_ERROR(
"Filtering sampler is incompatible with non-filtering sampler "
"binding.");
}
DAWN_FALLTHROUGH;
case wgpu::SamplerBindingType::Filtering:
if (entry.sampler->IsComparison()) {
return DAWN_VALIDATION_ERROR(
"Comparison sampler is incompatible with non-comparison sampler "
"binding.");
}
break;
case wgpu::SamplerBindingType::Comparison:
if (!entry.sampler->HasCompareFunction()) {
return DAWN_VALIDATION_ERROR("Expected comparison sampler");
if (!entry.sampler->IsComparison()) {
return DAWN_VALIDATION_ERROR(
"Non-comparison sampler is imcompatible with comparison sampler "
"binding.");
}
break;
default:

View File

@ -63,6 +63,11 @@ namespace dawn_native {
StorageTextureBindingLayout storageTexture;
};
struct BindingSlot {
BindGroupIndex group;
BindingNumber binding;
};
struct PerStageBindingCounts {
uint32_t sampledTextureCount;
uint32_t samplerCount;

View File

@ -26,55 +26,61 @@ namespace dawn_native {
// Format
// TODO(dawn:527): Remove when unused.
ComponentTypeBit ToComponentTypeBit(wgpu::TextureComponentType type) {
SampleTypeBit ToSampleTypeBit(wgpu::TextureComponentType type) {
switch (type) {
case wgpu::TextureComponentType::Float:
return SampleTypeBit::Float;
case wgpu::TextureComponentType::Sint:
return SampleTypeBit::Sint;
case wgpu::TextureComponentType::Uint:
return SampleTypeBit::Uint;
case wgpu::TextureComponentType::DepthComparison:
return SampleTypeBit::Depth;
}
}
SampleTypeBit SampleTypeToSampleTypeBit(wgpu::TextureSampleType sampleType) {
switch (sampleType) {
case wgpu::TextureSampleType::Float:
case wgpu::TextureSampleType::UnfilterableFloat:
case wgpu::TextureSampleType::Sint:
case wgpu::TextureSampleType::Uint:
case wgpu::TextureSampleType::Depth:
case wgpu::TextureSampleType::Undefined:
// When the compiler complains that you need to add a case statement here, please
// also add a corresponding static assert below!
break;
}
// Check that ComponentTypeBit bits are in the same position / order as the respective
// wgpu::TextureComponentType value.
static_assert(ComponentTypeBit::Float ==
static_cast<ComponentTypeBit>(
1 << static_cast<uint32_t>(wgpu::TextureComponentType::Float)),
"");
static_assert(ComponentTypeBit::Uint ==
static_cast<ComponentTypeBit>(
1 << static_cast<uint32_t>(wgpu::TextureComponentType::Uint)),
"");
static_assert(ComponentTypeBit::Sint ==
static_cast<ComponentTypeBit>(
1 << static_cast<uint32_t>(wgpu::TextureComponentType::Sint)),
static_assert(static_cast<uint32_t>(wgpu::TextureSampleType::Undefined) == 0, "");
if (sampleType == wgpu::TextureSampleType::Undefined) {
return SampleTypeBit::None;
}
// Check that SampleTypeBit bits are in the same position / order as the respective
// wgpu::TextureSampleType value.
static_assert(SampleTypeBit::Float ==
static_cast<SampleTypeBit>(
1 << (static_cast<uint32_t>(wgpu::TextureSampleType::Float) - 1)),
"");
static_assert(
ComponentTypeBit::DepthComparison ==
static_cast<ComponentTypeBit>(
1 << static_cast<uint32_t>(wgpu::TextureComponentType::DepthComparison)),
SampleTypeBit::UnfilterableFloat ==
static_cast<SampleTypeBit>(
1 << (static_cast<uint32_t>(wgpu::TextureSampleType::UnfilterableFloat) - 1)),
"");
return static_cast<ComponentTypeBit>(1 << static_cast<uint32_t>(type));
}
ComponentTypeBit SampleTypeToComponentTypeBit(wgpu::TextureSampleType sampleType) {
switch (sampleType) {
case wgpu::TextureSampleType::Float:
case wgpu::TextureSampleType::UnfilterableFloat:
return ComponentTypeBit::Float;
case wgpu::TextureSampleType::Sint:
return ComponentTypeBit::Sint;
case wgpu::TextureSampleType::Uint:
return ComponentTypeBit::Uint;
case wgpu::TextureSampleType::Depth:
return ComponentTypeBit::DepthComparison;
case wgpu::TextureSampleType::Undefined:
UNREACHABLE();
}
// TODO(dawn:527): Ideally we can get this path to use that static_cast method as well.
static_assert(SampleTypeBit::Uint ==
static_cast<SampleTypeBit>(
1 << (static_cast<uint32_t>(wgpu::TextureSampleType::Uint) - 1)),
"");
static_assert(SampleTypeBit::Sint ==
static_cast<SampleTypeBit>(
1 << (static_cast<uint32_t>(wgpu::TextureSampleType::Sint) - 1)),
"");
static_assert(SampleTypeBit::Depth ==
static_cast<SampleTypeBit>(
1 << (static_cast<uint32_t>(wgpu::TextureSampleType::Depth) - 1)),
"");
return static_cast<SampleTypeBit>(1 << (static_cast<uint32_t>(sampleType) - 1));
}
bool Format::IsColor() const {
@ -129,7 +135,8 @@ namespace dawn_native {
FormatTable table;
std::bitset<kKnownFormatCount> formatsSet;
using Type = wgpu::TextureComponentType;
static constexpr SampleTypeBit kAnyFloat =
SampleTypeBit::Float | SampleTypeBit::UnfilterableFloat;
auto AddFormat = [&table, &formatsSet](Format format) {
size_t index = ComputeFormatIndex(format.format);
@ -151,7 +158,7 @@ namespace dawn_native {
auto AddColorFormat = [&AddFormat](wgpu::TextureFormat format, bool renderable,
bool supportsStorageUsage, uint32_t byteSize,
Type type) {
SampleTypeBit sampleTypes) {
Format internalFormat;
internalFormat.format = format;
internalFormat.isRenderable = renderable;
@ -163,8 +170,26 @@ namespace dawn_native {
firstAspect->block.byteSize = byteSize;
firstAspect->block.width = 1;
firstAspect->block.height = 1;
firstAspect->baseType = type;
firstAspect->supportedComponentTypes = ToComponentTypeBit(type);
if (HasOneBit(sampleTypes)) {
switch (sampleTypes) {
case SampleTypeBit::Float:
case SampleTypeBit::UnfilterableFloat:
firstAspect->baseType = wgpu::TextureComponentType::Float;
break;
case SampleTypeBit::Sint:
firstAspect->baseType = wgpu::TextureComponentType::Sint;
break;
case SampleTypeBit::Uint:
firstAspect->baseType = wgpu::TextureComponentType::Uint;
break;
default:
UNREACHABLE();
}
} else {
ASSERT((sampleTypes & SampleTypeBit::Float) != 0);
firstAspect->baseType = wgpu::TextureComponentType::Float;
}
firstAspect->supportedSampleTypes = sampleTypes;
firstAspect->format = format;
AddFormat(internalFormat);
};
@ -182,8 +207,7 @@ namespace dawn_native {
firstAspect->block.width = 1;
firstAspect->block.height = 1;
firstAspect->baseType = wgpu::TextureComponentType::Float;
firstAspect->supportedComponentTypes =
ComponentTypeBit::Float | ComponentTypeBit::DepthComparison;
firstAspect->supportedSampleTypes = kAnyFloat | SampleTypeBit::Depth;
firstAspect->format = format;
AddFormat(internalFormat);
};
@ -201,7 +225,7 @@ namespace dawn_native {
firstAspect->block.width = 1;
firstAspect->block.height = 1;
firstAspect->baseType = wgpu::TextureComponentType::Uint;
firstAspect->supportedComponentTypes = ComponentTypeBit::Uint;
firstAspect->supportedSampleTypes = SampleTypeBit::Uint;
firstAspect->format = format;
AddFormat(internalFormat);
};
@ -220,7 +244,7 @@ namespace dawn_native {
firstAspect->block.width = width;
firstAspect->block.height = height;
firstAspect->baseType = wgpu::TextureComponentType::Float;
firstAspect->supportedComponentTypes = ComponentTypeBit::Float;
firstAspect->supportedSampleTypes = kAnyFloat;
firstAspect->format = format;
AddFormat(internalFormat);
};
@ -247,53 +271,52 @@ namespace dawn_native {
};
// clang-format off
// 1 byte color formats
AddColorFormat(wgpu::TextureFormat::R8Unorm, true, false, 1, Type::Float);
AddColorFormat(wgpu::TextureFormat::R8Snorm, false, false, 1, Type::Float);
AddColorFormat(wgpu::TextureFormat::R8Uint, true, false, 1, Type::Uint);
AddColorFormat(wgpu::TextureFormat::R8Sint, true, false, 1, Type::Sint);
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);
// 2 bytes color formats
AddColorFormat(wgpu::TextureFormat::R16Uint, true, false, 2, Type::Uint);
AddColorFormat(wgpu::TextureFormat::R16Sint, true, false, 2, Type::Sint);
AddColorFormat(wgpu::TextureFormat::R16Float, true, false, 2, Type::Float);
AddColorFormat(wgpu::TextureFormat::RG8Unorm, true, false, 2, Type::Float);
AddColorFormat(wgpu::TextureFormat::RG8Snorm, false, false, 2, Type::Float);
AddColorFormat(wgpu::TextureFormat::RG8Uint, true, false, 2, Type::Uint);
AddColorFormat(wgpu::TextureFormat::RG8Sint, true, false, 2, Type::Sint);
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);
// 4 bytes color formats
AddColorFormat(wgpu::TextureFormat::R32Uint, true, true, 4, Type::Uint);
AddColorFormat(wgpu::TextureFormat::R32Sint, true, true, 4, Type::Sint);
AddColorFormat(wgpu::TextureFormat::R32Float, true, true, 4, Type::Float);
AddColorFormat(wgpu::TextureFormat::RG16Uint, true, false, 4, Type::Uint);
AddColorFormat(wgpu::TextureFormat::RG16Sint, true, false, 4, Type::Sint);
AddColorFormat(wgpu::TextureFormat::RG16Float, true, false, 4, Type::Float);
AddColorFormat(wgpu::TextureFormat::RGBA8Unorm, true, true, 4, Type::Float);
AddColorFormat(wgpu::TextureFormat::RGBA8UnormSrgb, true, false, 4, Type::Float);
AddColorFormat(wgpu::TextureFormat::RGBA8Snorm, false, true, 4, Type::Float);
AddColorFormat(wgpu::TextureFormat::RGBA8Uint, true, true, 4, Type::Uint);
AddColorFormat(wgpu::TextureFormat::RGBA8Sint, true, true, 4, Type::Sint);
AddColorFormat(wgpu::TextureFormat::BGRA8Unorm, true, false, 4, Type::Float);
AddColorFormat(wgpu::TextureFormat::BGRA8UnormSrgb, true, false, 4, Type::Float);
AddColorFormat(wgpu::TextureFormat::RGB10A2Unorm, true, false, 4, Type::Float);
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::RG11B10Ufloat, false, false, 4, Type::Float);
AddColorFormat(wgpu::TextureFormat::RGB9E5Ufloat, false, false, 4, Type::Float);
AddColorFormat(wgpu::TextureFormat::RG11B10Ufloat, false, false, 4, kAnyFloat);
AddColorFormat(wgpu::TextureFormat::RGB9E5Ufloat, false, false, 4, kAnyFloat);
// 8 bytes color formats
AddColorFormat(wgpu::TextureFormat::RG32Uint, true, true, 8, Type::Uint);
AddColorFormat(wgpu::TextureFormat::RG32Sint, true, true, 8, Type::Sint);
AddColorFormat(wgpu::TextureFormat::RG32Float, true, true, 8, Type::Float);
AddColorFormat(wgpu::TextureFormat::RGBA16Uint, true, true, 8, Type::Uint);
AddColorFormat(wgpu::TextureFormat::RGBA16Sint, true, true, 8, Type::Sint);
AddColorFormat(wgpu::TextureFormat::RGBA16Float, true, true, 8, Type::Float);
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);
// 16 bytes color formats
AddColorFormat(wgpu::TextureFormat::RGBA32Uint, true, true, 16, Type::Uint);
AddColorFormat(wgpu::TextureFormat::RGBA32Sint, true, true, 16, Type::Sint);
AddColorFormat(wgpu::TextureFormat::RGBA32Float, true, true, 16, Type::Float);
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);
// Depth-stencil formats
AddDepthFormat(wgpu::TextureFormat::Depth32Float, 4);

View File

@ -45,19 +45,20 @@ namespace dawn_native {
enum class Aspect : uint8_t;
class DeviceBase;
// This mirrors wgpu::TextureComponentType as a bitmask instead.
enum class ComponentTypeBit : uint8_t {
// This mirrors wgpu::TextureSampleType as a bitmask instead.
enum class SampleTypeBit : uint8_t {
None = 0x0,
Float = 0x1,
Sint = 0x2,
Uint = 0x4,
DepthComparison = 0x8,
UnfilterableFloat = 0x2,
Depth = 0x4,
Sint = 0x8,
Uint = 0x10,
};
// Converts an wgpu::TextureComponentType to its bitmask representation.
ComponentTypeBit ToComponentTypeBit(wgpu::TextureComponentType type);
SampleTypeBit ToSampleTypeBit(wgpu::TextureComponentType type);
// Converts an wgpu::TextureSampleType to its bitmask representation.
ComponentTypeBit SampleTypeToComponentTypeBit(wgpu::TextureSampleType sampleType);
SampleTypeBit SampleTypeToSampleTypeBit(wgpu::TextureSampleType sampleType);
struct TexelBlockInfo {
uint32_t byteSize;
@ -67,8 +68,10 @@ namespace dawn_native {
struct AspectInfo {
TexelBlockInfo block;
// TODO(crbug.com/dawn/367): Replace TextureComponentType with TextureSampleType, or make it
// an internal Dawn enum.
wgpu::TextureComponentType baseType;
ComponentTypeBit supportedComponentTypes;
SampleTypeBit supportedSampleTypes;
wgpu::TextureFormat format;
};
@ -127,7 +130,7 @@ namespace dawn_native {
namespace wgpu {
template <>
struct IsDawnBitmask<dawn_native::ComponentTypeBit> {
struct IsDawnBitmask<dawn_native::SampleTypeBit> {
static constexpr bool enable = true;
};

View File

@ -88,7 +88,9 @@ namespace dawn_native {
modifiedEntry->binding == mergedEntry.binding &&
modifiedEntry->buffer.type == mergedEntry.buffer.type &&
modifiedEntry->sampler.type == mergedEntry.sampler.type &&
modifiedEntry->texture.sampleType == mergedEntry.texture.sampleType &&
// Compatibility between these sample types is checked below.
(modifiedEntry->texture.sampleType != wgpu::TextureSampleType::Undefined) ==
(mergedEntry.texture.sampleType != wgpu::TextureSampleType::Undefined) &&
modifiedEntry->storageTexture.access == mergedEntry.storageTexture.access;
// Minimum buffer binding size excluded because we take the maximum seen across stages.
@ -98,8 +100,18 @@ namespace dawn_native {
}
if (modifiedEntry->texture.sampleType != wgpu::TextureSampleType::Undefined) {
// Sample types are compatible if they are exactly equal,
// or if the |modifiedEntry| is Float and the |mergedEntry| is UnfilterableFloat.
// Note that the |mergedEntry| never has type Float. Texture bindings all start
// as UnfilterableFloat and are promoted to Float if they are statically used with
// a sampler.
ASSERT(mergedEntry.texture.sampleType != wgpu::TextureSampleType::Float);
bool compatibleSampleTypes =
modifiedEntry->texture.sampleType == mergedEntry.texture.sampleType ||
(modifiedEntry->texture.sampleType == wgpu::TextureSampleType::Float &&
mergedEntry.texture.sampleType == wgpu::TextureSampleType::UnfilterableFloat);
compatible =
compatible &&
compatible && compatibleSampleTypes &&
modifiedEntry->texture.viewDimension == mergedEntry.texture.viewDimension &&
modifiedEntry->texture.multisampled == mergedEntry.texture.multisampled;
}
@ -136,16 +148,51 @@ namespace dawn_native {
BindGroupLayoutEntry entry = {};
switch (shaderBinding.bindingType) {
case BindingInfoType::Buffer:
entry.buffer = shaderBinding.buffer;
entry.buffer.type = shaderBinding.buffer.type;
entry.buffer.hasDynamicOffset = shaderBinding.buffer.hasDynamicOffset;
entry.buffer.minBindingSize = shaderBinding.buffer.minBindingSize;
break;
case BindingInfoType::Sampler:
entry.sampler = shaderBinding.sampler;
if (shaderBinding.sampler.isComparison) {
entry.sampler.type = wgpu::SamplerBindingType::Comparison;
} else {
entry.sampler.type = wgpu::SamplerBindingType::Filtering;
}
break;
case BindingInfoType::Texture:
entry.texture = shaderBinding.texture;
switch (shaderBinding.texture.compatibleSampleTypes) {
case SampleTypeBit::Depth:
entry.texture.sampleType = wgpu::TextureSampleType::Depth;
break;
case SampleTypeBit::Sint:
entry.texture.sampleType = wgpu::TextureSampleType::Sint;
break;
case SampleTypeBit::Uint:
entry.texture.sampleType = wgpu::TextureSampleType::Uint;
break;
case SampleTypeBit::Float:
case SampleTypeBit::UnfilterableFloat:
case SampleTypeBit::None:
UNREACHABLE();
break;
default:
if (shaderBinding.texture.compatibleSampleTypes ==
(SampleTypeBit::Float | SampleTypeBit::UnfilterableFloat)) {
// Default to UnfilterableFloat. It will be promoted to Float if it
// is used with a sampler.
entry.texture.sampleType =
wgpu::TextureSampleType::UnfilterableFloat;
} else {
UNREACHABLE();
}
}
entry.texture.viewDimension = shaderBinding.texture.viewDimension;
entry.texture.multisampled = shaderBinding.texture.multisampled;
break;
case BindingInfoType::StorageTexture:
entry.storageTexture = shaderBinding.storageTexture;
entry.storageTexture.access = shaderBinding.storageTexture.access;
entry.storageTexture.format = shaderBinding.storageTexture.format;
entry.storageTexture.viewDimension = shaderBinding.storageTexture.viewDimension;
break;
case BindingInfoType::ExternalTexture:
// TODO(dawn:728) On backend configurations that use SPIRV-Cross to reflect
@ -185,11 +232,10 @@ namespace dawn_native {
// Loops over all the reflected BindGroupLayoutEntries from shaders.
for (const StageAndDescriptor& stage : stages) {
const EntryPointMetadata::BindingInfoArray& info =
stage.module->GetEntryPoint(stage.entryPoint).bindings;
const EntryPointMetadata& metadata = stage.module->GetEntryPoint(stage.entryPoint);
for (BindGroupIndex group(0); group < info.size(); ++group) {
for (const auto& bindingIt : info[group]) {
for (BindGroupIndex group(0); group < metadata.bindings.size(); ++group) {
for (const auto& bindingIt : metadata.bindings[group]) {
BindingNumber bindingNumber = bindingIt.first;
const EntryPointMetadata::ShaderBindingInfo& shaderBinding = bindingIt.second;
@ -206,6 +252,15 @@ namespace dawn_native {
}
}
}
// Promote any Unfilterable textures used with a sampler to Filtering.
for (const EntryPointMetadata::SamplerTexturePair& pair :
metadata.samplerTexturePairs) {
BindGroupLayoutEntry* entry = &entryData[pair.texture.group][pair.texture.binding];
if (entry->texture.sampleType == wgpu::TextureSampleType::UnfilterableFloat) {
entry->texture.sampleType = wgpu::TextureSampleType::Float;
}
}
}
// Create the bind group layouts. We need to keep track of the last non-empty BGL because

View File

@ -100,10 +100,15 @@ namespace dawn_native {
return new SamplerBase(device, ObjectBase::kError);
}
bool SamplerBase::HasCompareFunction() const {
bool SamplerBase::IsComparison() const {
return mCompareFunction != wgpu::CompareFunction::Undefined;
}
bool SamplerBase::IsFiltering() const {
return mMinFilter == wgpu::FilterMode::Linear || mMagFilter == wgpu::FilterMode::Linear ||
mMipmapFilter == wgpu::FilterMode::Linear;
}
size_t SamplerBase::ComputeContentHash() {
ObjectContentHasher recorder;
recorder.Record(mAddressModeU, mAddressModeV, mAddressModeW, mMagFilter, mMinFilter,

View File

@ -33,7 +33,8 @@ namespace dawn_native {
static SamplerBase* MakeError(DeviceBase* device);
bool HasCompareFunction() const;
bool IsComparison() const;
bool IsFiltering() const;
// Functions necessary for the unordered_set<SamplerBase*>-based cache.
size_t ComputeContentHash() override;

View File

@ -266,17 +266,17 @@ namespace dawn_native {
}
}
wgpu::TextureSampleType TintSampledKindToTextureSampleType(
SampleTypeBit TintSampledKindToSampleTypeBit(
tint::inspector::ResourceBinding::SampledKind s) {
switch (s) {
case tint::inspector::ResourceBinding::SampledKind::kSInt:
return wgpu::TextureSampleType::Sint;
return SampleTypeBit::Sint;
case tint::inspector::ResourceBinding::SampledKind::kUInt:
return wgpu::TextureSampleType::Uint;
return SampleTypeBit::Uint;
case tint::inspector::ResourceBinding::SampledKind::kFloat:
return wgpu::TextureSampleType::Float;
return SampleTypeBit::Float | SampleTypeBit::UnfilterableFloat;
case tint::inspector::ResourceBinding::SampledKind::kUnknown:
return wgpu::TextureSampleType::Undefined;
return SampleTypeBit::None;
}
}
@ -535,10 +535,11 @@ namespace dawn_native {
GetShaderDeclarationString(group, bindingNumber));
}
if (layoutInfo.texture.sampleType != shaderInfo.texture.sampleType) {
if ((SampleTypeToSampleTypeBit(layoutInfo.texture.sampleType) &
shaderInfo.texture.compatibleSampleTypes) == 0) {
return DAWN_VALIDATION_ERROR(
"The texture sampleType of the bind group layout entry is "
"different from " +
"not compatible with " +
GetShaderDeclarationString(group, bindingNumber));
}
@ -621,10 +622,13 @@ namespace dawn_native {
}
case BindingInfoType::Sampler:
// TODO(crbug.com/dawn/367): Temporarily allow using either a sampler or a
// comparison sampler until we can perform the proper shader analysis of
// what type is used in the shader module.
break;
if ((layoutInfo.sampler.type == wgpu::SamplerBindingType::Comparison) !=
shaderInfo.sampler.isComparison) {
return DAWN_VALIDATION_ERROR(
"The sampler type of the bind group layout entry is "
"not compatible with " +
GetShaderDeclarationString(group, bindingNumber));
}
}
}
@ -695,21 +699,23 @@ namespace dawn_native {
info->texture.viewDimension =
SpirvDimToTextureViewDimension(imageType.dim, imageType.arrayed);
info->texture.sampleType =
SpirvBaseTypeToTextureSampleType(textureComponentType);
info->texture.multisampled = imageType.ms;
info->texture.compatibleSampleTypes =
SpirvBaseTypeToSampleTypeBit(textureComponentType);
if (imageType.depth) {
if (imageType.ms) {
return DAWN_VALIDATION_ERROR(
"Multisampled depth textures aren't supported");
}
if (info->texture.sampleType != wgpu::TextureSampleType::Float) {
if ((info->texture.compatibleSampleTypes & SampleTypeBit::Float) ==
0) {
return DAWN_VALIDATION_ERROR(
"Depth textures must have a float type");
}
info->texture.sampleType = wgpu::TextureSampleType::Depth;
info->texture.compatibleSampleTypes = SampleTypeBit::Depth;
}
if (imageType.ms && imageType.arrayed) {
return DAWN_VALIDATION_ERROR(
"Multisampled array textures aren't supported");
@ -779,7 +785,7 @@ namespace dawn_native {
break;
}
case BindingInfoType::Sampler: {
info->sampler.type = wgpu::SamplerBindingType::Filtering;
info->sampler.isComparison = false;
break;
}
case BindingInfoType::ExternalTexture: {
@ -897,26 +903,6 @@ namespace dawn_native {
auto metadata = std::make_unique<EntryPointMetadata>();
DAWN_TRY_ASSIGN(metadata->stage, TintPipelineStageToShaderStage(entryPoint.stage));
if (metadata->stage == SingleShaderStage::Vertex) {
for (auto& stage_input : entryPoint.input_variables) {
if (!stage_input.has_location_decoration) {
return DAWN_VALIDATION_ERROR(
"Need Location decoration on Vertex input");
}
uint32_t location = stage_input.location_decoration;
if (location >= kMaxVertexAttributes) {
return DAWN_VALIDATION_ERROR("Attribute location over limits");
}
metadata->usedVertexAttributes.set(location);
}
for (auto& stage_output : entryPoint.output_variables) {
if (!stage_output.has_location_decoration) {
return DAWN_VALIDATION_ERROR(
"Need Location decoration on Vertex output");
}
}
}
if (metadata->stage == SingleShaderStage::Compute) {
metadata->localWorkgroupSize.x = entryPoint.workgroup_size_x;
@ -926,11 +912,11 @@ namespace dawn_native {
if (metadata->stage == SingleShaderStage::Vertex) {
for (const auto& input_var : entryPoint.input_variables) {
uint32_t location = 0;
if (input_var.has_location_decoration) {
location = input_var.location_decoration;
if (!input_var.has_location_decoration) {
return DAWN_VALIDATION_ERROR(
"Need Location decoration on Vertex input");
}
uint32_t location = input_var.location_decoration;
if (DAWN_UNLIKELY(location >= kMaxVertexAttributes)) {
std::stringstream ss;
ss << "Attribute location (" << location << ") over limits";
@ -978,7 +964,8 @@ namespace dawn_native {
}
}
for (auto& resource : inspector.GetResourceBindings(entryPoint.name)) {
for (const tint::inspector::ResourceBinding& resource :
inspector.GetResourceBindings(entryPoint.name)) {
BindingNumber bindingNumber(resource.binding);
BindGroupIndex bindGroupIndex(resource.bind_group);
if (bindGroupIndex >= kMaxBindGroupsTyped) {
@ -1001,17 +988,27 @@ namespace dawn_native {
resource.resource_type));
break;
case BindingInfoType::Sampler:
info->sampler.type = wgpu::SamplerBindingType::Filtering;
switch (resource.resource_type) {
case tint::inspector::ResourceBinding::ResourceType::kSampler:
info->sampler.isComparison = false;
break;
case tint::inspector::ResourceBinding::ResourceType::
kComparisonSampler:
info->sampler.isComparison = true;
break;
default:
UNREACHABLE();
}
break;
case BindingInfoType::Texture:
info->texture.viewDimension =
TintTextureDimensionToTextureViewDimension(resource.dim);
if (resource.resource_type ==
tint::inspector::ResourceBinding::ResourceType::kDepthTexture) {
info->texture.sampleType = wgpu::TextureSampleType::Depth;
info->texture.compatibleSampleTypes = SampleTypeBit::Depth;
} else {
info->texture.sampleType =
TintSampledKindToTextureSampleType(resource.sampled_kind);
info->texture.compatibleSampleTypes =
TintSampledKindToSampleTypeBit(resource.sampled_kind);
}
info->texture.multisampled = resource.resource_type ==
tint::inspector::ResourceBinding::
@ -1035,6 +1032,21 @@ namespace dawn_native {
}
}
std::vector<tint::inspector::SamplerTexturePair> samplerTextureUses =
inspector.GetSamplerTextureUses(entryPoint.name);
metadata->samplerTexturePairs.reserve(samplerTextureUses.size());
std::transform(
samplerTextureUses.begin(), samplerTextureUses.end(),
std::back_inserter(metadata->samplerTexturePairs),
[](const tint::inspector::SamplerTexturePair& pair) {
EntryPointMetadata::SamplerTexturePair result;
result.sampler = {BindGroupIndex(pair.sampler_binding_point.group),
BindingNumber(pair.sampler_binding_point.binding)};
result.texture = {BindGroupIndex(pair.texture_binding_point.group),
BindingNumber(pair.texture_binding_point.binding)};
return result;
});
result[entryPoint.name] = std::move(metadata);
}
return std::move(result);
@ -1233,6 +1245,43 @@ namespace dawn_native {
}
}
// Validate that filtering samplers are not used with unfilterable textures.
for (const auto& pair : entryPoint.samplerTexturePairs) {
const BindGroupLayoutBase* samplerBGL = layout->GetBindGroupLayout(pair.sampler.group);
const BindingInfo& samplerInfo =
samplerBGL->GetBindingInfo(samplerBGL->GetBindingIndex(pair.sampler.binding));
if (samplerInfo.sampler.type != wgpu::SamplerBindingType::Filtering) {
continue;
}
const BindGroupLayoutBase* textureBGL = layout->GetBindGroupLayout(pair.texture.group);
const BindingInfo& textureInfo =
textureBGL->GetBindingInfo(textureBGL->GetBindingIndex(pair.texture.binding));
ASSERT(textureInfo.bindingType != BindingInfoType::Buffer &&
textureInfo.bindingType != BindingInfoType::Sampler &&
textureInfo.bindingType != BindingInfoType::StorageTexture);
if (textureInfo.bindingType != BindingInfoType::Texture) {
continue;
}
// Uint/sint can't be statically used with a sampler, so they any
// texture bindings reflected must be float or depth textures. If
// the shader uses a float/depth texture but the bind group layout
// specifies a uint/sint texture binding,
// |ValidateCompatibilityWithBindGroupLayout| will fail since the
// sampleType does not match.
ASSERT(textureInfo.texture.sampleType != wgpu::TextureSampleType::Undefined &&
textureInfo.texture.sampleType != wgpu::TextureSampleType::Uint &&
textureInfo.texture.sampleType != wgpu::TextureSampleType::Sint);
if (textureInfo.texture.sampleType == wgpu::TextureSampleType::UnfilterableFloat) {
return DAWN_VALIDATION_ERROR(
"unfilterable-float texture bindings cannot be sampled with a "
"filtering sampler");
}
}
return {};
}

View File

@ -104,16 +104,34 @@ namespace dawn_native {
// pointers to EntryPointMetadata are safe to store as long as you also keep a Ref to the
// ShaderModuleBase.
struct EntryPointMetadata {
// Mirrors wgpu::SamplerBindingLayout but instead stores a single boolean
// for isComparison instead of a wgpu::SamplerBindingType enum.
struct ShaderSamplerBindingInfo {
bool isComparison;
};
// Mirrors wgpu::TextureBindingLayout but instead has a set of compatible sampleTypes
// instead of a single enum.
struct ShaderTextureBindingInfo {
SampleTypeBit compatibleSampleTypes;
wgpu::TextureViewDimension viewDimension;
bool multisampled;
};
// Per-binding shader metadata contains some SPIRV specific information in addition to
// most of the frontend per-binding information.
struct ShaderBindingInfo : BindingInfo {
struct ShaderBindingInfo {
// The SPIRV ID of the resource.
uint32_t id;
uint32_t base_type_id;
private:
// Disallow access to unused members.
using BindingInfo::visibility;
BindingNumber binding;
BindingInfoType bindingType;
BufferBindingLayout buffer;
ShaderSamplerBindingInfo sampler;
ShaderTextureBindingInfo texture;
StorageTextureBindingLayout storageTexture;
};
// bindings[G][B] is the reflection data for the binding defined with
@ -122,6 +140,12 @@ namespace dawn_native {
using BindingInfoArray = ityp::array<BindGroupIndex, BindingGroupInfoMap, kMaxBindGroups>;
BindingInfoArray bindings;
struct SamplerTexturePair {
BindingSlot sampler;
BindingSlot texture;
};
std::vector<SamplerTexturePair> samplerTexturePairs;
// The set of vertex attributes this entryPoint uses.
std::bitset<kMaxVertexAttributes> usedVertexAttributes;

View File

@ -148,15 +148,14 @@ namespace dawn_native {
}
}
wgpu::TextureSampleType SpirvBaseTypeToTextureSampleType(
spirv_cross::SPIRType::BaseType spirvBaseType) {
SampleTypeBit SpirvBaseTypeToSampleTypeBit(spirv_cross::SPIRType::BaseType spirvBaseType) {
switch (spirvBaseType) {
case spirv_cross::SPIRType::Float:
return wgpu::TextureSampleType::Float;
return SampleTypeBit::Float | SampleTypeBit::UnfilterableFloat;
case spirv_cross::SPIRType::Int:
return wgpu::TextureSampleType::Sint;
return SampleTypeBit::Sint;
case spirv_cross::SPIRType::UInt:
return wgpu::TextureSampleType::Uint;
return SampleTypeBit::Uint;
default:
UNREACHABLE();
}

View File

@ -39,8 +39,7 @@ namespace dawn_native {
// Returns the format "component type" corresponding to the SPIRV base type.
wgpu::TextureComponentType SpirvBaseTypeToTextureComponentType(
spirv_cross::SPIRType::BaseType spirvBaseType);
wgpu::TextureSampleType SpirvBaseTypeToTextureSampleType(
spirv_cross::SPIRType::BaseType spirvBaseType);
SampleTypeBit SpirvBaseTypeToSampleTypeBit(spirv_cross::SPIRType::BaseType spirvBaseType);
} // namespace dawn_native

View File

@ -193,17 +193,9 @@ class DepthStencilSamplingTest : public DawnTest {
return textureSampleCompare(tex, samp, vec2<f32>(0.5, 0.5), uniforms.compareRef);
})");
// TODO(crbug.com/dawn/367): Cannot use GetBindGroupLayout for comparison samplers without
// shader reflection data.
wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::Comparison},
{1, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Depth},
{2, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Uniform}});
utils::ComboRenderPipelineDescriptor pipelineDescriptor;
pipelineDescriptor.vertex.module = vsModule;
pipelineDescriptor.cFragment.module = fsModule;
pipelineDescriptor.layout = utils::MakeBasicPipelineLayout(device, &bgl);
pipelineDescriptor.primitive.topology = wgpu::PrimitiveTopology::PointList;
pipelineDescriptor.cTargets[0].format = wgpu::TextureFormat::R32Float;
@ -228,15 +220,7 @@ class DepthStencilSamplingTest : public DawnTest {
samplerResult.value = textureSampleCompare(tex, samp, vec2<f32>(0.5, 0.5), uniforms.compareRef);
})");
// TODO(crbug.com/dawn/367): Cannot use GetBindGroupLayout without shader reflection data.
wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
device, {{0, wgpu::ShaderStage::Compute, wgpu::SamplerBindingType::Comparison},
{1, wgpu::ShaderStage::Compute, wgpu::TextureSampleType::Depth},
{2, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Uniform},
{3, wgpu::ShaderStage::Compute, wgpu::BufferBindingType::Storage}});
wgpu::ComputePipelineDescriptor pipelineDescriptor;
pipelineDescriptor.layout = utils::MakeBasicPipelineLayout(device, &bgl);
pipelineDescriptor.compute.module = csModule;
pipelineDescriptor.compute.entryPoint = "main";
@ -709,6 +693,9 @@ TEST_P(DepthStencilSamplingTest, CompareFunctionsRender) {
// Initialization via renderPass loadOp doesn't work on Mac Intel.
DAWN_SUPPRESS_TEST_IF(IsMetal() && IsIntel());
// Depends on Tint's shader reflection
DAWN_TEST_UNSUPPORTED_IF(!HasToggleEnabled("use_tint_generator"));
wgpu::RenderPipeline pipeline = CreateComparisonRenderPipeline();
for (wgpu::TextureFormat format : kDepthFormats) {
@ -728,6 +715,9 @@ TEST_P(DepthStencilSamplingTest, DISABLED_CompareFunctionsCompute) {
// Initialization via renderPass loadOp doesn't work on Mac Intel.
DAWN_SUPPRESS_TEST_IF(IsMetal() && IsIntel());
// Depends on Tint's shader reflection
DAWN_TEST_UNSUPPORTED_IF(!HasToggleEnabled("use_tint_generator"));
wgpu::ComputePipeline pipeline = CreateComparisonComputePipeline();
for (wgpu::TextureFormat format : kDepthFormats) {

View File

@ -435,20 +435,61 @@ TEST_F(BindGroupValidationTest, StorageTextureUsage) {
ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, view}}));
}
// Check that a texture must have the correct component type
TEST_F(BindGroupValidationTest, TextureComponentType) {
wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout(
device, {{0, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Float}});
// Check that a texture must have the correct sample type
TEST_F(BindGroupValidationTest, TextureSampleType) {
auto DoTest = [this](bool success, wgpu::TextureFormat format,
wgpu::TextureSampleType sampleType) {
wgpu::BindGroupLayout layout =
utils::MakeBindGroupLayout(device, {{0, wgpu::ShaderStage::Fragment, sampleType}});
// Control case: setting a Float typed texture view works.
utils::MakeBindGroup(device, layout, {{0, mSampledTextureView}});
wgpu::TextureDescriptor descriptor;
descriptor.size = {4, 4, 1};
descriptor.usage = wgpu::TextureUsage::Sampled;
descriptor.format = format;
// Make a Uint component typed texture and try to set it to a Float component binding.
wgpu::Texture uintTexture =
CreateTexture(wgpu::TextureUsage::Sampled, wgpu::TextureFormat::RGBA8Uint, 1);
wgpu::TextureView uintTextureView = uintTexture.CreateView();
wgpu::TextureView view = device.CreateTexture(&descriptor).CreateView();
ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, uintTextureView}}));
if (success) {
utils::MakeBindGroup(device, layout, {{0, view}});
} else {
ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, view}}));
}
};
// Test that RGBA8Unorm is only compatible with float/unfilterable-float
DoTest(true, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureSampleType::Float);
DoTest(true, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureSampleType::UnfilterableFloat);
DoTest(false, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureSampleType::Depth);
DoTest(false, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureSampleType::Uint);
DoTest(false, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureSampleType::Sint);
// Test that R32Float is only compatible with unfilterable-float
DoTest(false, wgpu::TextureFormat::R32Float, wgpu::TextureSampleType::Float);
DoTest(true, wgpu::TextureFormat::R32Float, wgpu::TextureSampleType::UnfilterableFloat);
DoTest(false, wgpu::TextureFormat::R32Float, wgpu::TextureSampleType::Depth);
DoTest(false, wgpu::TextureFormat::R32Float, wgpu::TextureSampleType::Uint);
DoTest(false, wgpu::TextureFormat::R32Float, wgpu::TextureSampleType::Sint);
// Test that Depth32Float is only compatible with float/unfilterable-float/depth
DoTest(true, wgpu::TextureFormat::Depth32Float, wgpu::TextureSampleType::Float);
DoTest(true, wgpu::TextureFormat::Depth32Float, wgpu::TextureSampleType::UnfilterableFloat);
DoTest(true, wgpu::TextureFormat::Depth32Float, wgpu::TextureSampleType::Depth);
DoTest(false, wgpu::TextureFormat::Depth32Float, wgpu::TextureSampleType::Uint);
DoTest(false, wgpu::TextureFormat::Depth32Float, wgpu::TextureSampleType::Sint);
// Test that RG8Uint is only compatible with uint
DoTest(false, wgpu::TextureFormat::RG8Uint, wgpu::TextureSampleType::Float);
DoTest(false, wgpu::TextureFormat::RG8Uint, wgpu::TextureSampleType::UnfilterableFloat);
DoTest(false, wgpu::TextureFormat::RG8Uint, wgpu::TextureSampleType::Depth);
DoTest(true, wgpu::TextureFormat::RG8Uint, wgpu::TextureSampleType::Uint);
DoTest(false, wgpu::TextureFormat::RG8Uint, wgpu::TextureSampleType::Sint);
// Test that R16Sint is only compatible with sint
DoTest(false, wgpu::TextureFormat::R16Sint, wgpu::TextureSampleType::Float);
DoTest(false, wgpu::TextureFormat::R16Sint, wgpu::TextureSampleType::UnfilterableFloat);
DoTest(false, wgpu::TextureFormat::R16Sint, wgpu::TextureSampleType::Depth);
DoTest(false, wgpu::TextureFormat::R16Sint, wgpu::TextureSampleType::Uint);
DoTest(true, wgpu::TextureFormat::R16Sint, wgpu::TextureSampleType::Sint);
}
// Test which depth-stencil formats are allowed to be sampled (all).
@ -493,36 +534,6 @@ TEST_F(BindGroupValidationTest, SamplingDepthStencilTexture) {
}
}
// Check that a texture must have a correct format for DepthComparison
TEST_F(BindGroupValidationTest, TextureComponentTypeDepthComparison) {
wgpu::BindGroupLayout depthLayout = utils::MakeBindGroupLayout(
device, {{0, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Depth}});
// Control case: setting a depth texture works.
wgpu::Texture depthTexture =
CreateTexture(wgpu::TextureUsage::Sampled, wgpu::TextureFormat::Depth32Float, 1);
utils::MakeBindGroup(device, depthLayout, {{0, depthTexture.CreateView()}});
// Error case: setting a Float typed texture view fails.
ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, depthLayout, {{0, mSampledTextureView}}));
}
// Check that a depth texture is allowed to be used for both TextureComponentType::Float and
// ::DepthComparison
TEST_F(BindGroupValidationTest, TextureComponentTypeForDepthTexture) {
wgpu::BindGroupLayout depthLayout = utils::MakeBindGroupLayout(
device, {{0, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Depth}});
wgpu::BindGroupLayout floatLayout = utils::MakeBindGroupLayout(
device, {{0, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Float}});
wgpu::Texture depthTexture =
CreateTexture(wgpu::TextureUsage::Sampled, wgpu::TextureFormat::Depth32Float, 1);
utils::MakeBindGroup(device, depthLayout, {{0, depthTexture.CreateView()}});
utils::MakeBindGroup(device, floatLayout, {{0, depthTexture.CreateView()}});
}
// Check that a texture must have the correct dimension
TEST_F(BindGroupValidationTest, TextureDimension) {
wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout(
@ -2324,7 +2335,7 @@ TEST_F(BindingsValidationTest, BindGroupsWithLessBindingsThanPipelineLayout) {
TestComputePassBindings(bg.data(), kBindingNum, computePipeline, false);
}
class ComparisonSamplerBindingTest : public ValidationTest {
class SamplerTypeBindingTest : public ValidationTest {
protected:
wgpu::RenderPipeline CreateFragmentPipeline(wgpu::BindGroupLayout* bindGroupLayout,
const char* fragmentSource) {
@ -2345,10 +2356,13 @@ class ComparisonSamplerBindingTest : public ValidationTest {
}
};
// TODO(crbug.com/dawn/367): Disabled until we can perform shader analysis
// of which samplers are comparison samplers.
TEST_F(ComparisonSamplerBindingTest, DISABLED_ShaderAndBGLMatches) {
// Test that sampler binding works with normal sampler in the shader.
// Test that the use of sampler and comparison_sampler in the shader must match the bind group
// layout.
TEST_F(SamplerTypeBindingTest, ShaderAndBGLMatches) {
// Tint needed for proper shader reflection.
DAWN_SKIP_TEST_IF(!HasToggleEnabled("use_tint_generator"));
// Test that a filtering sampler binding works with normal sampler in the shader.
{
wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::Filtering}});
@ -2356,11 +2370,23 @@ TEST_F(ComparisonSamplerBindingTest, DISABLED_ShaderAndBGLMatches) {
CreateFragmentPipeline(&bindGroupLayout, R"(
[[group(0), binding(0)]] var mySampler: sampler;
[[stage(fragment)]] fn main() {
let s : sampler = mySampler;
ignore(mySampler);
})");
}
// Test that comparison sampler binding works with shadow sampler in the shader.
// Test that a non-filtering sampler binding works with normal sampler in the shader.
{
wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::NonFiltering}});
CreateFragmentPipeline(&bindGroupLayout, R"(
[[group(0), binding(0)]] var mySampler: sampler;
[[stage(fragment)]] fn main() {
ignore(mySampler);
})");
}
// Test that comparison sampler binding works with comparison sampler in the shader.
{
wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::Comparison}});
@ -2368,11 +2394,11 @@ TEST_F(ComparisonSamplerBindingTest, DISABLED_ShaderAndBGLMatches) {
CreateFragmentPipeline(&bindGroupLayout, R"(
[[group(0), binding(0)]] var mySampler: sampler_comparison;
[[stage(fragment)]] fn main() {
let s : sampler_comparison = mySampler;
ignore(mySampler);
})");
}
// Test that sampler binding does not work with comparison sampler in the shader.
// Test that filtering sampler binding does not work with comparison sampler in the shader.
{
wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::Filtering}});
@ -2380,7 +2406,19 @@ TEST_F(ComparisonSamplerBindingTest, DISABLED_ShaderAndBGLMatches) {
ASSERT_DEVICE_ERROR(CreateFragmentPipeline(&bindGroupLayout, R"(
[[group(0), binding(0)]] var mySampler: sampler_comparison;
[[stage(fragment)]] fn main() {
let s : sampler_comparison = mySampler;
ignore(mySampler);
})"));
}
// Test that non-filtering sampler binding does not work with comparison sampler in the shader.
{
wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::NonFiltering}});
ASSERT_DEVICE_ERROR(CreateFragmentPipeline(&bindGroupLayout, R"(
[[group(0), binding(0)]] var mySampler: sampler_comparison;
[[stage(fragment)]] fn main() {
ignore(mySampler);
})"));
}
@ -2392,12 +2430,110 @@ TEST_F(ComparisonSamplerBindingTest, DISABLED_ShaderAndBGLMatches) {
ASSERT_DEVICE_ERROR(CreateFragmentPipeline(&bindGroupLayout, R"(
[[group(0), binding(0)]] var mySampler: sampler;
[[stage(fragment)]] fn main() {
let s : sampler = mySampler;
ignore(mySampler);
})"));
}
// Test that a filtering sampler can be used to sample a float texture.
{
wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::Filtering},
{1, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Float}});
CreateFragmentPipeline(&bindGroupLayout, R"(
[[group(0), binding(0)]] var mySampler: sampler;
[[group(0), binding(1)]] var myTexture: texture_2d<f32>;
[[stage(fragment)]] fn main() {
ignore(textureSample(myTexture, mySampler, vec2<f32>(0.0, 0.0)));
})");
}
// Test that a non-filtering sampler can be used to sample a float texture.
{
wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::NonFiltering},
{1, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Float}});
CreateFragmentPipeline(&bindGroupLayout, R"(
[[group(0), binding(0)]] var mySampler: sampler;
[[group(0), binding(1)]] var myTexture: texture_2d<f32>;
[[stage(fragment)]] fn main() {
ignore(textureSample(myTexture, mySampler, vec2<f32>(0.0, 0.0)));
})");
}
// Test that a filtering sampler can be used to sample a depth texture.
{
wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::Filtering},
{1, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Depth}});
CreateFragmentPipeline(&bindGroupLayout, R"(
[[group(0), binding(0)]] var mySampler: sampler;
[[group(0), binding(1)]] var myTexture: texture_depth_2d;
[[stage(fragment)]] fn main() {
ignore(textureSample(myTexture, mySampler, vec2<f32>(0.0, 0.0)));
})");
}
// Test that a non-filtering sampler can be used to sample a depth texture.
{
wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::NonFiltering},
{1, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Depth}});
CreateFragmentPipeline(&bindGroupLayout, R"(
[[group(0), binding(0)]] var mySampler: sampler;
[[group(0), binding(1)]] var myTexture: texture_depth_2d;
[[stage(fragment)]] fn main() {
ignore(textureSample(myTexture, mySampler, vec2<f32>(0.0, 0.0)));
})");
}
// Test that a comparison sampler can be used to sample a depth texture.
{
wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::Comparison},
{1, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::Depth}});
CreateFragmentPipeline(&bindGroupLayout, R"(
[[group(0), binding(0)]] var mySampler: sampler_comparison;
[[group(0), binding(1)]] var myTexture: texture_depth_2d;
[[stage(fragment)]] fn main() {
ignore(textureSampleCompare(myTexture, mySampler, vec2<f32>(0.0, 0.0), 0.0));
})");
}
// Test that a filtering sampler cannot be used to sample an unfilterable-float texture.
{
wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::Filtering},
{1, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::UnfilterableFloat}});
ASSERT_DEVICE_ERROR(CreateFragmentPipeline(&bindGroupLayout, R"(
[[group(0), binding(0)]] var mySampler: sampler;
[[group(0), binding(1)]] var myTexture: texture_2d<f32>;
[[stage(fragment)]] fn main() {
ignore(textureSample(myTexture, mySampler, vec2<f32>(0.0, 0.0)));
})"));
}
// Test that a non-filtering sampler can be used to sample an unfilterable-float texture.
{
wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::NonFiltering},
{1, wgpu::ShaderStage::Fragment, wgpu::TextureSampleType::UnfilterableFloat}});
CreateFragmentPipeline(&bindGroupLayout, R"(
[[group(0), binding(0)]] var mySampler: sampler;
[[group(0), binding(1)]] var myTexture: texture_2d<f32>;
[[stage(fragment)]] fn main() {
ignore(textureSample(myTexture, mySampler, vec2<f32>(0.0, 0.0)));
})");
}
}
TEST_F(ComparisonSamplerBindingTest, SamplerAndBindGroupMatches) {
TEST_F(SamplerTypeBindingTest, SamplerAndBindGroupMatches) {
// Test that sampler binding works with normal sampler.
{
wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
@ -2436,4 +2572,59 @@ TEST_F(ComparisonSamplerBindingTest, SamplerAndBindGroupMatches) {
ASSERT_DEVICE_ERROR(
utils::MakeBindGroup(device, bindGroupLayout, {{0, device.CreateSampler(&desc)}}));
}
// Test that filtering sampler binding works with a filtering or non-filtering sampler.
{
wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::Filtering}});
// Test each filter member
{
wgpu::SamplerDescriptor desc;
desc.minFilter = wgpu::FilterMode::Linear;
utils::MakeBindGroup(device, bindGroupLayout, {{0, device.CreateSampler(&desc)}});
}
{
wgpu::SamplerDescriptor desc;
desc.magFilter = wgpu::FilterMode::Linear;
utils::MakeBindGroup(device, bindGroupLayout, {{0, device.CreateSampler(&desc)}});
}
{
wgpu::SamplerDescriptor desc;
desc.mipmapFilter = wgpu::FilterMode::Linear;
utils::MakeBindGroup(device, bindGroupLayout, {{0, device.CreateSampler(&desc)}});
}
// Test non-filtering sampler
utils::MakeBindGroup(device, bindGroupLayout, {{0, device.CreateSampler()}});
}
// Test that non-filtering sampler binding does not work with a filtering sampler.
{
wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout(
device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::NonFiltering}});
// Test each filter member
{
wgpu::SamplerDescriptor desc;
desc.minFilter = wgpu::FilterMode::Linear;
ASSERT_DEVICE_ERROR(
utils::MakeBindGroup(device, bindGroupLayout, {{0, device.CreateSampler(&desc)}}));
}
{
wgpu::SamplerDescriptor desc;
desc.magFilter = wgpu::FilterMode::Linear;
ASSERT_DEVICE_ERROR(
utils::MakeBindGroup(device, bindGroupLayout, {{0, device.CreateSampler(&desc)}}));
}
{
wgpu::SamplerDescriptor desc;
desc.mipmapFilter = wgpu::FilterMode::Linear;
ASSERT_DEVICE_ERROR(
utils::MakeBindGroup(device, bindGroupLayout, {{0, device.CreateSampler(&desc)}}));
}
// Test non-filtering sampler
utils::MakeBindGroup(device, bindGroupLayout, {{0, device.CreateSampler()}});
}
}

View File

@ -139,6 +139,122 @@ TEST_F(GetBindGroupLayoutTests, DefaultShaderStageAndDynamicOffsets) {
EXPECT_NE(device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get());
}
TEST_F(GetBindGroupLayoutTests, DefaultTextureSampleType) {
// This test works assuming Dawn Native's object deduplication.
// Getting the same pointer to equivalent bind group layouts is an implementation detail of Dawn
// Native.
DAWN_SKIP_TEST_IF(UsesWire());
// Relies on Tint shader reflection.
DAWN_SKIP_TEST_IF(!HasToggleEnabled("use_tint_generator"));
wgpu::BindGroupLayout filteringBGL = utils::MakeBindGroupLayout(
device, {{0, wgpu::ShaderStage::Vertex | wgpu::ShaderStage::Fragment,
wgpu::TextureSampleType::Float},
{1, wgpu::ShaderStage::Vertex | wgpu::ShaderStage::Fragment,
wgpu::SamplerBindingType::Filtering}});
wgpu::BindGroupLayout nonFilteringBGL = utils::MakeBindGroupLayout(
device, {{0, wgpu::ShaderStage::Vertex | wgpu::ShaderStage::Fragment,
wgpu::TextureSampleType::UnfilterableFloat},
{1, wgpu::ShaderStage::Vertex | wgpu::ShaderStage::Fragment,
wgpu::SamplerBindingType::Filtering}});
wgpu::ShaderModule emptyVertexModule = utils::CreateShaderModule(device, R"(
[[group(0), binding(0)]] var myTexture : texture_2d<f32>;
[[group(0), binding(1)]] var mySampler : sampler;
[[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
ignore(myTexture);
ignore(mySampler);
return vec4<f32>();
})");
wgpu::ShaderModule textureLoadVertexModule = utils::CreateShaderModule(device, R"(
[[group(0), binding(0)]] var myTexture : texture_2d<f32>;
[[group(0), binding(1)]] var mySampler : sampler;
[[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
ignore(textureLoad(myTexture, vec2<i32>(), 0));
ignore(mySampler);
return vec4<f32>();
})");
wgpu::ShaderModule textureSampleVertexModule = utils::CreateShaderModule(device, R"(
[[group(0), binding(0)]] var myTexture : texture_2d<f32>;
[[group(0), binding(1)]] var mySampler : sampler;
[[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
ignore(textureSampleLevel(myTexture, mySampler, vec2<f32>(), 0.0));
return vec4<f32>();
})");
wgpu::ShaderModule unusedTextureFragmentModule = utils::CreateShaderModule(device, R"(
[[group(0), binding(0)]] var myTexture : texture_2d<f32>;
[[group(0), binding(1)]] var mySampler : sampler;
[[stage(fragment)]] fn main() {
ignore(myTexture);
ignore(mySampler);
})");
wgpu::ShaderModule textureLoadFragmentModule = utils::CreateShaderModule(device, R"(
[[group(0), binding(0)]] var myTexture : texture_2d<f32>;
[[group(0), binding(1)]] var mySampler : sampler;
[[stage(fragment)]] fn main() {
ignore(textureLoad(myTexture, vec2<i32>(), 0));
ignore(mySampler);
})");
wgpu::ShaderModule textureSampleFragmentModule = utils::CreateShaderModule(device, R"(
[[group(0), binding(0)]] var myTexture : texture_2d<f32>;
[[group(0), binding(1)]] var mySampler : sampler;
[[stage(fragment)]] fn main() {
ignore(textureSample(myTexture, mySampler, vec2<f32>()));
})");
auto BGLFromModules = [this](wgpu::ShaderModule vertexModule,
wgpu::ShaderModule fragmentModule) {
utils::ComboRenderPipelineDescriptor descriptor;
descriptor.vertex.module = vertexModule;
descriptor.cFragment.module = fragmentModule;
return device.CreateRenderPipeline(&descriptor).GetBindGroupLayout(0);
};
// Textures not used default to non-filtering
EXPECT_EQ(BGLFromModules(emptyVertexModule, unusedTextureFragmentModule).Get(),
nonFilteringBGL.Get());
EXPECT_NE(BGLFromModules(emptyVertexModule, unusedTextureFragmentModule).Get(),
filteringBGL.Get());
// Textures used with textureLoad default to non-filtering
EXPECT_EQ(BGLFromModules(emptyVertexModule, textureLoadFragmentModule).Get(),
nonFilteringBGL.Get());
EXPECT_NE(BGLFromModules(emptyVertexModule, textureLoadFragmentModule).Get(),
filteringBGL.Get());
// Textures used with textureLoad on both stages default to non-filtering
EXPECT_EQ(BGLFromModules(textureLoadVertexModule, textureLoadFragmentModule).Get(),
nonFilteringBGL.Get());
EXPECT_NE(BGLFromModules(textureLoadVertexModule, textureLoadFragmentModule).Get(),
filteringBGL.Get());
// Textures used with textureSample default to filtering
EXPECT_NE(BGLFromModules(emptyVertexModule, textureSampleFragmentModule).Get(),
nonFilteringBGL.Get());
EXPECT_EQ(BGLFromModules(emptyVertexModule, textureSampleFragmentModule).Get(),
filteringBGL.Get());
EXPECT_NE(BGLFromModules(textureSampleVertexModule, unusedTextureFragmentModule).Get(),
nonFilteringBGL.Get());
EXPECT_EQ(BGLFromModules(textureSampleVertexModule, unusedTextureFragmentModule).Get(),
filteringBGL.Get());
// Textures used with both textureLoad and textureSample default to filtering
EXPECT_NE(BGLFromModules(textureLoadVertexModule, textureSampleFragmentModule).Get(),
nonFilteringBGL.Get());
EXPECT_EQ(BGLFromModules(textureLoadVertexModule, textureSampleFragmentModule).Get(),
filteringBGL.Get());
EXPECT_NE(BGLFromModules(textureSampleVertexModule, textureLoadFragmentModule).Get(),
nonFilteringBGL.Get());
EXPECT_EQ(BGLFromModules(textureSampleVertexModule, textureLoadFragmentModule).Get(),
filteringBGL.Get());
}
// Test GetBindGroupLayout works with a compute pipeline
TEST_F(GetBindGroupLayoutTests, ComputePipeline) {
// This test works assuming Dawn Native's object deduplication.
@ -240,7 +356,7 @@ TEST_F(GetBindGroupLayoutTests, BindingType) {
binding.buffer.type = wgpu::BufferBindingType::Undefined;
binding.buffer.minBindingSize = 0;
{
binding.texture.sampleType = wgpu::TextureSampleType::Float;
binding.texture.sampleType = wgpu::TextureSampleType::UnfilterableFloat;
wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"(
[[group(0), binding(0)]] var myTexture : texture_2d<f32>;
@ -311,7 +427,7 @@ TEST_F(GetBindGroupLayoutTests, ViewDimension) {
wgpu::BindGroupLayoutEntry binding = {};
binding.binding = 0;
binding.visibility = wgpu::ShaderStage::Fragment;
binding.texture.sampleType = wgpu::TextureSampleType::Float;
binding.texture.sampleType = wgpu::TextureSampleType::UnfilterableFloat;
wgpu::BindGroupLayoutDescriptor desc = {};
desc.entryCount = 1;
@ -400,7 +516,7 @@ TEST_F(GetBindGroupLayoutTests, TextureComponentType) {
desc.entries = &binding;
{
binding.texture.sampleType = wgpu::TextureSampleType::Float;
binding.texture.sampleType = wgpu::TextureSampleType::UnfilterableFloat;
wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"(
[[group(0), binding(0)]] var myTexture : texture_2d<f32>;