blob: c6480e6b9f2a4b2c9ed06cb7afb534418ce01418 [file] [log] [blame]
// Copyright 2026 The Dawn & Tint Authors
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "dawn/native/ResourceTableDefaultResources.h"
#include "dawn/common/ityp_array.h"
#include "dawn/native/Device.h"
#include "dawn/native/ResourceTable.h"
#include "dawn/native/Texture.h"
#include "tint/tint.h"
namespace dawn::native {
namespace {
constexpr auto kDefaults = std::array{
tint::ResourceType::kTexture1d_f32_filterable,
tint::ResourceType::kTexture2d_f32_filterable,
tint::ResourceType::kTexture2dArray_f32_filterable,
tint::ResourceType::kTextureCube_f32_filterable,
tint::ResourceType::kTextureCubeArray_f32_filterable,
tint::ResourceType::kTexture3d_f32_filterable,
tint::ResourceType::kTexture1d_f32_unfilterable,
tint::ResourceType::kTexture2d_f32_unfilterable,
tint::ResourceType::kTexture2dArray_f32_unfilterable,
tint::ResourceType::kTextureCube_f32_unfilterable,
tint::ResourceType::kTextureCubeArray_f32_unfilterable,
tint::ResourceType::kTexture3d_f32_unfilterable,
tint::ResourceType::kTexture1d_u32,
tint::ResourceType::kTexture2d_u32,
tint::ResourceType::kTexture2dArray_u32,
tint::ResourceType::kTextureCube_u32,
tint::ResourceType::kTextureCubeArray_u32,
tint::ResourceType::kTexture3d_u32,
tint::ResourceType::kTexture1d_i32,
tint::ResourceType::kTexture2d_i32,
tint::ResourceType::kTexture2dArray_i32,
tint::ResourceType::kTextureCube_i32,
tint::ResourceType::kTextureCubeArray_i32,
tint::ResourceType::kTexture3d_i32,
tint::ResourceType::kTextureMultisampled2d_f32,
tint::ResourceType::kTextureMultisampled2d_u32,
tint::ResourceType::kTextureMultisampled2d_i32,
tint::ResourceType::kTextureDepth2d,
tint::ResourceType::kTextureDepth2dArray,
tint::ResourceType::kTextureDepthCube,
tint::ResourceType::kTextureDepthCubeArray,
tint::ResourceType::kTextureDepthMultisampled2d,
tint::ResourceType::kSampler_filtering,
tint::ResourceType::kSampler_non_filtering,
tint::ResourceType::kSampler_comparison,
};
// Mapping of tint::ResourceType to its index in kDefaults, computed at compile time.
constexpr auto kIndexOfDefault = [] {
ityp::array<tint::ResourceType, uint32_t, kDefaults.size()> indices{};
for (uint32_t i = 0; i < kDefaults.size(); ++i) {
indices[tint::ResourceType(i)] = uint32_t(kDefaults[i]);
}
return indices;
}();
// This helper function is used in ASSERTs to check that the default resources are compatible with
// the typeIds that they will be used as defaults for.
[[maybe_unused]] bool AreTypeIDCompatible(tint::ResourceType resourceTypeId,
tint::ResourceType slotTypeId) {
if (resourceTypeId == slotTypeId) {
return true;
}
// Cases where conversions are allowed:
switch (slotTypeId) {
case tint::ResourceType::kTexture1d_f32_unfilterable:
return resourceTypeId == tint::ResourceType::kTexture1d_f32_filterable;
case tint::ResourceType::kTexture2d_f32_unfilterable:
return resourceTypeId == tint::ResourceType::kTexture2d_f32_filterable ||
resourceTypeId == tint::ResourceType::kTextureDepth2d;
case tint::ResourceType::kTexture2dArray_f32_unfilterable:
return resourceTypeId == tint::ResourceType::kTexture2dArray_f32_filterable ||
resourceTypeId == tint::ResourceType::kTextureDepth2dArray;
case tint::ResourceType::kTextureCube_f32_unfilterable:
return resourceTypeId == tint::ResourceType::kTextureCube_f32_filterable ||
resourceTypeId == tint::ResourceType::kTextureDepthCube;
case tint::ResourceType::kTextureCubeArray_f32_unfilterable:
return resourceTypeId == tint::ResourceType::kTextureCubeArray_f32_filterable ||
resourceTypeId == tint::ResourceType::kTextureDepthCubeArray;
case tint::ResourceType::kTexture3d_f32_unfilterable:
return resourceTypeId == tint::ResourceType::kTexture3d_f32_filterable;
case tint::ResourceType::kSampler_filtering:
return resourceTypeId == tint::ResourceType::kSampler_non_filtering;
default:
return false;
}
}
} // namespace
// static
ityp::span<ResourceTableSlot, const tint::ResourceType> ResourceTableDefaultResources::GetOrder() {
return {kDefaults.data(), ResourceTableSlot(uint32_t(kDefaults.size()))};
}
// static
ResourceTableSlot ResourceTableDefaultResources::GetCount() {
return GetOrder().size();
}
// static
ResourceTableSlot ResourceTableDefaultResources::IndexOf(tint::ResourceType resourceType) {
return ResourceTableSlot{kIndexOfDefault[resourceType]};
}
ResultOrError<ityp::span<ResourceTableSlot, ResourceTableDefaultResources::Resource>>
ResourceTableDefaultResources::GetOrCreate(DeviceBase* device) {
if (!mDefaultResources.empty()) {
return {{mDefaultResources.data(), mDefaultResources.size()}};
}
// Each resource is added in the order specified by GetOrder()
auto AddDefaultTextureView = [&](TextureBase* texture, const TextureViewDescriptor* viewDesc =
nullptr) -> MaybeError {
DAWN_TRY(texture->Pin(wgpu::TextureUsage::TextureBinding));
Ref<TextureViewBase> view;
DAWN_TRY_ASSIGN(view, device->CreateTextureView(texture, viewDesc));
// Check that the resource we will have will match the order of default textures that we
// will give to the shader compilation.
DAWN_ASSERT(AreTypeIDCompatible(ResourceTableBase::ComputeTypeId(view),
GetOrder()[mDefaultResources.size()]));
mDefaultResources.push_back(view);
return {};
};
auto AddDefaultSampler = [&](Ref<SamplerBase> sampler) {
// Check that the resource we will have will match the order of default textures that we
// will give to the shader compilation.
DAWN_ASSERT(AreTypeIDCompatible(ResourceTableBase::ComputeTypeId(sampler),
GetOrder()[mDefaultResources.size()]));
mDefaultResources.push_back(sampler);
};
// Create the color format single-sampled views.
for (auto format :
{wgpu::TextureFormat::R8Unorm, wgpu::TextureFormat::R8Uint, wgpu::TextureFormat::R8Sint}) {
// Create the necessary 1/2/3D textures.
TextureDescriptor tDesc{
.label = "default SampledTexture resource",
.usage = wgpu::TextureUsage::TextureBinding,
.size = {1},
.format = format,
};
tDesc.size = {1};
tDesc.dimension = wgpu::TextureDimension::e1D;
Ref<TextureBase> t1D;
DAWN_TRY_ASSIGN(t1D, device->CreateTexture(&tDesc));
tDesc.size = {1, 1, 6};
tDesc.dimension = wgpu::TextureDimension::e2D;
Ref<TextureBase> t2D;
DAWN_TRY_ASSIGN(t2D, device->CreateTexture(&tDesc));
tDesc.size = {1, 1, 1};
tDesc.dimension = wgpu::TextureDimension::e3D;
Ref<TextureBase> t3D;
DAWN_TRY_ASSIGN(t3D, device->CreateTexture(&tDesc));
// There are two different sets of default resources for R8Unorm because we first add all
// the filterable f32 sample types, then all the unfilterable f32 sample types.
uint32_t timesToAdd = format == wgpu::TextureFormat::R8Unorm ? 2 : 1;
for (uint32_t i = 0; i < timesToAdd; i++) {
// Create all the default binding view, reusing the 2D texture between
// 2D/2DArray/Cube/CubeArray.
DAWN_TRY(AddDefaultTextureView(t1D.Get()));
TextureViewDescriptor vDesc{
.label = "default SampledTexture resource",
};
vDesc.arrayLayerCount = 1;
vDesc.dimension = wgpu::TextureViewDimension::e2D;
DAWN_TRY(AddDefaultTextureView(t2D.Get(), &vDesc));
vDesc.dimension = wgpu::TextureViewDimension::e2DArray;
DAWN_TRY(AddDefaultTextureView(t2D.Get(), &vDesc));
vDesc.arrayLayerCount = 6;
vDesc.dimension = wgpu::TextureViewDimension::Cube;
DAWN_TRY(AddDefaultTextureView(t2D.Get(), &vDesc));
vDesc.dimension = wgpu::TextureViewDimension::CubeArray;
DAWN_TRY(AddDefaultTextureView(t2D.Get(), &vDesc));
DAWN_TRY(AddDefaultTextureView(t3D.Get()));
}
}
// Create the color format multi-sampled views.
for (auto format :
{wgpu::TextureFormat::R8Unorm, wgpu::TextureFormat::R8Uint, wgpu::TextureFormat::R8Sint}) {
TextureDescriptor tDesc{
.label = "default SampledTexture resource",
.usage = wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment,
.dimension = wgpu::TextureDimension::e2D,
.size = {1, 1},
.format = format,
.sampleCount = 4,
};
Ref<TextureBase> t;
DAWN_TRY_ASSIGN(t, device->CreateTexture(&tDesc));
DAWN_TRY(AddDefaultTextureView(t.Get()));
}
// Create the single-sampled depth texture default resource.
{
TextureDescriptor tDesc{
.label = "default SampledTexture resource",
.usage = wgpu::TextureUsage::TextureBinding,
.dimension = wgpu::TextureDimension::e2D,
.size = {1, 1, 6},
.format = wgpu::TextureFormat::Depth16Unorm,
};
Ref<TextureBase> t;
DAWN_TRY_ASSIGN(t, device->CreateTexture(&tDesc));
TextureViewDescriptor vDesc{
.label = "default SampledTexture resource",
};
vDesc.arrayLayerCount = 1;
vDesc.dimension = wgpu::TextureViewDimension::e2D;
DAWN_TRY(AddDefaultTextureView(t.Get(), &vDesc));
vDesc.dimension = wgpu::TextureViewDimension::e2DArray;
DAWN_TRY(AddDefaultTextureView(t.Get(), &vDesc));
vDesc.arrayLayerCount = 6;
vDesc.dimension = wgpu::TextureViewDimension::Cube;
DAWN_TRY(AddDefaultTextureView(t.Get(), &vDesc));
vDesc.dimension = wgpu::TextureViewDimension::CubeArray;
DAWN_TRY(AddDefaultTextureView(t.Get(), &vDesc));
}
// Create the multi-sampled depth texture default resource.
{
TextureDescriptor tDesc{
.label = "default SampledTexture resource",
.usage = wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment,
.dimension = wgpu::TextureDimension::e2D,
.size = {1, 1},
.format = wgpu::TextureFormat::Depth16Unorm,
.sampleCount = 4,
};
Ref<TextureBase> t;
DAWN_TRY_ASSIGN(t, device->CreateTexture(&tDesc));
DAWN_TRY(AddDefaultTextureView(t.Get()));
}
// Create the samplers.
{
// Filtering and non-filtering both use a non-filtering sampler
{
SamplerDescriptor sDesc{
.label = "default Sampler resource",
.magFilter = wgpu::FilterMode::Nearest,
.minFilter = wgpu::FilterMode::Nearest,
.mipmapFilter = wgpu::MipmapFilterMode::Nearest,
};
Ref<SamplerBase> s;
DAWN_TRY_ASSIGN(s, device->CreateSampler(&sDesc));
AddDefaultSampler(s); // Filtering
AddDefaultSampler(s); // Non-filtering
}
// Comparison
{
SamplerDescriptor sDesc{
.label = "default Sampler resource",
.compare = wgpu::CompareFunction::Always,
};
Ref<SamplerBase> s;
DAWN_TRY_ASSIGN(s, device->CreateSampler(&sDesc));
AddDefaultSampler(s);
}
}
return {{mDefaultResources.data(), mDefaultResources.size()}};
}
} // namespace dawn::native