2018-07-18 09:40:26 +00:00
|
|
|
// Copyright 2017 The Dawn Authors
|
2017-04-20 18:38:20 +00:00
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
|
2018-07-24 11:53:51 +00:00
|
|
|
#include "dawn_native/ShaderModule.h"
|
2017-04-20 18:38:20 +00:00
|
|
|
|
2018-07-24 11:53:51 +00:00
|
|
|
#include "dawn_native/BindGroupLayout.h"
|
|
|
|
#include "dawn_native/Device.h"
|
|
|
|
#include "dawn_native/Pipeline.h"
|
|
|
|
#include "dawn_native/PipelineLayout.h"
|
2017-04-20 18:38:20 +00:00
|
|
|
|
|
|
|
#include <spirv-cross/spirv_cross.hpp>
|
2018-09-06 15:25:46 +00:00
|
|
|
#include <spirv-tools/libspirv.hpp>
|
2017-04-20 18:38:20 +00:00
|
|
|
|
2018-07-24 14:45:45 +00:00
|
|
|
namespace dawn_native {
|
2017-04-20 18:38:20 +00:00
|
|
|
|
2018-08-22 13:37:29 +00:00
|
|
|
MaybeError ValidateShaderModuleDescriptor(DeviceBase*,
|
|
|
|
const ShaderModuleDescriptor* descriptor) {
|
2018-09-10 14:17:24 +00:00
|
|
|
if (descriptor->nextInChain != nullptr) {
|
|
|
|
return DAWN_VALIDATION_ERROR("nextInChain must be nullptr");
|
|
|
|
}
|
2018-09-06 15:25:46 +00:00
|
|
|
|
2018-11-28 16:54:31 +00:00
|
|
|
spvtools::SpirvTools spirvTools(SPV_ENV_VULKAN_1_1);
|
2018-09-06 15:25:46 +00:00
|
|
|
|
|
|
|
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(descriptor->code, descriptor->codeSize)) {
|
2018-09-10 14:17:24 +00:00
|
|
|
return DAWN_VALIDATION_ERROR(errorStream.str().c_str());
|
2018-09-06 15:25:46 +00:00
|
|
|
}
|
|
|
|
|
2018-08-20 15:01:20 +00:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
// ShaderModuleBase
|
|
|
|
|
|
|
|
ShaderModuleBase::ShaderModuleBase(DeviceBase* device, const ShaderModuleDescriptor*)
|
2018-10-15 12:54:30 +00:00
|
|
|
: ObjectBase(device) {
|
2017-07-26 16:35:35 +00:00
|
|
|
}
|
|
|
|
|
2017-04-20 18:38:20 +00:00
|
|
|
void ShaderModuleBase::ExtractSpirvInfo(const spirv_cross::Compiler& compiler) {
|
2018-10-15 12:54:30 +00:00
|
|
|
DeviceBase* device = GetDevice();
|
2017-05-08 08:52:11 +00:00
|
|
|
// TODO(cwallez@chromium.org): make errors here builder-level
|
2017-06-05 20:23:18 +00:00
|
|
|
// currently errors here do not prevent the shadermodule from being used
|
2017-04-20 18:38:20 +00:00
|
|
|
const auto& resources = compiler.get_shader_resources();
|
|
|
|
|
|
|
|
switch (compiler.get_execution_model()) {
|
|
|
|
case spv::ExecutionModelVertex:
|
2018-07-18 09:38:11 +00:00
|
|
|
mExecutionModel = dawn::ShaderStage::Vertex;
|
2017-04-20 18:38:20 +00:00
|
|
|
break;
|
|
|
|
case spv::ExecutionModelFragment:
|
2018-07-18 09:38:11 +00:00
|
|
|
mExecutionModel = dawn::ShaderStage::Fragment;
|
2017-04-20 18:38:20 +00:00
|
|
|
break;
|
|
|
|
case spv::ExecutionModelGLCompute:
|
2018-07-18 09:38:11 +00:00
|
|
|
mExecutionModel = dawn::ShaderStage::Compute;
|
2017-04-20 18:38:20 +00:00
|
|
|
break;
|
|
|
|
default:
|
2017-07-11 01:48:12 +00:00
|
|
|
UNREACHABLE();
|
2017-04-20 18:38:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Extract push constants
|
2017-11-23 18:32:51 +00:00
|
|
|
mPushConstants.mask.reset();
|
|
|
|
mPushConstants.sizes.fill(0);
|
|
|
|
mPushConstants.types.fill(PushConstantType::Int);
|
2017-04-20 18:38:20 +00:00
|
|
|
|
|
|
|
if (resources.push_constant_buffers.size() > 0) {
|
|
|
|
auto interfaceBlock = resources.push_constant_buffers[0];
|
|
|
|
|
|
|
|
const auto& blockType = compiler.get_type(interfaceBlock.type_id);
|
|
|
|
ASSERT(blockType.basetype == spirv_cross::SPIRType::Struct);
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < blockType.member_types.size(); i++) {
|
2018-08-24 12:51:21 +00:00
|
|
|
ASSERT(compiler.get_member_decoration_bitset(blockType.self, i)
|
|
|
|
.get(spv::DecorationOffset));
|
|
|
|
|
2017-11-24 18:59:42 +00:00
|
|
|
uint32_t offset =
|
|
|
|
compiler.get_member_decoration(blockType.self, i, spv::DecorationOffset);
|
2017-04-20 18:38:20 +00:00
|
|
|
ASSERT(offset % 4 == 0);
|
|
|
|
offset /= 4;
|
|
|
|
|
|
|
|
auto memberType = compiler.get_type(blockType.member_types[i]);
|
|
|
|
PushConstantType constantType;
|
|
|
|
if (memberType.basetype == spirv_cross::SPIRType::Int) {
|
|
|
|
constantType = PushConstantType::Int;
|
|
|
|
} else if (memberType.basetype == spirv_cross::SPIRType::UInt) {
|
|
|
|
constantType = PushConstantType::UInt;
|
|
|
|
} else {
|
|
|
|
ASSERT(memberType.basetype == spirv_cross::SPIRType::Float);
|
|
|
|
constantType = PushConstantType::Float;
|
|
|
|
}
|
|
|
|
|
2017-11-24 18:59:42 +00:00
|
|
|
// TODO(cwallez@chromium.org): check for overflows and make the logic better take
|
|
|
|
// into account things like the array of types with padding.
|
2017-07-20 15:03:02 +00:00
|
|
|
uint32_t size = memberType.vecsize * memberType.columns;
|
|
|
|
// Handle unidimensional arrays
|
|
|
|
if (!memberType.array.empty()) {
|
|
|
|
size *= memberType.array[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (offset + size > kMaxPushConstants) {
|
2018-10-15 12:54:30 +00:00
|
|
|
device->HandleError("Push constant block too big in the SPIRV");
|
2017-07-20 15:03:02 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-11-23 18:32:51 +00:00
|
|
|
mPushConstants.mask.set(offset);
|
2017-11-24 18:59:42 +00:00
|
|
|
mPushConstants.names[offset] =
|
|
|
|
interfaceBlock.name + "." + compiler.get_member_name(blockType.self, i);
|
2017-11-23 18:32:51 +00:00
|
|
|
mPushConstants.sizes[offset] = size;
|
|
|
|
mPushConstants.types[offset] = constantType;
|
2017-04-20 18:38:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fill in bindingInfo with the SPIRV bindings
|
|
|
|
auto ExtractResourcesBinding = [this](const std::vector<spirv_cross::Resource>& resources,
|
2017-11-24 18:59:42 +00:00
|
|
|
const spirv_cross::Compiler& compiler,
|
2018-07-18 09:38:11 +00:00
|
|
|
dawn::BindingType bindingType) {
|
2017-04-20 18:38:20 +00:00
|
|
|
for (const auto& resource : resources) {
|
2018-08-24 12:51:21 +00:00
|
|
|
ASSERT(compiler.get_decoration_bitset(resource.id).get(spv::DecorationBinding));
|
|
|
|
ASSERT(
|
|
|
|
compiler.get_decoration_bitset(resource.id).get(spv::DecorationDescriptorSet));
|
|
|
|
|
2017-04-20 18:38:20 +00:00
|
|
|
uint32_t binding = compiler.get_decoration(resource.id, spv::DecorationBinding);
|
|
|
|
uint32_t set = compiler.get_decoration(resource.id, spv::DecorationDescriptorSet);
|
|
|
|
|
|
|
|
if (binding >= kMaxBindingsPerGroup || set >= kMaxBindGroups) {
|
2018-10-15 12:54:30 +00:00
|
|
|
GetDevice()->HandleError("Binding over limits in the SPIRV");
|
2017-04-20 18:38:20 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-11-23 18:32:51 +00:00
|
|
|
auto& info = mBindingInfo[set][binding];
|
2017-04-20 18:38:20 +00:00
|
|
|
info.used = true;
|
|
|
|
info.id = resource.id;
|
|
|
|
info.base_type_id = resource.base_type_id;
|
2017-07-22 00:00:22 +00:00
|
|
|
info.type = bindingType;
|
2017-04-20 18:38:20 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-11-24 18:59:42 +00:00
|
|
|
ExtractResourcesBinding(resources.uniform_buffers, compiler,
|
2018-07-18 09:38:11 +00:00
|
|
|
dawn::BindingType::UniformBuffer);
|
2017-11-24 18:59:42 +00:00
|
|
|
ExtractResourcesBinding(resources.separate_images, compiler,
|
2018-07-18 09:38:11 +00:00
|
|
|
dawn::BindingType::SampledTexture);
|
|
|
|
ExtractResourcesBinding(resources.separate_samplers, compiler, dawn::BindingType::Sampler);
|
2017-11-24 18:59:42 +00:00
|
|
|
ExtractResourcesBinding(resources.storage_buffers, compiler,
|
2018-07-18 09:38:11 +00:00
|
|
|
dawn::BindingType::StorageBuffer);
|
2017-04-20 18:38:20 +00:00
|
|
|
|
|
|
|
// Extract the vertex attributes
|
2018-07-18 09:38:11 +00:00
|
|
|
if (mExecutionModel == dawn::ShaderStage::Vertex) {
|
2017-04-20 18:38:20 +00:00
|
|
|
for (const auto& attrib : resources.stage_inputs) {
|
2018-08-24 12:51:21 +00:00
|
|
|
ASSERT(compiler.get_decoration_bitset(attrib.id).get(spv::DecorationLocation));
|
2017-04-20 18:38:20 +00:00
|
|
|
uint32_t location = compiler.get_decoration(attrib.id, spv::DecorationLocation);
|
|
|
|
|
|
|
|
if (location >= kMaxVertexAttributes) {
|
2018-10-15 12:54:30 +00:00
|
|
|
device->HandleError("Attribute location over limits in the SPIRV");
|
2017-04-20 18:38:20 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-11-23 18:32:51 +00:00
|
|
|
mUsedVertexAttributes.set(location);
|
2017-04-20 18:38:20 +00:00
|
|
|
}
|
|
|
|
|
2017-11-24 18:59:42 +00:00
|
|
|
// Without a location qualifier on vertex outputs, spirv_cross::CompilerMSL gives them
|
|
|
|
// all the location 0, causing a compile error.
|
2017-04-20 18:38:20 +00:00
|
|
|
for (const auto& attrib : resources.stage_outputs) {
|
2018-08-24 12:51:21 +00:00
|
|
|
if (!compiler.get_decoration_bitset(attrib.id).get(spv::DecorationLocation)) {
|
2018-10-15 12:54:30 +00:00
|
|
|
device->HandleError("Need location qualifier on vertex output");
|
2017-04-20 18:38:20 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-18 09:38:11 +00:00
|
|
|
if (mExecutionModel == dawn::ShaderStage::Fragment) {
|
2017-11-24 18:59:42 +00:00
|
|
|
// Without a location qualifier on vertex inputs, spirv_cross::CompilerMSL gives them
|
|
|
|
// all the location 0, causing a compile error.
|
2017-04-20 18:38:20 +00:00
|
|
|
for (const auto& attrib : resources.stage_inputs) {
|
2018-08-24 12:51:21 +00:00
|
|
|
if (!compiler.get_decoration_bitset(attrib.id).get(spv::DecorationLocation)) {
|
2018-10-15 12:54:30 +00:00
|
|
|
device->HandleError("Need location qualifier on fragment input");
|
2017-04-20 18:38:20 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const ShaderModuleBase::PushConstantInfo& ShaderModuleBase::GetPushConstants() const {
|
2017-11-23 18:32:51 +00:00
|
|
|
return mPushConstants;
|
2017-04-20 18:38:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const ShaderModuleBase::ModuleBindingInfo& ShaderModuleBase::GetBindingInfo() const {
|
2017-11-23 18:32:51 +00:00
|
|
|
return mBindingInfo;
|
2017-04-20 18:38:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const std::bitset<kMaxVertexAttributes>& ShaderModuleBase::GetUsedVertexAttributes() const {
|
2017-11-23 18:32:51 +00:00
|
|
|
return mUsedVertexAttributes;
|
2017-04-20 18:38:20 +00:00
|
|
|
}
|
|
|
|
|
2018-07-18 09:38:11 +00:00
|
|
|
dawn::ShaderStage ShaderModuleBase::GetExecutionModel() const {
|
2017-11-23 18:32:51 +00:00
|
|
|
return mExecutionModel;
|
2017-04-20 18:38:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ShaderModuleBase::IsCompatibleWithPipelineLayout(const PipelineLayoutBase* layout) {
|
2019-02-11 23:35:33 +00:00
|
|
|
for (uint32_t group : IterateBitSet(layout->GetBindGroupLayoutsMask())) {
|
2017-04-20 18:38:20 +00:00
|
|
|
if (!IsCompatibleWithBindGroupLayout(group, layout->GetBindGroupLayout(group))) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2019-02-11 23:35:33 +00:00
|
|
|
|
|
|
|
for (uint32_t group : IterateBitSet(~layout->GetBindGroupLayoutsMask())) {
|
|
|
|
for (size_t i = 0; i < kMaxBindingsPerGroup; ++i) {
|
|
|
|
if (mBindingInfo[group][i].used) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-20 18:38:20 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-11-24 18:59:42 +00:00
|
|
|
bool ShaderModuleBase::IsCompatibleWithBindGroupLayout(size_t group,
|
|
|
|
const BindGroupLayoutBase* layout) {
|
2017-04-20 18:38:20 +00:00
|
|
|
const auto& layoutInfo = layout->GetBindingInfo();
|
|
|
|
for (size_t i = 0; i < kMaxBindingsPerGroup; ++i) {
|
2017-11-23 18:32:51 +00:00
|
|
|
const auto& moduleInfo = mBindingInfo[group][i];
|
2017-04-20 18:38:20 +00:00
|
|
|
|
|
|
|
if (!moduleInfo.used) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (moduleInfo.type != layoutInfo.types[i]) {
|
|
|
|
return false;
|
|
|
|
}
|
2017-11-23 18:32:51 +00:00
|
|
|
if ((layoutInfo.visibilities[i] & StageBit(mExecutionModel)) == 0) {
|
2017-04-20 18:38:20 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-07-24 14:45:45 +00:00
|
|
|
} // namespace dawn_native
|