|
|
@@ -234,17 +234,18 @@ ShaderModuleSpirV(Stage stage, std::vector<uint32_t> words, BamCacheRecord *reco
|
|
|
_parameters.push_back(std::move(var));
|
|
|
}
|
|
|
else if (def._storage_class == spv::StorageClassStorageBuffer) {
|
|
|
- _used_caps |= C_storage_buffer;
|
|
|
- }
|
|
|
- else if (def._storage_class == spv::StorageClassUniform) {
|
|
|
- // Older versions of SPIR-V defined SSBOs differently.
|
|
|
+ // For whatever reason, in GLSL, the name of an SSBO is derived from the
|
|
|
+ // name of the struct type.
|
|
|
const Definition &type_pointer_def = writer.get_definition(def._type_id);
|
|
|
nassertd(type_pointer_def._dtype == DT_type_pointer) continue;
|
|
|
const Definition &type_def = writer.get_definition(type_pointer_def._type_id);
|
|
|
nassertd(type_def._dtype == DT_type) continue;
|
|
|
- if (type_def._flags & DF_buffer_block) {
|
|
|
- _used_caps |= C_storage_buffer;
|
|
|
- }
|
|
|
+ nassertd(!type_def._name.empty()) continue;
|
|
|
+
|
|
|
+ var.name = InternalName::make(type_def._name);
|
|
|
+ _parameters.push_back(std::move(var));
|
|
|
+
|
|
|
+ _used_caps |= C_storage_buffer;
|
|
|
}
|
|
|
}
|
|
|
else if (def._dtype == DT_variable && def.is_used() &&
|
|
|
@@ -2188,17 +2189,16 @@ r_define_type(InstructionIterator &it, const ShaderType *type) {
|
|
|
|
|
|
uint32_t nargs = 8;
|
|
|
switch (image_type->get_access()) {
|
|
|
- case ShaderType::Image::Access::unknown:
|
|
|
- break;
|
|
|
- case ShaderType::Image::Access::read_only:
|
|
|
+ case ShaderType::Access::none:
|
|
|
+ case ShaderType::Access::read_only:
|
|
|
args[8] = spv::AccessQualifierReadOnly;
|
|
|
++nargs;
|
|
|
break;
|
|
|
- case ShaderType::Image::Access::write_only:
|
|
|
+ case ShaderType::Access::write_only:
|
|
|
args[8] = spv::AccessQualifierWriteOnly;
|
|
|
++nargs;
|
|
|
break;
|
|
|
- case ShaderType::Image::Access::read_write:
|
|
|
+ case ShaderType::Access::read_write:
|
|
|
args[8] = spv::AccessQualifierReadWrite;
|
|
|
++nargs;
|
|
|
break;
|
|
|
@@ -2518,17 +2518,17 @@ parse_instruction(const Instruction &op, uint32_t ¤t_function_id) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- ShaderType::Image::Access access = ShaderType::Image::Access::unknown;
|
|
|
+ ShaderType::Access access = ShaderType::Access::read_write;
|
|
|
if (op.nargs > 8) {
|
|
|
switch ((spv::AccessQualifier)op.args[8]) {
|
|
|
case spv::AccessQualifierReadOnly:
|
|
|
- access = ShaderType::Image::Access::read_only;
|
|
|
+ access = ShaderType::Access::read_only;
|
|
|
break;
|
|
|
case spv::AccessQualifierWriteOnly:
|
|
|
- access = ShaderType::Image::Access::write_only;
|
|
|
+ access = ShaderType::Access::write_only;
|
|
|
break;
|
|
|
case spv::AccessQualifierReadWrite:
|
|
|
- access = ShaderType::Image::Access::read_write;
|
|
|
+ access = ShaderType::Access::read_write;
|
|
|
break;
|
|
|
default:
|
|
|
shader_cat.error()
|
|
|
@@ -2536,6 +2536,12 @@ parse_instruction(const Instruction &op, uint32_t ¤t_function_id) {
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
+ if (_defs[op.args[0]]._flags & DF_non_writable) {
|
|
|
+ access = (access & ShaderType::Access::read_only);
|
|
|
+ }
|
|
|
+ if (_defs[op.args[0]]._flags & DF_non_readable) {
|
|
|
+ access = (access & ShaderType::Access::write_only);
|
|
|
+ }
|
|
|
|
|
|
record_type(op.args[0], ShaderType::register_type(
|
|
|
ShaderType::Image(texture_type, sampled_type->get_scalar_type(), access)));
|
|
|
@@ -2575,6 +2581,7 @@ parse_instruction(const Instruction &op, uint32_t ¤t_function_id) {
|
|
|
case spv::OpTypeStruct:
|
|
|
{
|
|
|
Definition &struct_def = _defs[op.args[0]];
|
|
|
+ int access_flags = DF_non_writable | DF_non_readable;
|
|
|
ShaderType::Struct type;
|
|
|
for (size_t i = 0; i < op.nargs - 1; ++i) {
|
|
|
uint32_t member_type_id = op.args[i + 1];
|
|
|
@@ -2591,13 +2598,42 @@ parse_instruction(const Instruction &op, uint32_t ¤t_function_id) {
|
|
|
// Ignore built-in member.
|
|
|
continue;
|
|
|
}
|
|
|
+
|
|
|
+ const ShaderType *member_type = _defs[member_type_id]._type;
|
|
|
+ if (member_def._flags & (DF_non_writable | DF_non_readable)) {
|
|
|
+ // If an image member has the readonly/writeonly qualifiers,
|
|
|
+ // then we'll inject those back into the type.
|
|
|
+ if (const ShaderType::Image *image = member_type->as_image()) {
|
|
|
+ ShaderType::Access access = image->get_access();
|
|
|
+ if (member_def._flags & DF_non_writable) {
|
|
|
+ access = (access & ShaderType::Access::read_only);
|
|
|
+ }
|
|
|
+ if (member_def._flags & DF_non_readable) {
|
|
|
+ access = (access & ShaderType::Access::write_only);
|
|
|
+ }
|
|
|
+ if (access != image->get_access()) {
|
|
|
+ member_type = ShaderType::register_type(ShaderType::Image(
|
|
|
+ image->get_texture_type(),
|
|
|
+ image->get_sampled_type(),
|
|
|
+ access));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
if (member_def._offset >= 0) {
|
|
|
- type.add_member(_defs[member_type_id]._type, member_def._name, (uint32_t)member_def._offset);
|
|
|
+ type.add_member(member_type, member_def._name, (uint32_t)member_def._offset);
|
|
|
} else {
|
|
|
- type.add_member(_defs[member_type_id]._type, member_def._name);
|
|
|
+ type.add_member(member_type, member_def._name);
|
|
|
}
|
|
|
+
|
|
|
+ // If any member is writable, the struct shan't be marked readonly.
|
|
|
+ access_flags &= member_def._flags;
|
|
|
}
|
|
|
record_type(op.args[0], ShaderType::register_type(std::move(type)));
|
|
|
+
|
|
|
+ // If all struct members are flagged readonly/writeonly, we tag the type
|
|
|
+ // so as well, since glslang doesn't decorate an SSBO in its entirety as
|
|
|
+ // readonly/writeonly properly (it applies it to all members instead)
|
|
|
+ _defs[op.args[0]]._flags |= access_flags;
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
@@ -2780,6 +2816,14 @@ parse_instruction(const Instruction &op, uint32_t ¤t_function_id) {
|
|
|
_defs[op.args[0]]._builtin = (spv::BuiltIn)op.args[2];
|
|
|
break;
|
|
|
|
|
|
+ case spv::DecorationNonWritable:
|
|
|
+ _defs[op.args[0]]._flags |= DF_non_writable;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case spv::DecorationNonReadable:
|
|
|
+ _defs[op.args[0]]._flags |= DF_non_readable;
|
|
|
+ break;
|
|
|
+
|
|
|
case spv::DecorationLocation:
|
|
|
_defs[op.args[0]]._location = op.args[2];
|
|
|
break;
|
|
|
@@ -2803,8 +2847,30 @@ parse_instruction(const Instruction &op, uint32_t ¤t_function_id) {
|
|
|
_defs[op.args[0]].modify_member(op.args[1])._builtin = (spv::BuiltIn)op.args[3];
|
|
|
break;
|
|
|
|
|
|
+ case spv::DecorationNonWritable:
|
|
|
+ _defs[op.args[0]].modify_member(op.args[1])._flags |= DF_non_writable;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case spv::DecorationNonReadable:
|
|
|
+ _defs[op.args[0]].modify_member(op.args[1])._flags |= DF_non_readable;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case spv::DecorationLocation:
|
|
|
+ _defs[op.args[0]].modify_member(op.args[1])._location = op.args[3];
|
|
|
+ break;
|
|
|
+
|
|
|
+ case spv::DecorationBinding:
|
|
|
+ shader_cat.error()
|
|
|
+ << "Invalid " << "binding" << " decoration on struct member\n";
|
|
|
+ break;
|
|
|
+
|
|
|
+ case spv::DecorationDescriptorSet:
|
|
|
+ shader_cat.error()
|
|
|
+ << "Invalid " << "set" << " decoration on struct member\n";
|
|
|
+ break;
|
|
|
+
|
|
|
case spv::DecorationOffset:
|
|
|
- _defs[op.args[0]].modify_member(op.args[1])._offset = (spv::BuiltIn)op.args[3];
|
|
|
+ _defs[op.args[0]].modify_member(op.args[1])._offset = op.args[3];
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
@@ -3037,6 +3103,12 @@ record_variable(uint32_t id, uint32_t type_pointer_id, spv::StorageClass storage
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ // In older versions of SPIR-V, an SSBO was defined using BufferBlock.
|
|
|
+ if (storage_class == spv::StorageClassUniform &&
|
|
|
+ (type_def._flags & DF_buffer_block) != 0) {
|
|
|
+ storage_class = spv::StorageClassStorageBuffer;
|
|
|
+ }
|
|
|
+
|
|
|
def._dtype = DT_variable;
|
|
|
def._type = type_def._type;
|
|
|
def._type_id = type_pointer_id;
|
|
|
@@ -3044,6 +3116,26 @@ record_variable(uint32_t id, uint32_t type_pointer_id, spv::StorageClass storage
|
|
|
def._origin_id = id;
|
|
|
def._function_id = function_id;
|
|
|
|
|
|
+ if (def._flags & (DF_non_writable | DF_non_readable)) {
|
|
|
+ // If an image variable has the readonly/writeonly qualifiers, then we'll
|
|
|
+ // inject those back into the type.
|
|
|
+ if (const ShaderType::Image *image = def._type->as_image()) {
|
|
|
+ ShaderType::Access access = image->get_access();
|
|
|
+ if (def._flags & DF_non_writable) {
|
|
|
+ access = (access & ShaderType::Access::read_only);
|
|
|
+ }
|
|
|
+ if (def._flags & DF_non_readable) {
|
|
|
+ access = (access & ShaderType::Access::write_only);
|
|
|
+ }
|
|
|
+ if (access != image->get_access()) {
|
|
|
+ def._type = ShaderType::register_type(ShaderType::Image(
|
|
|
+ image->get_texture_type(),
|
|
|
+ image->get_sampled_type(),
|
|
|
+ access));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
#ifndef NDEBUG
|
|
|
if (storage_class == spv::StorageClassUniformConstant && shader_cat.is_debug()) {
|
|
|
std::ostream &out = shader_cat.debug()
|