1115 lines
51 KiB
C++
1115 lines
51 KiB
C++
// Copyright 2017 The Dawn Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include "dawn_native/ShaderModule.h"
|
|
|
|
#include "common/HashUtils.h"
|
|
#include "dawn_native/BindGroupLayout.h"
|
|
#include "dawn_native/Device.h"
|
|
#include "dawn_native/Pipeline.h"
|
|
#include "dawn_native/PipelineLayout.h"
|
|
|
|
#include <spirv-tools/libspirv.hpp>
|
|
#include <spirv_cross.hpp>
|
|
|
|
#ifdef DAWN_ENABLE_WGSL
|
|
// Tint include must be after spirv_cross.hpp, because spirv-cross has its own
|
|
// version of spirv_headers.
|
|
// clang-format off
|
|
#include <tint/tint.h>
|
|
// clang-format on
|
|
#endif // DAWN_ENABLE_WGSL
|
|
|
|
#include <sstream>
|
|
|
|
namespace dawn_native {
|
|
|
|
namespace {
|
|
Format::Type SpirvCrossBaseTypeToFormatType(spirv_cross::SPIRType::BaseType spirvBaseType) {
|
|
switch (spirvBaseType) {
|
|
case spirv_cross::SPIRType::Float:
|
|
return Format::Float;
|
|
case spirv_cross::SPIRType::Int:
|
|
return Format::Sint;
|
|
case spirv_cross::SPIRType::UInt:
|
|
return Format::Uint;
|
|
default:
|
|
UNREACHABLE();
|
|
return Format::Other;
|
|
}
|
|
}
|
|
|
|
wgpu::TextureViewDimension SpirvDimToTextureViewDimension(spv::Dim dim, bool arrayed) {
|
|
switch (dim) {
|
|
case spv::Dim::Dim1D:
|
|
return wgpu::TextureViewDimension::e1D;
|
|
case spv::Dim::Dim2D:
|
|
if (arrayed) {
|
|
return wgpu::TextureViewDimension::e2DArray;
|
|
} else {
|
|
return wgpu::TextureViewDimension::e2D;
|
|
}
|
|
case spv::Dim::Dim3D:
|
|
return wgpu::TextureViewDimension::e3D;
|
|
case spv::Dim::DimCube:
|
|
if (arrayed) {
|
|
return wgpu::TextureViewDimension::CubeArray;
|
|
} else {
|
|
return wgpu::TextureViewDimension::Cube;
|
|
}
|
|
default:
|
|
UNREACHABLE();
|
|
return wgpu::TextureViewDimension::Undefined;
|
|
}
|
|
}
|
|
|
|
wgpu::TextureViewDimension ToWGPUTextureViewDimension(
|
|
shaderc_spvc_texture_view_dimension dim) {
|
|
switch (dim) {
|
|
case shaderc_spvc_texture_view_dimension_undefined:
|
|
return wgpu::TextureViewDimension::Undefined;
|
|
case shaderc_spvc_texture_view_dimension_e1D:
|
|
return wgpu::TextureViewDimension::e1D;
|
|
case shaderc_spvc_texture_view_dimension_e2D:
|
|
return wgpu::TextureViewDimension::e2D;
|
|
case shaderc_spvc_texture_view_dimension_e2D_array:
|
|
return wgpu::TextureViewDimension::e2DArray;
|
|
case shaderc_spvc_texture_view_dimension_cube:
|
|
return wgpu::TextureViewDimension::Cube;
|
|
case shaderc_spvc_texture_view_dimension_cube_array:
|
|
return wgpu::TextureViewDimension::CubeArray;
|
|
case shaderc_spvc_texture_view_dimension_e3D:
|
|
return wgpu::TextureViewDimension::e3D;
|
|
}
|
|
UNREACHABLE();
|
|
}
|
|
|
|
Format::Type ToDawnFormatType(shaderc_spvc_texture_format_type type) {
|
|
switch (type) {
|
|
case shaderc_spvc_texture_format_type_float:
|
|
return Format::Type::Float;
|
|
case shaderc_spvc_texture_format_type_sint:
|
|
return Format::Type::Sint;
|
|
case shaderc_spvc_texture_format_type_uint:
|
|
return Format::Type::Uint;
|
|
case shaderc_spvc_texture_format_type_other:
|
|
return Format::Type::Other;
|
|
}
|
|
UNREACHABLE();
|
|
}
|
|
|
|
wgpu::BindingType ToWGPUBindingType(shaderc_spvc_binding_type type) {
|
|
switch (type) {
|
|
case shaderc_spvc_binding_type_uniform_buffer:
|
|
return wgpu::BindingType::UniformBuffer;
|
|
case shaderc_spvc_binding_type_storage_buffer:
|
|
return wgpu::BindingType::StorageBuffer;
|
|
case shaderc_spvc_binding_type_readonly_storage_buffer:
|
|
return wgpu::BindingType::ReadonlyStorageBuffer;
|
|
case shaderc_spvc_binding_type_sampler:
|
|
return wgpu::BindingType::Sampler;
|
|
case shaderc_spvc_binding_type_comparison_sampler:
|
|
return wgpu::BindingType::ComparisonSampler;
|
|
case shaderc_spvc_binding_type_sampled_texture:
|
|
return wgpu::BindingType::SampledTexture;
|
|
case shaderc_spvc_binding_type_readonly_storage_texture:
|
|
return wgpu::BindingType::ReadonlyStorageTexture;
|
|
case shaderc_spvc_binding_type_writeonly_storage_texture:
|
|
return wgpu::BindingType::WriteonlyStorageTexture;
|
|
case shaderc_spvc_binding_type_storage_texture:
|
|
return wgpu::BindingType::StorageTexture;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
SingleShaderStage ToSingleShaderStage(shaderc_spvc_execution_model execution_model) {
|
|
switch (execution_model) {
|
|
case shaderc_spvc_execution_model_vertex:
|
|
return SingleShaderStage::Vertex;
|
|
case shaderc_spvc_execution_model_fragment:
|
|
return SingleShaderStage::Fragment;
|
|
case shaderc_spvc_execution_model_glcompute:
|
|
return SingleShaderStage::Compute;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
wgpu::TextureFormat ToWGPUTextureFormat(spv::ImageFormat format) {
|
|
switch (format) {
|
|
case spv::ImageFormatR8:
|
|
return wgpu::TextureFormat::R8Unorm;
|
|
case spv::ImageFormatR8Snorm:
|
|
return wgpu::TextureFormat::R8Snorm;
|
|
case spv::ImageFormatR8ui:
|
|
return wgpu::TextureFormat::R8Uint;
|
|
case spv::ImageFormatR8i:
|
|
return wgpu::TextureFormat::R8Sint;
|
|
case spv::ImageFormatR16ui:
|
|
return wgpu::TextureFormat::R16Uint;
|
|
case spv::ImageFormatR16i:
|
|
return wgpu::TextureFormat::R16Sint;
|
|
case spv::ImageFormatR16f:
|
|
return wgpu::TextureFormat::R16Float;
|
|
case spv::ImageFormatRg8:
|
|
return wgpu::TextureFormat::RG8Unorm;
|
|
case spv::ImageFormatRg8Snorm:
|
|
return wgpu::TextureFormat::RG8Snorm;
|
|
case spv::ImageFormatRg8ui:
|
|
return wgpu::TextureFormat::RG8Uint;
|
|
case spv::ImageFormatRg8i:
|
|
return wgpu::TextureFormat::RG8Sint;
|
|
case spv::ImageFormatR32f:
|
|
return wgpu::TextureFormat::R32Float;
|
|
case spv::ImageFormatR32ui:
|
|
return wgpu::TextureFormat::R32Uint;
|
|
case spv::ImageFormatR32i:
|
|
return wgpu::TextureFormat::R32Sint;
|
|
case spv::ImageFormatRg16ui:
|
|
return wgpu::TextureFormat::RG16Uint;
|
|
case spv::ImageFormatRg16i:
|
|
return wgpu::TextureFormat::RG16Sint;
|
|
case spv::ImageFormatRg16f:
|
|
return wgpu::TextureFormat::RG16Float;
|
|
case spv::ImageFormatRgba8:
|
|
return wgpu::TextureFormat::RGBA8Unorm;
|
|
case spv::ImageFormatRgba8Snorm:
|
|
return wgpu::TextureFormat::RGBA8Snorm;
|
|
case spv::ImageFormatRgba8ui:
|
|
return wgpu::TextureFormat::RGBA8Uint;
|
|
case spv::ImageFormatRgba8i:
|
|
return wgpu::TextureFormat::RGBA8Sint;
|
|
case spv::ImageFormatRgb10A2:
|
|
return wgpu::TextureFormat::RGB10A2Unorm;
|
|
case spv::ImageFormatR11fG11fB10f:
|
|
return wgpu::TextureFormat::RG11B10Float;
|
|
case spv::ImageFormatRg32f:
|
|
return wgpu::TextureFormat::RG32Float;
|
|
case spv::ImageFormatRg32ui:
|
|
return wgpu::TextureFormat::RG32Uint;
|
|
case spv::ImageFormatRg32i:
|
|
return wgpu::TextureFormat::RG32Sint;
|
|
case spv::ImageFormatRgba16ui:
|
|
return wgpu::TextureFormat::RGBA16Uint;
|
|
case spv::ImageFormatRgba16i:
|
|
return wgpu::TextureFormat::RGBA16Sint;
|
|
case spv::ImageFormatRgba16f:
|
|
return wgpu::TextureFormat::RGBA16Float;
|
|
case spv::ImageFormatRgba32f:
|
|
return wgpu::TextureFormat::RGBA32Float;
|
|
case spv::ImageFormatRgba32ui:
|
|
return wgpu::TextureFormat::RGBA32Uint;
|
|
case spv::ImageFormatRgba32i:
|
|
return wgpu::TextureFormat::RGBA32Sint;
|
|
default:
|
|
return wgpu::TextureFormat::Undefined;
|
|
}
|
|
}
|
|
|
|
wgpu::TextureFormat ToWGPUTextureFormat(shaderc_spvc_storage_texture_format format) {
|
|
switch (format) {
|
|
case shaderc_spvc_storage_texture_format_r8unorm:
|
|
return wgpu::TextureFormat::R8Unorm;
|
|
case shaderc_spvc_storage_texture_format_r8snorm:
|
|
return wgpu::TextureFormat::R8Snorm;
|
|
case shaderc_spvc_storage_texture_format_r8uint:
|
|
return wgpu::TextureFormat::R8Uint;
|
|
case shaderc_spvc_storage_texture_format_r8sint:
|
|
return wgpu::TextureFormat::R8Sint;
|
|
case shaderc_spvc_storage_texture_format_r16uint:
|
|
return wgpu::TextureFormat::R16Uint;
|
|
case shaderc_spvc_storage_texture_format_r16sint:
|
|
return wgpu::TextureFormat::R16Sint;
|
|
case shaderc_spvc_storage_texture_format_r16float:
|
|
return wgpu::TextureFormat::R16Float;
|
|
case shaderc_spvc_storage_texture_format_rg8unorm:
|
|
return wgpu::TextureFormat::RG8Unorm;
|
|
case shaderc_spvc_storage_texture_format_rg8snorm:
|
|
return wgpu::TextureFormat::RG8Snorm;
|
|
case shaderc_spvc_storage_texture_format_rg8uint:
|
|
return wgpu::TextureFormat::RG8Uint;
|
|
case shaderc_spvc_storage_texture_format_rg8sint:
|
|
return wgpu::TextureFormat::RG8Sint;
|
|
case shaderc_spvc_storage_texture_format_r32float:
|
|
return wgpu::TextureFormat::R32Float;
|
|
case shaderc_spvc_storage_texture_format_r32uint:
|
|
return wgpu::TextureFormat::R32Uint;
|
|
case shaderc_spvc_storage_texture_format_r32sint:
|
|
return wgpu::TextureFormat::R32Sint;
|
|
case shaderc_spvc_storage_texture_format_rg16uint:
|
|
return wgpu::TextureFormat::RG16Uint;
|
|
case shaderc_spvc_storage_texture_format_rg16sint:
|
|
return wgpu::TextureFormat::RG16Sint;
|
|
case shaderc_spvc_storage_texture_format_rg16float:
|
|
return wgpu::TextureFormat::RG16Float;
|
|
case shaderc_spvc_storage_texture_format_rgba8unorm:
|
|
return wgpu::TextureFormat::RGBA8Unorm;
|
|
case shaderc_spvc_storage_texture_format_rgba8snorm:
|
|
return wgpu::TextureFormat::RGBA8Snorm;
|
|
case shaderc_spvc_storage_texture_format_rgba8uint:
|
|
return wgpu::TextureFormat::RGBA8Uint;
|
|
case shaderc_spvc_storage_texture_format_rgba8sint:
|
|
return wgpu::TextureFormat::RGBA8Sint;
|
|
case shaderc_spvc_storage_texture_format_rgb10a2unorm:
|
|
return wgpu::TextureFormat::RGB10A2Unorm;
|
|
case shaderc_spvc_storage_texture_format_rg11b10float:
|
|
return wgpu::TextureFormat::RG11B10Float;
|
|
case shaderc_spvc_storage_texture_format_rg32float:
|
|
return wgpu::TextureFormat::RG32Float;
|
|
case shaderc_spvc_storage_texture_format_rg32uint:
|
|
return wgpu::TextureFormat::RG32Uint;
|
|
case shaderc_spvc_storage_texture_format_rg32sint:
|
|
return wgpu::TextureFormat::RG32Sint;
|
|
case shaderc_spvc_storage_texture_format_rgba16uint:
|
|
return wgpu::TextureFormat::RGBA16Uint;
|
|
case shaderc_spvc_storage_texture_format_rgba16sint:
|
|
return wgpu::TextureFormat::RGBA16Sint;
|
|
case shaderc_spvc_storage_texture_format_rgba16float:
|
|
return wgpu::TextureFormat::RGBA16Float;
|
|
case shaderc_spvc_storage_texture_format_rgba32float:
|
|
return wgpu::TextureFormat::RGBA32Float;
|
|
case shaderc_spvc_storage_texture_format_rgba32uint:
|
|
return wgpu::TextureFormat::RGBA32Uint;
|
|
case shaderc_spvc_storage_texture_format_rgba32sint:
|
|
return wgpu::TextureFormat::RGBA32Sint;
|
|
default:
|
|
return wgpu::TextureFormat::Undefined;
|
|
}
|
|
}
|
|
|
|
std::string GetShaderDeclarationString(BindGroupIndex group, BindingNumber binding) {
|
|
std::ostringstream ostream;
|
|
ostream << "the shader module declaration at set " << static_cast<uint32_t>(group)
|
|
<< " binding " << static_cast<uint32_t>(binding);
|
|
return ostream.str();
|
|
}
|
|
} // anonymous namespace
|
|
|
|
MaybeError ValidateSpirv(DeviceBase*, const uint32_t* code, uint32_t codeSize) {
|
|
spvtools::SpirvTools spirvTools(SPV_ENV_VULKAN_1_1);
|
|
|
|
std::ostringstream errorStream;
|
|
errorStream << "SPIRV Validation failure:" << std::endl;
|
|
|
|
spirvTools.SetMessageConsumer([&errorStream](spv_message_level_t level, const char*,
|
|
const spv_position_t& position,
|
|
const char* message) {
|
|
switch (level) {
|
|
case SPV_MSG_FATAL:
|
|
case SPV_MSG_INTERNAL_ERROR:
|
|
case SPV_MSG_ERROR:
|
|
errorStream << "error: line " << position.index << ": " << message << std::endl;
|
|
break;
|
|
case SPV_MSG_WARNING:
|
|
errorStream << "warning: line " << position.index << ": " << message
|
|
<< std::endl;
|
|
break;
|
|
case SPV_MSG_INFO:
|
|
errorStream << "info: line " << position.index << ": " << message << std::endl;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
});
|
|
|
|
if (!spirvTools.Validate(code, codeSize)) {
|
|
return DAWN_VALIDATION_ERROR(errorStream.str().c_str());
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
#ifdef DAWN_ENABLE_WGSL
|
|
MaybeError ValidateWGSL(const char* source) {
|
|
std::ostringstream errorStream;
|
|
errorStream << "Tint WGSL failure:" << std::endl;
|
|
|
|
tint::Context context;
|
|
tint::reader::wgsl::Parser parser(&context, source);
|
|
|
|
if (!parser.Parse()) {
|
|
errorStream << "Parser: " << parser.error() << std::endl;
|
|
return DAWN_VALIDATION_ERROR(errorStream.str().c_str());
|
|
}
|
|
|
|
tint::ast::Module module = parser.module();
|
|
if (!module.IsValid()) {
|
|
errorStream << "Invalid module generated..." << std::endl;
|
|
return DAWN_VALIDATION_ERROR(errorStream.str().c_str());
|
|
}
|
|
|
|
tint::TypeDeterminer type_determiner(&context, &module);
|
|
if (!type_determiner.Determine()) {
|
|
errorStream << "Type Determination: " << type_determiner.error();
|
|
return DAWN_VALIDATION_ERROR(errorStream.str().c_str());
|
|
}
|
|
|
|
tint::Validator validator;
|
|
if (!validator.Validate(module)) {
|
|
errorStream << "Validation: " << validator.error() << std::endl;
|
|
return DAWN_VALIDATION_ERROR(errorStream.str().c_str());
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
ResultOrError<std::vector<uint32_t>> ConvertWGSLToSPIRV(const char* source) {
|
|
std::ostringstream errorStream;
|
|
errorStream << "Tint WGSL->SPIR-V failure:" << std::endl;
|
|
|
|
tint::Context context;
|
|
tint::reader::wgsl::Parser parser(&context, source);
|
|
|
|
// TODO: This is a duplicate parse with ValidateWGSL, need to store
|
|
// state between calls to avoid this.
|
|
if (!parser.Parse()) {
|
|
errorStream << "Parser: " << parser.error() << std::endl;
|
|
return DAWN_VALIDATION_ERROR(errorStream.str().c_str());
|
|
}
|
|
|
|
tint::ast::Module module = parser.module();
|
|
if (!module.IsValid()) {
|
|
errorStream << "Invalid module generated..." << std::endl;
|
|
return DAWN_VALIDATION_ERROR(errorStream.str().c_str());
|
|
}
|
|
|
|
tint::TypeDeterminer type_determiner(&context, &module);
|
|
if (!type_determiner.Determine()) {
|
|
errorStream << "Type Determination: " << type_determiner.error();
|
|
return DAWN_VALIDATION_ERROR(errorStream.str().c_str());
|
|
}
|
|
|
|
tint::writer::spirv::Generator generator(std::move(module));
|
|
if (!generator.Generate()) {
|
|
errorStream << "Generator: " << generator.error() << std::endl;
|
|
return DAWN_VALIDATION_ERROR(errorStream.str().c_str());
|
|
}
|
|
|
|
std::vector<uint32_t> spirv = generator.result();
|
|
return std::move(spirv);
|
|
}
|
|
#endif // DAWN_ENABLE_WGSL
|
|
|
|
MaybeError ValidateShaderModuleDescriptor(DeviceBase* device,
|
|
const ShaderModuleDescriptor* descriptor) {
|
|
const ChainedStruct* chainedDescriptor = descriptor->nextInChain;
|
|
if (chainedDescriptor == nullptr) {
|
|
return DAWN_VALIDATION_ERROR("Shader module descriptor missing chained descriptor");
|
|
}
|
|
// For now only a single SPIRV or WGSL subdescriptor is allowed.
|
|
if (chainedDescriptor->nextInChain != nullptr) {
|
|
return DAWN_VALIDATION_ERROR(
|
|
"Shader module descriptor chained nextInChain must be nullptr");
|
|
}
|
|
|
|
switch (chainedDescriptor->sType) {
|
|
case wgpu::SType::ShaderModuleSPIRVDescriptor: {
|
|
const auto* spirvDesc =
|
|
static_cast<const ShaderModuleSPIRVDescriptor*>(chainedDescriptor);
|
|
DAWN_TRY(ValidateSpirv(device, spirvDesc->code, spirvDesc->codeSize));
|
|
break;
|
|
}
|
|
|
|
case wgpu::SType::ShaderModuleWGSLDescriptor: {
|
|
#ifdef DAWN_ENABLE_WGSL
|
|
const auto* wgslDesc =
|
|
static_cast<const ShaderModuleWGSLDescriptor*>(chainedDescriptor);
|
|
DAWN_TRY(ValidateWGSL(wgslDesc->source));
|
|
break;
|
|
#else
|
|
return DAWN_VALIDATION_ERROR("WGSL not supported (yet)");
|
|
#endif // DAWN_ENABLE_WGSL
|
|
}
|
|
default:
|
|
return DAWN_VALIDATION_ERROR("Unsupported sType");
|
|
}
|
|
|
|
return {};
|
|
} // namespace
|
|
|
|
// ShaderModuleBase
|
|
|
|
ShaderModuleBase::ShaderModuleBase(DeviceBase* device, const ShaderModuleDescriptor* descriptor)
|
|
: CachedObject(device), mType(Type::Undefined) {
|
|
ASSERT(descriptor->nextInChain != nullptr);
|
|
switch (descriptor->nextInChain->sType) {
|
|
case wgpu::SType::ShaderModuleSPIRVDescriptor: {
|
|
mType = Type::Spirv;
|
|
const auto* spirvDesc =
|
|
static_cast<const ShaderModuleSPIRVDescriptor*>(descriptor->nextInChain);
|
|
mSpirv.assign(spirvDesc->code, spirvDesc->code + spirvDesc->codeSize);
|
|
break;
|
|
}
|
|
case wgpu::SType::ShaderModuleWGSLDescriptor: {
|
|
mType = Type::Wgsl;
|
|
const auto* wgslDesc =
|
|
static_cast<const ShaderModuleWGSLDescriptor*>(descriptor->nextInChain);
|
|
mWgsl = std::string(wgslDesc->source);
|
|
break;
|
|
}
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
|
|
mFragmentOutputFormatBaseTypes.fill(Format::Other);
|
|
if (GetDevice()->IsToggleEnabled(Toggle::UseSpvcParser)) {
|
|
mSpvcContext.SetUseSpvcParser(true);
|
|
}
|
|
}
|
|
|
|
ShaderModuleBase::ShaderModuleBase(DeviceBase* device, ObjectBase::ErrorTag tag)
|
|
: CachedObject(device, tag), mType(Type::Undefined) {
|
|
}
|
|
|
|
ShaderModuleBase::~ShaderModuleBase() {
|
|
if (IsCachedReference()) {
|
|
GetDevice()->UncacheShaderModule(this);
|
|
}
|
|
}
|
|
|
|
// static
|
|
ShaderModuleBase* ShaderModuleBase::MakeError(DeviceBase* device) {
|
|
return new ShaderModuleBase(device, ObjectBase::kError);
|
|
}
|
|
|
|
MaybeError ShaderModuleBase::ExtractSpirvInfo(const spirv_cross::Compiler& compiler) {
|
|
ASSERT(!IsError());
|
|
if (GetDevice()->IsToggleEnabled(Toggle::UseSpvc)) {
|
|
DAWN_TRY(ExtractSpirvInfoWithSpvc());
|
|
} else {
|
|
DAWN_TRY(ExtractSpirvInfoWithSpirvCross(compiler));
|
|
}
|
|
return {};
|
|
}
|
|
|
|
MaybeError ShaderModuleBase::ExtractSpirvInfoWithSpvc() {
|
|
shaderc_spvc_execution_model execution_model;
|
|
DAWN_TRY(CheckSpvcSuccess(mSpvcContext.GetExecutionModel(&execution_model),
|
|
"Unable to get execution model for shader."));
|
|
mExecutionModel = ToSingleShaderStage(execution_model);
|
|
|
|
size_t push_constant_buffers_count;
|
|
DAWN_TRY(
|
|
CheckSpvcSuccess(mSpvcContext.GetPushConstantBufferCount(&push_constant_buffers_count),
|
|
"Unable to get push constant buffer count for shader."));
|
|
|
|
// TODO(rharrison): This should be handled by spirv-val pass in spvc,
|
|
// but need to confirm.
|
|
if (push_constant_buffers_count > 0) {
|
|
return DAWN_VALIDATION_ERROR("Push constants aren't supported.");
|
|
}
|
|
|
|
// Fill in bindingInfo with the SPIRV bindings
|
|
auto ExtractResourcesBinding =
|
|
[this](std::vector<shaderc_spvc_binding_info> bindings) -> MaybeError {
|
|
for (const auto& binding : bindings) {
|
|
BindGroupIndex bindGroupIndex(binding.set);
|
|
|
|
if (bindGroupIndex >= kMaxBindGroupsTyped) {
|
|
return DAWN_VALIDATION_ERROR("Bind group index over limits in the SPIRV");
|
|
}
|
|
|
|
const auto& it = mBindingInfo[bindGroupIndex].emplace(
|
|
BindingNumber(binding.binding), ShaderBindingInfo{});
|
|
if (!it.second) {
|
|
return DAWN_VALIDATION_ERROR("Shader has duplicate bindings");
|
|
}
|
|
|
|
ShaderBindingInfo* info = &it.first->second;
|
|
info->id = binding.id;
|
|
info->base_type_id = binding.base_type_id;
|
|
info->type = ToWGPUBindingType(binding.binding_type);
|
|
|
|
switch (info->type) {
|
|
case wgpu::BindingType::SampledTexture: {
|
|
info->multisampled = binding.multisampled;
|
|
info->viewDimension = ToWGPUTextureViewDimension(binding.texture_dimension);
|
|
info->textureComponentType =
|
|
ToDawnFormatType(binding.texture_component_type);
|
|
break;
|
|
}
|
|
case wgpu::BindingType::StorageTexture:
|
|
case wgpu::BindingType::ReadonlyStorageTexture:
|
|
case wgpu::BindingType::WriteonlyStorageTexture: {
|
|
wgpu::TextureFormat storageTextureFormat =
|
|
ToWGPUTextureFormat(binding.storage_texture_format);
|
|
if (storageTextureFormat == wgpu::TextureFormat::Undefined) {
|
|
return DAWN_VALIDATION_ERROR(
|
|
"Invalid image format declaration on storage image");
|
|
}
|
|
const Format& format =
|
|
GetDevice()->GetValidInternalFormat(storageTextureFormat);
|
|
if (!format.supportsStorageUsage) {
|
|
return DAWN_VALIDATION_ERROR(
|
|
"The storage texture format is not supported");
|
|
}
|
|
info->multisampled = binding.multisampled;
|
|
info->storageTextureFormat = storageTextureFormat;
|
|
info->viewDimension = ToWGPUTextureViewDimension(binding.texture_dimension);
|
|
break;
|
|
}
|
|
case wgpu::BindingType::UniformBuffer:
|
|
case wgpu::BindingType::StorageBuffer:
|
|
case wgpu::BindingType::ReadonlyStorageBuffer:
|
|
info->minBufferBindingSize = binding.minimum_buffer_size;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return {};
|
|
};
|
|
|
|
std::vector<shaderc_spvc_binding_info> resource_bindings;
|
|
DAWN_TRY(CheckSpvcSuccess(mSpvcContext.GetBindingInfo(
|
|
shaderc_spvc_shader_resource_uniform_buffers,
|
|
shaderc_spvc_binding_type_uniform_buffer, &resource_bindings),
|
|
"Unable to get binding info for uniform buffers from shader"));
|
|
DAWN_TRY(ExtractResourcesBinding(resource_bindings));
|
|
|
|
DAWN_TRY(CheckSpvcSuccess(
|
|
mSpvcContext.GetBindingInfo(shaderc_spvc_shader_resource_separate_images,
|
|
shaderc_spvc_binding_type_sampled_texture,
|
|
&resource_bindings),
|
|
"Unable to get binding info for sampled textures from shader"));
|
|
DAWN_TRY(ExtractResourcesBinding(resource_bindings));
|
|
|
|
DAWN_TRY(CheckSpvcSuccess(
|
|
mSpvcContext.GetBindingInfo(shaderc_spvc_shader_resource_separate_samplers,
|
|
shaderc_spvc_binding_type_sampler, &resource_bindings),
|
|
"Unable to get binding info for samples from shader"));
|
|
DAWN_TRY(ExtractResourcesBinding(resource_bindings));
|
|
|
|
DAWN_TRY(CheckSpvcSuccess(mSpvcContext.GetBindingInfo(
|
|
shaderc_spvc_shader_resource_storage_buffers,
|
|
shaderc_spvc_binding_type_storage_buffer, &resource_bindings),
|
|
"Unable to get binding info for storage buffers from shader"));
|
|
DAWN_TRY(ExtractResourcesBinding(resource_bindings));
|
|
|
|
DAWN_TRY(CheckSpvcSuccess(
|
|
mSpvcContext.GetBindingInfo(shaderc_spvc_shader_resource_storage_images,
|
|
shaderc_spvc_binding_type_storage_texture,
|
|
&resource_bindings),
|
|
"Unable to get binding info for storage textures from shader"));
|
|
DAWN_TRY(ExtractResourcesBinding(resource_bindings));
|
|
|
|
std::vector<shaderc_spvc_resource_location_info> input_stage_locations;
|
|
DAWN_TRY(CheckSpvcSuccess(mSpvcContext.GetInputStageLocationInfo(&input_stage_locations),
|
|
"Unable to get input stage location information from shader"));
|
|
|
|
for (const auto& input : input_stage_locations) {
|
|
if (mExecutionModel == SingleShaderStage::Vertex) {
|
|
if (input.location >= kMaxVertexAttributes) {
|
|
return DAWN_VALIDATION_ERROR("Attribute location over limits in the SPIRV");
|
|
}
|
|
mUsedVertexAttributes.set(input.location);
|
|
} else if (mExecutionModel == SingleShaderStage::Fragment) {
|
|
// Without a location qualifier on vertex inputs, spirv_cross::CompilerMSL gives
|
|
// them all the location 0, causing a compile error.
|
|
if (!input.has_location) {
|
|
return DAWN_VALIDATION_ERROR("Need location qualifier on fragment input");
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<shaderc_spvc_resource_location_info> output_stage_locations;
|
|
DAWN_TRY(CheckSpvcSuccess(mSpvcContext.GetOutputStageLocationInfo(&output_stage_locations),
|
|
"Unable to get output stage location information from shader"));
|
|
|
|
for (const auto& output : output_stage_locations) {
|
|
if (mExecutionModel == SingleShaderStage::Vertex) {
|
|
// Without a location qualifier on vertex outputs, spirv_cross::CompilerMSL
|
|
// gives them all the location 0, causing a compile error.
|
|
if (!output.has_location) {
|
|
return DAWN_VALIDATION_ERROR("Need location qualifier on vertex output");
|
|
}
|
|
} else if (mExecutionModel == SingleShaderStage::Fragment) {
|
|
if (output.location >= kMaxColorAttachments) {
|
|
return DAWN_VALIDATION_ERROR(
|
|
"Fragment output location over limits in the SPIRV");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mExecutionModel == SingleShaderStage::Fragment) {
|
|
std::vector<shaderc_spvc_resource_type_info> output_types;
|
|
DAWN_TRY(CheckSpvcSuccess(mSpvcContext.GetOutputStageTypeInfo(&output_types),
|
|
"Unable to get output stage type information from shader"));
|
|
|
|
for (const auto& output : output_types) {
|
|
if (output.type == shaderc_spvc_texture_format_type_other) {
|
|
return DAWN_VALIDATION_ERROR("Unexpected Fragment output type");
|
|
}
|
|
mFragmentOutputFormatBaseTypes[output.location] = ToDawnFormatType(output.type);
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
MaybeError ShaderModuleBase::ExtractSpirvInfoWithSpirvCross(
|
|
const spirv_cross::Compiler& compiler) {
|
|
// TODO(cwallez@chromium.org): make errors here creation errors
|
|
// currently errors here do not prevent the shadermodule from being used
|
|
const auto& resources = compiler.get_shader_resources();
|
|
|
|
switch (compiler.get_execution_model()) {
|
|
case spv::ExecutionModelVertex:
|
|
mExecutionModel = SingleShaderStage::Vertex;
|
|
break;
|
|
case spv::ExecutionModelFragment:
|
|
mExecutionModel = SingleShaderStage::Fragment;
|
|
break;
|
|
case spv::ExecutionModelGLCompute:
|
|
mExecutionModel = SingleShaderStage::Compute;
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
return DAWN_VALIDATION_ERROR("Unexpected shader execution model");
|
|
}
|
|
|
|
if (resources.push_constant_buffers.size() > 0) {
|
|
return DAWN_VALIDATION_ERROR("Push constants aren't supported.");
|
|
}
|
|
|
|
if (resources.sampled_images.size() > 0) {
|
|
return DAWN_VALIDATION_ERROR("Combined images and samplers aren't supported.");
|
|
}
|
|
|
|
// Fill in bindingInfo with the SPIRV bindings
|
|
auto ExtractResourcesBinding =
|
|
[this](const spirv_cross::SmallVector<spirv_cross::Resource>& resources,
|
|
const spirv_cross::Compiler& compiler,
|
|
wgpu::BindingType bindingType) -> MaybeError {
|
|
for (const auto& resource : resources) {
|
|
if (!compiler.get_decoration_bitset(resource.id).get(spv::DecorationBinding)) {
|
|
return DAWN_VALIDATION_ERROR("No Binding decoration set for resource");
|
|
}
|
|
|
|
if (!compiler.get_decoration_bitset(resource.id)
|
|
.get(spv::DecorationDescriptorSet)) {
|
|
return DAWN_VALIDATION_ERROR("No Descriptor Decoration set for resource");
|
|
}
|
|
|
|
BindingNumber bindingNumber(
|
|
compiler.get_decoration(resource.id, spv::DecorationBinding));
|
|
BindGroupIndex bindGroupIndex(
|
|
compiler.get_decoration(resource.id, spv::DecorationDescriptorSet));
|
|
|
|
if (bindGroupIndex >= kMaxBindGroupsTyped) {
|
|
return DAWN_VALIDATION_ERROR("Bind group index over limits in the SPIRV");
|
|
}
|
|
|
|
const auto& it =
|
|
mBindingInfo[bindGroupIndex].emplace(bindingNumber, ShaderBindingInfo{});
|
|
if (!it.second) {
|
|
return DAWN_VALIDATION_ERROR("Shader has duplicate bindings");
|
|
}
|
|
|
|
ShaderBindingInfo* info = &it.first->second;
|
|
info->id = resource.id;
|
|
info->base_type_id = resource.base_type_id;
|
|
|
|
if (bindingType == wgpu::BindingType::UniformBuffer ||
|
|
bindingType == wgpu::BindingType::StorageBuffer ||
|
|
bindingType == wgpu::BindingType::ReadonlyStorageBuffer) {
|
|
// Determine buffer size, with a minimum of 1 element in the runtime array
|
|
spirv_cross::SPIRType type = compiler.get_type(info->base_type_id);
|
|
info->minBufferBindingSize =
|
|
compiler.get_declared_struct_size_runtime_array(type, 1);
|
|
}
|
|
|
|
switch (bindingType) {
|
|
case wgpu::BindingType::SampledTexture: {
|
|
spirv_cross::SPIRType::ImageType imageType =
|
|
compiler.get_type(info->base_type_id).image;
|
|
spirv_cross::SPIRType::BaseType textureComponentType =
|
|
compiler.get_type(imageType.type).basetype;
|
|
|
|
info->multisampled = imageType.ms;
|
|
info->viewDimension =
|
|
SpirvDimToTextureViewDimension(imageType.dim, imageType.arrayed);
|
|
info->textureComponentType =
|
|
SpirvCrossBaseTypeToFormatType(textureComponentType);
|
|
info->type = bindingType;
|
|
break;
|
|
}
|
|
case wgpu::BindingType::StorageBuffer: {
|
|
// Differentiate between readonly storage bindings and writable ones
|
|
// based on the NonWritable decoration
|
|
spirv_cross::Bitset flags = compiler.get_buffer_block_flags(resource.id);
|
|
if (flags.get(spv::DecorationNonWritable)) {
|
|
info->type = wgpu::BindingType::ReadonlyStorageBuffer;
|
|
} else {
|
|
info->type = wgpu::BindingType::StorageBuffer;
|
|
}
|
|
break;
|
|
}
|
|
case wgpu::BindingType::StorageTexture: {
|
|
spirv_cross::Bitset flags = compiler.get_decoration_bitset(resource.id);
|
|
if (flags.get(spv::DecorationNonReadable)) {
|
|
info->type = wgpu::BindingType::WriteonlyStorageTexture;
|
|
} else if (flags.get(spv::DecorationNonWritable)) {
|
|
info->type = wgpu::BindingType::ReadonlyStorageTexture;
|
|
} else {
|
|
info->type = wgpu::BindingType::StorageTexture;
|
|
}
|
|
|
|
spirv_cross::SPIRType::ImageType imageType =
|
|
compiler.get_type(info->base_type_id).image;
|
|
wgpu::TextureFormat storageTextureFormat =
|
|
ToWGPUTextureFormat(imageType.format);
|
|
if (storageTextureFormat == wgpu::TextureFormat::Undefined) {
|
|
return DAWN_VALIDATION_ERROR(
|
|
"Invalid image format declaration on storage image");
|
|
}
|
|
const Format& format =
|
|
GetDevice()->GetValidInternalFormat(storageTextureFormat);
|
|
if (!format.supportsStorageUsage) {
|
|
return DAWN_VALIDATION_ERROR(
|
|
"The storage texture format is not supported");
|
|
}
|
|
info->multisampled = imageType.ms;
|
|
info->storageTextureFormat = storageTextureFormat;
|
|
info->viewDimension =
|
|
SpirvDimToTextureViewDimension(imageType.dim, imageType.arrayed);
|
|
break;
|
|
}
|
|
default:
|
|
info->type = bindingType;
|
|
}
|
|
}
|
|
return {};
|
|
};
|
|
|
|
DAWN_TRY(ExtractResourcesBinding(resources.uniform_buffers, compiler,
|
|
wgpu::BindingType::UniformBuffer));
|
|
DAWN_TRY(ExtractResourcesBinding(resources.separate_images, compiler,
|
|
wgpu::BindingType::SampledTexture));
|
|
DAWN_TRY(ExtractResourcesBinding(resources.separate_samplers, compiler,
|
|
wgpu::BindingType::Sampler));
|
|
DAWN_TRY(ExtractResourcesBinding(resources.storage_buffers, compiler,
|
|
wgpu::BindingType::StorageBuffer));
|
|
DAWN_TRY(ExtractResourcesBinding(resources.storage_images, compiler,
|
|
wgpu::BindingType::StorageTexture));
|
|
|
|
// Extract the vertex attributes
|
|
if (mExecutionModel == SingleShaderStage::Vertex) {
|
|
for (const auto& attrib : resources.stage_inputs) {
|
|
if (!(compiler.get_decoration_bitset(attrib.id).get(spv::DecorationLocation))) {
|
|
return DAWN_VALIDATION_ERROR(
|
|
"Unable to find Location decoration for Vertex input");
|
|
}
|
|
uint32_t location = compiler.get_decoration(attrib.id, spv::DecorationLocation);
|
|
|
|
if (location >= kMaxVertexAttributes) {
|
|
return DAWN_VALIDATION_ERROR("Attribute location over limits in the SPIRV");
|
|
}
|
|
|
|
mUsedVertexAttributes.set(location);
|
|
}
|
|
|
|
// Without a location qualifier on vertex outputs, spirv_cross::CompilerMSL gives
|
|
// them all the location 0, causing a compile error.
|
|
for (const auto& attrib : resources.stage_outputs) {
|
|
if (!compiler.get_decoration_bitset(attrib.id).get(spv::DecorationLocation)) {
|
|
return DAWN_VALIDATION_ERROR("Need location qualifier on vertex output");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mExecutionModel == SingleShaderStage::Fragment) {
|
|
// Without a location qualifier on vertex inputs, spirv_cross::CompilerMSL gives
|
|
// them all the location 0, causing a compile error.
|
|
for (const auto& attrib : resources.stage_inputs) {
|
|
if (!compiler.get_decoration_bitset(attrib.id).get(spv::DecorationLocation)) {
|
|
return DAWN_VALIDATION_ERROR("Need location qualifier on fragment input");
|
|
}
|
|
}
|
|
|
|
for (const auto& fragmentOutput : resources.stage_outputs) {
|
|
if (!compiler.get_decoration_bitset(fragmentOutput.id)
|
|
.get(spv::DecorationLocation)) {
|
|
return DAWN_VALIDATION_ERROR(
|
|
"Unable to find Location decoration for Fragment output");
|
|
}
|
|
uint32_t location =
|
|
compiler.get_decoration(fragmentOutput.id, spv::DecorationLocation);
|
|
if (location >= kMaxColorAttachments) {
|
|
return DAWN_VALIDATION_ERROR(
|
|
"Fragment output location over limits in the SPIRV");
|
|
}
|
|
|
|
spirv_cross::SPIRType::BaseType shaderFragmentOutputBaseType =
|
|
compiler.get_type(fragmentOutput.base_type_id).basetype;
|
|
Format::Type formatType =
|
|
SpirvCrossBaseTypeToFormatType(shaderFragmentOutputBaseType);
|
|
if (formatType == Format::Type::Other) {
|
|
return DAWN_VALIDATION_ERROR("Unexpected Fragment output type");
|
|
}
|
|
mFragmentOutputFormatBaseTypes[location] = formatType;
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
const ShaderModuleBase::ModuleBindingInfo& ShaderModuleBase::GetBindingInfo() const {
|
|
ASSERT(!IsError());
|
|
return mBindingInfo;
|
|
}
|
|
|
|
const std::bitset<kMaxVertexAttributes>& ShaderModuleBase::GetUsedVertexAttributes() const {
|
|
ASSERT(!IsError());
|
|
return mUsedVertexAttributes;
|
|
}
|
|
|
|
const ShaderModuleBase::FragmentOutputBaseTypes& ShaderModuleBase::GetFragmentOutputBaseTypes()
|
|
const {
|
|
ASSERT(!IsError());
|
|
return mFragmentOutputFormatBaseTypes;
|
|
}
|
|
|
|
SingleShaderStage ShaderModuleBase::GetExecutionModel() const {
|
|
ASSERT(!IsError());
|
|
return mExecutionModel;
|
|
}
|
|
|
|
RequiredBufferSizes ShaderModuleBase::ComputeRequiredBufferSizesForLayout(
|
|
const PipelineLayoutBase* layout) const {
|
|
RequiredBufferSizes bufferSizes;
|
|
for (BindGroupIndex group : IterateBitSet(layout->GetBindGroupLayoutsMask())) {
|
|
bufferSizes[group] =
|
|
GetBindGroupMinBufferSizes(mBindingInfo[group], layout->GetBindGroupLayout(group));
|
|
}
|
|
|
|
return bufferSizes;
|
|
}
|
|
|
|
std::vector<uint64_t> ShaderModuleBase::GetBindGroupMinBufferSizes(
|
|
const BindingInfoMap& shaderMap,
|
|
const BindGroupLayoutBase* layout) const {
|
|
std::vector<uint64_t> requiredBufferSizes(layout->GetUnverifiedBufferCount());
|
|
uint32_t packedIdx = 0;
|
|
|
|
for (BindingIndex bindingIndex{0}; bindingIndex < layout->GetBufferCount();
|
|
++bindingIndex) {
|
|
const BindingInfo& bindingInfo = layout->GetBindingInfo(bindingIndex);
|
|
if (bindingInfo.minBufferBindingSize != 0) {
|
|
// Skip bindings that have minimum buffer size set in the layout
|
|
continue;
|
|
}
|
|
|
|
ASSERT(packedIdx < requiredBufferSizes.size());
|
|
const auto& shaderInfo = shaderMap.find(bindingInfo.binding);
|
|
if (shaderInfo != shaderMap.end()) {
|
|
requiredBufferSizes[packedIdx] = shaderInfo->second.minBufferBindingSize;
|
|
} else {
|
|
// We have to include buffers if they are included in the bind group's
|
|
// packed vector. We don't actually need to check these at draw time, so
|
|
// if this is a problem in the future we can optimize it further.
|
|
requiredBufferSizes[packedIdx] = 0;
|
|
}
|
|
++packedIdx;
|
|
}
|
|
|
|
return requiredBufferSizes;
|
|
}
|
|
|
|
MaybeError ShaderModuleBase::ValidateCompatibilityWithPipelineLayout(
|
|
const PipelineLayoutBase* layout) const {
|
|
ASSERT(!IsError());
|
|
|
|
for (BindGroupIndex group : IterateBitSet(layout->GetBindGroupLayoutsMask())) {
|
|
DAWN_TRY(
|
|
ValidateCompatibilityWithBindGroupLayout(group, layout->GetBindGroupLayout(group)));
|
|
}
|
|
|
|
for (BindGroupIndex group : IterateBitSet(~layout->GetBindGroupLayoutsMask())) {
|
|
if (mBindingInfo[group].size() > 0) {
|
|
std::ostringstream ostream;
|
|
ostream << "No bind group layout entry matches the declaration set "
|
|
<< static_cast<uint32_t>(group) << " in the shader module";
|
|
return DAWN_VALIDATION_ERROR(ostream.str());
|
|
}
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
MaybeError ShaderModuleBase::ValidateCompatibilityWithBindGroupLayout(
|
|
BindGroupIndex group,
|
|
const BindGroupLayoutBase* layout) const {
|
|
ASSERT(!IsError());
|
|
|
|
const BindGroupLayoutBase::BindingMap& bindingMap = layout->GetBindingMap();
|
|
|
|
// Iterate over all bindings used by this group in the shader, and find the
|
|
// corresponding binding in the BindGroupLayout, if it exists.
|
|
for (const auto& it : mBindingInfo[group]) {
|
|
BindingNumber bindingNumber = it.first;
|
|
const ShaderBindingInfo& moduleInfo = it.second;
|
|
|
|
const auto& bindingIt = bindingMap.find(bindingNumber);
|
|
if (bindingIt == bindingMap.end()) {
|
|
return DAWN_VALIDATION_ERROR("Missing bind group layout entry for " +
|
|
GetShaderDeclarationString(group, bindingNumber));
|
|
}
|
|
BindingIndex bindingIndex(bindingIt->second);
|
|
|
|
const BindingInfo& bindingInfo = layout->GetBindingInfo(bindingIndex);
|
|
|
|
if (bindingInfo.type != moduleInfo.type) {
|
|
// Binding mismatch between shader and bind group is invalid. For example, a
|
|
// writable binding in the shader with a readonly storage buffer in the bind group
|
|
// layout is invalid. However, a readonly binding in the shader with a writable
|
|
// storage buffer in the bind group layout is valid.
|
|
bool validBindingConversion =
|
|
bindingInfo.type == wgpu::BindingType::StorageBuffer &&
|
|
moduleInfo.type == wgpu::BindingType::ReadonlyStorageBuffer;
|
|
|
|
// 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.
|
|
validBindingConversion |= (bindingInfo.type == wgpu::BindingType::Sampler &&
|
|
moduleInfo.type == wgpu::BindingType::ComparisonSampler);
|
|
validBindingConversion |=
|
|
(bindingInfo.type == wgpu::BindingType::ComparisonSampler &&
|
|
moduleInfo.type == wgpu::BindingType::Sampler);
|
|
|
|
if (!validBindingConversion) {
|
|
return DAWN_VALIDATION_ERROR(
|
|
"The binding type of the bind group layout entry conflicts " +
|
|
GetShaderDeclarationString(group, bindingNumber));
|
|
}
|
|
}
|
|
|
|
if ((bindingInfo.visibility & StageBit(mExecutionModel)) == 0) {
|
|
return DAWN_VALIDATION_ERROR("The bind group layout entry for " +
|
|
GetShaderDeclarationString(group, bindingNumber) +
|
|
" is not visible for the shader stage");
|
|
}
|
|
|
|
switch (bindingInfo.type) {
|
|
case wgpu::BindingType::SampledTexture: {
|
|
if (bindingInfo.textureComponentType != moduleInfo.textureComponentType) {
|
|
return DAWN_VALIDATION_ERROR(
|
|
"The textureComponentType of the bind group layout entry is different "
|
|
"from " +
|
|
GetShaderDeclarationString(group, bindingNumber));
|
|
}
|
|
|
|
if (bindingInfo.viewDimension != moduleInfo.viewDimension) {
|
|
return DAWN_VALIDATION_ERROR(
|
|
"The viewDimension of the bind group layout entry is different "
|
|
"from " +
|
|
GetShaderDeclarationString(group, bindingNumber));
|
|
}
|
|
break;
|
|
}
|
|
|
|
case wgpu::BindingType::ReadonlyStorageTexture:
|
|
case wgpu::BindingType::WriteonlyStorageTexture: {
|
|
ASSERT(bindingInfo.storageTextureFormat != wgpu::TextureFormat::Undefined);
|
|
ASSERT(moduleInfo.storageTextureFormat != wgpu::TextureFormat::Undefined);
|
|
if (bindingInfo.storageTextureFormat != moduleInfo.storageTextureFormat) {
|
|
return DAWN_VALIDATION_ERROR(
|
|
"The storageTextureFormat of the bind group layout entry is different "
|
|
"from " +
|
|
GetShaderDeclarationString(group, bindingNumber));
|
|
}
|
|
if (bindingInfo.viewDimension != moduleInfo.viewDimension) {
|
|
return DAWN_VALIDATION_ERROR(
|
|
"The viewDimension of the bind group layout entry is different "
|
|
"from " +
|
|
GetShaderDeclarationString(group, bindingNumber));
|
|
}
|
|
break;
|
|
}
|
|
|
|
case wgpu::BindingType::UniformBuffer:
|
|
case wgpu::BindingType::ReadonlyStorageBuffer:
|
|
case wgpu::BindingType::StorageBuffer: {
|
|
if (bindingInfo.minBufferBindingSize != 0 &&
|
|
moduleInfo.minBufferBindingSize > bindingInfo.minBufferBindingSize) {
|
|
return DAWN_VALIDATION_ERROR(
|
|
"The minimum buffer size of the bind group layout entry is smaller "
|
|
"than " +
|
|
GetShaderDeclarationString(group, bindingNumber));
|
|
}
|
|
break;
|
|
}
|
|
case wgpu::BindingType::Sampler:
|
|
case wgpu::BindingType::ComparisonSampler:
|
|
break;
|
|
|
|
case wgpu::BindingType::StorageTexture:
|
|
default:
|
|
UNREACHABLE();
|
|
return DAWN_VALIDATION_ERROR("Unsupported binding type");
|
|
}
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
size_t ShaderModuleBase::HashFunc::operator()(const ShaderModuleBase* module) const {
|
|
size_t hash = 0;
|
|
|
|
for (uint32_t word : module->mSpirv) {
|
|
HashCombine(&hash, word);
|
|
}
|
|
|
|
return hash;
|
|
}
|
|
|
|
bool ShaderModuleBase::EqualityFunc::operator()(const ShaderModuleBase* a,
|
|
const ShaderModuleBase* b) const {
|
|
return a->mSpirv == b->mSpirv;
|
|
}
|
|
|
|
MaybeError ShaderModuleBase::CheckSpvcSuccess(shaderc_spvc_status status,
|
|
const char* error_msg) {
|
|
if (status != shaderc_spvc_status_success) {
|
|
return DAWN_VALIDATION_ERROR(error_msg);
|
|
}
|
|
return {};
|
|
}
|
|
|
|
shaderc_spvc::Context* ShaderModuleBase::GetContext() {
|
|
return &mSpvcContext;
|
|
}
|
|
|
|
const std::vector<uint32_t>& ShaderModuleBase::GetSpirv() const {
|
|
return mSpirv;
|
|
}
|
|
|
|
shaderc_spvc::CompileOptions ShaderModuleBase::GetCompileOptions() const {
|
|
shaderc_spvc::CompileOptions options;
|
|
options.SetValidate(GetDevice()->IsValidationEnabled());
|
|
return options;
|
|
}
|
|
|
|
MaybeError ShaderModuleBase::InitializeBase() {
|
|
if (mType == Type::Wgsl) {
|
|
#ifdef DAWN_ENABLE_WGSL
|
|
DAWN_TRY_ASSIGN(mSpirv, ConvertWGSLToSPIRV(mWgsl.c_str()));
|
|
#else
|
|
return DAWN_VALIDATION_ERROR("WGSL not supported (yet)");
|
|
#endif // DAWN_ENABLE_WGSL
|
|
}
|
|
|
|
return {};
|
|
}
|
|
} // namespace dawn_native
|