Fix generated descriptor set layouts for shader modules with multiple entrypoints (#1497)

* Write descriptor set layout structs for each entrypoint rather than for the entire module

Signed-off-by: Arc-blroth <45273859+Arc-blroth@users.noreply.github.com>

* Fix descriptor set calculation for all storage classes across all SPIRV versions by inspecting each entrypoint's instruction tree. Also adds 27 instructions to parse.rs.

Signed-off-by: Arc-blroth <45273859+Arc-blroth@users.noreply.github.com>

* Add unit tests for descriptor set calculations

Signed-off-by: Arc-blroth <45273859+Arc-blroth@users.noreply.github.com>

* Fix BufferBlock not counting as a Block decoration on structs (this fixes the unit tests from 4875bcc2)

Signed-off-by: Arc-blroth <45273859+Arc-blroth@users.noreply.github.com>

* cargo fmt

Signed-off-by: Arc-blroth <45273859+Arc-blroth@users.noreply.github.com>

* Update changelog

Signed-off-by: Arc-blroth <45273859+Arc-blroth@users.noreply.github.com>
This commit is contained in:
Arc'blroth 2021-03-14 01:33:44 -08:00 committed by GitHub
parent b39086182b
commit 73eb8e7a9f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 835 additions and 57 deletions

View File

@ -6,6 +6,10 @@
- **Breaking** On `AutoCommandBufferBuilder`, methods that bind a descriptor set now take a `dynamic_offsets` parameter
- **Breaking** On `AutoCommandBufferBuilder` and `SyncCommandBufferBuilder`, the `update_buffer` method now takes `data` by reference
- **Breaking** Made `PipelineLayoutDescTweaks` public, for use with compute pipelines
- Fixed `shader!` generated descriptor set layouts for shader modules with multiple entrypoints
- **Breaking** Prefixed `shader!` generated descriptor set `Layout` structs with the name of the entrypoint the layout belongs to. For shaders generated from GLSL source, this means `Layout` has been renamed to `MainLayout`.
- **Breaking** `shader!` will no longer generate descriptor information for variables that are declared but not used in a shader.
- **Breaking** `shader!` now accepts structs in shader interfaces decorated with `BufferBlock` rather than `Block`
- Added support for `ImageAspect` and YV12/NV12 formats, for use with the UnsafeImage API.
- Added basic VK_KHR_external_memory, VK_KHR_external_memory_fd, and VK_EXT_external_memory_dma_buf support.
- Fixed potential segmentation fault in `ComputePipeline` when referencing `PipelineCache` objects.

View File

@ -26,7 +26,7 @@ use crate::parse;
use crate::read_file_to_string;
use crate::spec_consts;
use crate::structs;
use crate::{descriptor_sets, TypesMeta};
use crate::TypesMeta;
fn include_callback(
requested_source_path_raw: &str,
@ -247,14 +247,15 @@ pub(super) fn reflect(
let mut entry_points_outside_impl: Vec<TokenStream> = vec![];
for instruction in doc.instructions.iter() {
if let &Instruction::EntryPoint { .. } = instruction {
let (outside, entry_point) = entry_point::write_entry_point(&doc, instruction);
let (outside, entry_point, descriptor_sets) =
entry_point::write_entry_point(&doc, instruction, &types_meta);
entry_points_inside_impl.push(entry_point);
entry_points_outside_impl.push(outside);
entry_points_outside_impl.push(descriptor_sets);
}
}
let structs = structs::write_structs(&doc, &types_meta);
let descriptor_sets = descriptor_sets::write_descriptor_sets(&doc, &types_meta);
let specialization_constants = spec_consts::write_specialization_constants(&doc, &types_meta);
let uses = &types_meta.uses;
let ast = quote! {
@ -334,7 +335,6 @@ pub(super) fn reflect(
#structs
}
#descriptor_sets
#specialization_constants
};

View File

@ -9,55 +9,32 @@
use std::cmp;
use proc_macro2::TokenStream;
use proc_macro2::{Ident, TokenStream};
use crate::enums::{Decoration, Dim, ImageFormat, StorageClass};
use crate::parse::{Instruction, Spirv};
use crate::{spirv_search, TypesMeta};
use std::collections::HashSet;
pub(super) fn write_descriptor_sets(doc: &Spirv, types_meta: &TypesMeta) -> TokenStream {
// TODO: not implemented correctly
struct Descriptor {
set: u32,
binding: u32,
desc_ty: TokenStream,
array_count: u64,
readonly: bool,
}
pub(super) fn write_descriptor_sets(
doc: &Spirv,
entry_point_layout_name: &Ident,
entrypoint_id: u32,
interface: &[u32],
types_meta: &TypesMeta,
) -> TokenStream {
// TODO: somewhat implemented correctly
// Finding all the descriptors.
let mut descriptors = Vec::new();
struct Descriptor {
set: u32,
binding: u32,
desc_ty: TokenStream,
array_count: u64,
readonly: bool,
}
// Looping to find all the elements that have the `DescriptorSet` decoration.
for set_decoration in doc.get_decorations(Decoration::DecorationDescriptorSet) {
let variable_id = set_decoration.target_id;
let set = set_decoration.params[0];
// Find which type is pointed to by this variable.
let (pointed_ty, storage_class) = pointer_variable_ty(doc, variable_id);
// Name of the variable.
let name = spirv_search::name_from_id(doc, variable_id);
// Find the binding point of this descriptor.
// TODO: There was a previous todo here, I think it was asking for this to be implemented for member decorations? check git history
let binding = doc
.get_decoration_params(variable_id, Decoration::DecorationBinding)
.unwrap()[0];
// Find information about the kind of binding for this descriptor.
let (desc_ty, readonly, array_count) =
descriptor_infos(doc, pointed_ty, storage_class, false).expect(&format!(
"Couldn't find relevant type for uniform `{}` (type {}, maybe unimplemented)",
name, pointed_ty
));
descriptors.push(Descriptor {
desc_ty,
set,
binding,
array_count,
readonly,
});
}
let descriptors = find_descriptors(doc, entrypoint_id, interface);
// Looping to find all the push constant structs.
let mut push_constants_size = 0;
@ -127,10 +104,10 @@ pub(super) fn write_descriptor_sets(doc: &Spirv, types_meta: &TypesMeta) -> Toke
quote! {
#[derive(Debug, Clone)]
pub struct Layout(pub ShaderStages);
pub struct #entry_point_layout_name(pub ShaderStages);
#[allow(unsafe_code)]
unsafe impl PipelineLayoutDesc for Layout {
unsafe impl PipelineLayoutDesc for #entry_point_layout_name {
fn num_sets(&self) -> usize {
#num_sets
}
@ -160,6 +137,201 @@ pub(super) fn write_descriptor_sets(doc: &Spirv, types_meta: &TypesMeta) -> Toke
}
}
fn find_descriptors(doc: &Spirv, entrypoint_id: u32, interface: &[u32]) -> Vec<Descriptor> {
let mut descriptors = Vec::new();
// For SPIR-V 1.4+, the entrypoint interface can specify variables of all storage classes,
// and most tools will put all used variables in the entrypoint interface. However,
// SPIR-V 1.0-1.3 do not specify variables other than Input/Output ones in the interface,
// and instead the function itself must be inspected.
let variables = {
let mut found_variables: HashSet<u32> = interface.iter().cloned().collect();
let mut inspected_functions: HashSet<u32> = HashSet::new();
find_variables_in_function(
&doc,
entrypoint_id,
&mut inspected_functions,
&mut found_variables,
);
found_variables
};
// Looping to find all the interface elements that have the `DescriptorSet` decoration.
for set_decoration in doc.get_decorations(Decoration::DecorationDescriptorSet) {
let variable_id = set_decoration.target_id;
if !variables.contains(&variable_id) {
continue;
}
let set = set_decoration.params[0];
// Find which type is pointed to by this variable.
let (pointed_ty, storage_class) = pointer_variable_ty(doc, variable_id);
// Name of the variable.
let name = spirv_search::name_from_id(doc, variable_id);
// Find the binding point of this descriptor.
// TODO: There was a previous todo here, I think it was asking for this to be implemented for member decorations? check git history
let binding = doc
.get_decoration_params(variable_id, Decoration::DecorationBinding)
.unwrap()[0];
// Find information about the kind of binding for this descriptor.
let (desc_ty, readonly, array_count) =
descriptor_infos(doc, pointed_ty, storage_class, false).expect(&format!(
"Couldn't find relevant type for uniform `{}` (type {}, maybe unimplemented)",
name, pointed_ty
));
descriptors.push(Descriptor {
desc_ty,
set,
binding,
array_count,
readonly,
});
}
descriptors
}
// Recursively finds every pointer variable used in the execution of a function.
fn find_variables_in_function(
doc: &Spirv,
function: u32,
inspected_functions: &mut HashSet<u32>,
found_variables: &mut HashSet<u32>,
) {
inspected_functions.insert(function);
let mut in_function = false;
for instruction in &doc.instructions {
if !in_function {
match instruction {
Instruction::Function { result_id, .. } if result_id == &function => {
in_function = true;
}
_ => {}
}
} else {
// We only care about instructions that accept pointers.
// https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#_universal_validation_rules
match instruction {
Instruction::Load { pointer, .. } | Instruction::Store { pointer, .. } => {
found_variables.insert(*pointer);
}
Instruction::AccessChain { base_id, .. }
| Instruction::InBoundsAccessChain { base_id, .. } => {
found_variables.insert(*base_id);
}
Instruction::FunctionCall {
function_id, args, ..
} => {
args.iter().for_each(|&x| {
found_variables.insert(x);
});
if !inspected_functions.contains(function_id) {
find_variables_in_function(
doc,
*function_id,
inspected_functions,
found_variables,
);
}
}
Instruction::ImageTexelPointer {
image,
coordinate,
sample,
..
} => {
found_variables.insert(*image);
found_variables.insert(*coordinate);
found_variables.insert(*sample);
}
Instruction::CopyMemory {
target_id,
source_id,
..
} => {
found_variables.insert(*target_id);
found_variables.insert(*source_id);
}
Instruction::CopyObject { operand_id, .. } => {
found_variables.insert(*operand_id);
}
Instruction::AtomicLoad { pointer, .. }
| Instruction::AtomicIIncrement { pointer, .. }
| Instruction::AtomicIDecrement { pointer, .. }
| Instruction::AtomicFlagTestAndSet { pointer, .. }
| Instruction::AtomicFlagClear { pointer, .. } => {
found_variables.insert(*pointer);
}
Instruction::AtomicStore {
pointer, value_id, ..
}
| Instruction::AtomicExchange {
pointer, value_id, ..
}
| Instruction::AtomicIAdd {
pointer, value_id, ..
}
| Instruction::AtomicISub {
pointer, value_id, ..
}
| Instruction::AtomicSMin {
pointer, value_id, ..
}
| Instruction::AtomicUMin {
pointer, value_id, ..
}
| Instruction::AtomicSMax {
pointer, value_id, ..
}
| Instruction::AtomicUMax {
pointer, value_id, ..
}
| Instruction::AtomicAnd {
pointer, value_id, ..
}
| Instruction::AtomicOr {
pointer, value_id, ..
}
| Instruction::AtomicXor {
pointer, value_id, ..
} => {
found_variables.insert(*pointer);
found_variables.insert(*value_id);
}
Instruction::AtomicCompareExchange {
pointer,
value_id,
comparator_id,
..
}
| Instruction::AtomicCompareExchangeWeak {
pointer,
value_id,
comparator_id,
..
} => {
found_variables.insert(*pointer);
found_variables.insert(*value_id);
found_variables.insert(*comparator_id);
}
Instruction::ExtInst { operands, .. } => {
// We don't know which extended instructions take pointers,
// so we must interpret every operand as a pointer.
operands.iter().for_each(|&o| {
found_variables.insert(o);
});
}
Instruction::FunctionEnd => return,
_ => {}
}
}
}
}
/// Assumes that `variable` is a variable with a `TypePointer` and returns the id of the pointed
/// type and the storage class.
fn pointer_variable_ty(doc: &Spirv, variable: u32) -> (u32, StorageClass) {
@ -210,9 +382,12 @@ fn descriptor_infos(
let decoration_block = doc
.get_decoration_params(pointed_ty, Decoration::DecorationBlock)
.is_some();
let decoration_buffer_block = doc
.get_decoration_params(pointed_ty, Decoration::DecorationBufferBlock)
.is_some();
assert!(
decoration_block,
"Structs in shader interface are expected to be decorated with Block"
decoration_block ^ decoration_buffer_block,
"Structs in shader interface are expected to be decorated with one of Block or BufferBlock"
);
let is_ssbo = pointer_storage == StorageClass::StorageClassStorageBuffer;
@ -366,3 +541,183 @@ fn descriptor_infos(
})
.next()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::codegen::compile;
use crate::parse;
use shaderc::ShaderKind;
use std::path::{Path, PathBuf};
/// `entrypoint1.frag.glsl`:
/// ```glsl
/// #version 450
///
/// layout(set = 0, binding = 0) uniform Uniform {
/// uint data;
/// } ubo;
///
/// layout(set = 0, binding = 1) buffer Buffer {
/// uint data;
/// } bo;
///
/// layout(set = 0, binding = 2) uniform sampler textureSampler;
/// layout(set = 0, binding = 3) uniform texture2D imageTexture;
///
/// layout(push_constant) uniform PushConstant {
/// uint data;
/// } push;
///
/// layout(input_attachment_index = 0, set = 0, binding = 4) uniform subpassInput inputAttachment;
///
/// layout(location = 0) out vec4 outColor;
///
/// void entrypoint1() {
/// bo.data = 12;
/// outColor = vec4(
/// float(ubo.data),
/// float(push.data),
/// texture(sampler2D(imageTexture, textureSampler), vec2(0.0, 0.0)).x,
/// subpassLoad(inputAttachment).x
/// );
/// }
/// ```
///
/// `entrypoint2.frag.glsl`:
/// ```glsl
/// #version 450
///
/// layout(input_attachment_index = 0, set = 0, binding = 0) uniform subpassInput inputAttachment2;
///
/// layout(set = 0, binding = 1) buffer Buffer {
/// uint data;
/// } bo2;
///
/// layout(set = 0, binding = 2) uniform Uniform {
/// uint data;
/// } ubo2;
///
/// layout(push_constant) uniform PushConstant {
/// uint data;
/// } push2;
///
/// void entrypoint2() {
/// bo2.data = ubo2.data + push2.data + int(subpassLoad(inputAttachment2).y);
/// }
/// ```
///
/// Compiled and linked with:
/// ```sh
/// glslangvalidator -e entrypoint1 --source-entrypoint entrypoint1 -V100 entrypoint1.frag.glsl -o entrypoint1.spv
/// glslangvalidator -e entrypoint2 --source-entrypoint entrypoint2 -V100 entrypoint2.frag.glsl -o entrypoint2.spv
/// spirv-link entrypoint1.spv entrypoint2.spv -o multiple_entrypoints.spv
/// ```
#[test]
fn test_descriptor_calculation_with_multiple_entrypoints() {
let data = include_bytes!("../tests/multiple_entrypoints.spv");
let instructions: Vec<u32> = data
.chunks(4)
.map(|c| {
((c[3] as u32) << 24) | ((c[2] as u32) << 16) | ((c[1] as u32) << 8) | c[0] as u32
})
.collect();
let doc = parse::parse_spirv(&instructions).unwrap();
let mut descriptors = Vec::new();
for instruction in doc.instructions.iter() {
if let &Instruction::EntryPoint {
id, ref interface, ..
} = instruction
{
descriptors.push(find_descriptors(&doc, id, interface));
}
}
// Check first entrypoint
let e1_descriptors = descriptors.get(0).expect("Could not find entrypoint1");
let mut e1_bindings = Vec::new();
for d in e1_descriptors {
e1_bindings.push((d.set, d.binding));
}
assert_eq!(e1_bindings.len(), 5);
assert!(e1_bindings.contains(&(0, 0)));
assert!(e1_bindings.contains(&(0, 1)));
assert!(e1_bindings.contains(&(0, 2)));
assert!(e1_bindings.contains(&(0, 3)));
assert!(e1_bindings.contains(&(0, 4)));
// Check second entrypoint
let e2_descriptors = descriptors.get(1).expect("Could not find entrypoint2");
let mut e2_bindings = Vec::new();
for d in e2_descriptors {
e2_bindings.push((d.set, d.binding));
}
assert_eq!(e2_bindings.len(), 3);
assert!(e2_bindings.contains(&(0, 0)));
assert!(e2_bindings.contains(&(0, 1)));
assert!(e2_bindings.contains(&(0, 2)));
}
#[test]
fn test_descriptor_calculation_with_multiple_functions() {
let includes: [PathBuf; 0] = [];
let defines: [(String, String); 0] = [];
let comp = compile(
None,
&Path::new(""),
"
#version 450
layout(set = 1, binding = 0) buffer Buffer {
vec3 data;
} bo;
layout(set = 2, binding = 0) uniform Uniform {
float data;
} ubo;
layout(set = 3, binding = 1) uniform sampler textureSampler;
layout(set = 3, binding = 2) uniform texture2D imageTexture;
float withMagicSparkles(float data) {
return texture(sampler2D(imageTexture, textureSampler), vec2(data, data)).x;
}
vec3 makeSecretSauce() {
return vec3(withMagicSparkles(ubo.data));
}
void main() {
bo.data = makeSecretSauce();
}
",
ShaderKind::Vertex,
&includes,
&defines,
)
.unwrap();
let doc = parse::parse_spirv(comp.as_binary()).unwrap();
for instruction in doc.instructions.iter() {
if let &Instruction::EntryPoint {
id, ref interface, ..
} = instruction
{
let descriptors = find_descriptors(&doc, id, interface);
let mut bindings = Vec::new();
for d in descriptors {
bindings.push((d.set, d.binding));
}
assert_eq!(bindings.len(), 4);
assert!(bindings.contains(&(1, 0)));
assert!(bindings.contains(&(2, 0)));
assert!(bindings.contains(&(3, 1)));
assert!(bindings.contains(&(3, 2)));
return;
}
}
panic!("Could not find entrypoint");
}
}

View File

@ -10,11 +10,16 @@
use proc_macro2::{Span, TokenStream};
use syn::Ident;
use crate::descriptor_sets::write_descriptor_sets;
use crate::enums::{Decoration, ExecutionMode, ExecutionModel, StorageClass};
use crate::parse::{Instruction, Spirv};
use crate::spirv_search;
use crate::{spirv_search, TypesMeta};
pub fn write_entry_point(doc: &Spirv, instruction: &Instruction) -> (TokenStream, TokenStream) {
pub(super) fn write_entry_point(
doc: &Spirv,
instruction: &Instruction,
types_meta: &TypesMeta,
) -> (TokenStream, TokenStream, TokenStream) {
let (execution, id, ep_name, interface) = match instruction {
&Instruction::EntryPoint {
ref execution,
@ -52,6 +57,18 @@ pub fn write_entry_point(doc: &Spirv, instruction: &Instruction) -> (TokenStream
ignore_first_array_out,
);
let descriptor_sets_layout_name = Ident::new(
format!("{}Layout", capitalized_ep_name).as_str(),
Span::call_site(),
);
let descriptor_sets_layout_struct = write_descriptor_sets(
&doc,
&descriptor_sets_layout_name,
id,
interface,
&types_meta,
);
let spec_consts_struct = if crate::spec_consts::has_specialization_constants(doc) {
quote! { SpecializationConstants }
} else {
@ -61,10 +78,10 @@ pub fn write_entry_point(doc: &Spirv, instruction: &Instruction) -> (TokenStream
let (ty, f_call) = {
if let ExecutionModel::ExecutionModelGLCompute = *execution {
(
quote! { ::vulkano::pipeline::shader::ComputeEntryPoint<#spec_consts_struct, Layout> },
quote! { ::vulkano::pipeline::shader::ComputeEntryPoint<#spec_consts_struct, #descriptor_sets_layout_name> },
quote! { compute_entry_point(
::std::ffi::CStr::from_ptr(NAME.as_ptr() as *const _),
Layout(ShaderStages { compute: true, .. ShaderStages::none() })
#descriptor_sets_layout_name(ShaderStages { compute: true, .. ShaderStages::none() })
)},
)
} else {
@ -171,14 +188,14 @@ pub fn write_entry_point(doc: &Spirv, instruction: &Instruction) -> (TokenStream
#spec_consts_struct,
#capitalized_ep_name_input,
#capitalized_ep_name_output,
Layout>
#descriptor_sets_layout_name>
};
let f_call = quote! {
graphics_entry_point(
::std::ffi::CStr::from_ptr(NAME.as_ptr() as *const _),
#capitalized_ep_name_input,
#capitalized_ep_name_output,
Layout(#stage),
#descriptor_sets_layout_name(#stage),
#entry_ty
)
};
@ -207,7 +224,11 @@ pub fn write_entry_point(doc: &Spirv, instruction: &Instruction) -> (TokenStream
}
};
(interface_structs, entry_point)
(
interface_structs,
entry_point,
descriptor_sets_layout_struct,
)
}
struct Element {

View File

@ -75,6 +75,13 @@ pub enum Instruction {
result_id: u32,
name: String,
},
ExtInst {
result_type_id: u32,
result_id: u32,
set: u32,
instruction: u32,
operands: Vec<u32>,
},
MemoryModel(AddressingModel, MemoryModel),
EntryPoint {
execution: ExecutionModel,
@ -176,13 +183,61 @@ pub enum Instruction {
result_id: u32,
data: Vec<u32>,
},
Function {
result_type_id: u32,
result_id: u32,
function_control: u32,
function_type_id: u32,
},
FunctionEnd,
FunctionCall {
result_type_id: u32,
result_id: u32,
function_id: u32,
args: Vec<u32>,
},
Variable {
result_type_id: u32,
result_id: u32,
storage_class: StorageClass,
initializer: Option<u32>,
},
ImageTexelPointer {
result_type_id: u32,
result_id: u32,
image: u32,
coordinate: u32,
sample: u32,
},
Load {
result_type_id: u32,
result_id: u32,
pointer: u32,
memory_operands: Option<u32>,
},
Store {
pointer: u32,
object: u32,
memory_operands: Option<u32>,
},
CopyMemory {
target_id: u32,
source_id: u32,
memory_operands: Option<u32>,
source_memory_operands: Option<u32>,
},
AccessChain {
result_type_id: u32,
result_id: u32,
base_id: u32,
indexes: Vec<i32>,
},
InBoundsAccessChain {
result_type_id: u32,
result_id: u32,
base_id: u32,
indexes: Vec<i32>,
},
Decorate {
target_id: u32,
decoration: Decoration,
@ -205,6 +260,138 @@ pub enum Instruction {
decoration_group: u32,
targets: Vec<(u32, u32)>,
},
CopyObject {
result_type_id: u32,
result_id: u32,
operand_id: u32,
},
AtomicLoad {
result_type_id: u32,
result_id: u32,
pointer: u32,
scope_id: u32,
memory_semantics_id: u32,
},
AtomicStore {
pointer: u32,
scope_id: u32,
memory_semantics_id: u32,
value_id: u32,
},
AtomicExchange {
result_type_id: u32,
result_id: u32,
pointer: u32,
scope_id: u32,
memory_semantics_id: u32,
value_id: u32,
},
AtomicCompareExchange {
result_type_id: u32,
result_id: u32,
pointer: u32,
scope_id: u32,
memory_semantics_equal_id: u32,
memory_semantics_unequal_id: u32,
value_id: u32,
comparator_id: u32,
},
AtomicCompareExchangeWeak {
result_type_id: u32,
result_id: u32,
pointer: u32,
scope_id: u32,
memory_semantics_equal_id: u32,
memory_semantics_unequal_id: u32,
value_id: u32,
comparator_id: u32,
},
AtomicIIncrement {
result_type_id: u32,
result_id: u32,
pointer: u32,
scope_id: u32,
memory_semantics_id: u32,
},
AtomicIDecrement {
result_type_id: u32,
result_id: u32,
pointer: u32,
scope_id: u32,
memory_semantics_id: u32,
},
AtomicIAdd {
result_type_id: u32,
result_id: u32,
pointer: u32,
scope_id: u32,
memory_semantics_id: u32,
value_id: u32,
},
AtomicISub {
result_type_id: u32,
result_id: u32,
pointer: u32,
scope_id: u32,
memory_semantics_id: u32,
value_id: u32,
},
AtomicSMin {
result_type_id: u32,
result_id: u32,
pointer: u32,
scope_id: u32,
memory_semantics_id: u32,
value_id: u32,
},
AtomicUMin {
result_type_id: u32,
result_id: u32,
pointer: u32,
scope_id: u32,
memory_semantics_id: u32,
value_id: u32,
},
AtomicSMax {
result_type_id: u32,
result_id: u32,
pointer: u32,
scope_id: u32,
memory_semantics_id: u32,
value_id: u32,
},
AtomicUMax {
result_type_id: u32,
result_id: u32,
pointer: u32,
scope_id: u32,
memory_semantics_id: u32,
value_id: u32,
},
AtomicAnd {
result_type_id: u32,
result_id: u32,
pointer: u32,
scope_id: u32,
memory_semantics_id: u32,
value_id: u32,
},
AtomicOr {
result_type_id: u32,
result_id: u32,
pointer: u32,
scope_id: u32,
memory_semantics_id: u32,
value_id: u32,
},
AtomicXor {
result_type_id: u32,
result_id: u32,
pointer: u32,
scope_id: u32,
memory_semantics_id: u32,
value_id: u32,
},
Label {
result_id: u32,
},
@ -213,6 +400,18 @@ pub enum Instruction {
},
Kill,
Return,
AtomicFlagTestAndSet {
result_type_id: u32,
result_id: u32,
pointer: u32,
scope_id: u32,
memory_semantics_id: u32,
},
AtomicFlagClear {
pointer: u32,
scope_id: u32,
memory_semantics_id: u32,
},
}
fn parse_instruction(i: &[u32]) -> Result<(Instruction, &[u32]), ParseError> {
@ -246,6 +445,13 @@ fn decode_instruction(opcode: u16, operands: &[u32]) -> Result<Instruction, Pars
result_id: operands[0],
name: parse_string(&operands[1..]).0,
},
12 => Instruction::ExtInst {
result_type_id: operands[0],
result_id: operands[1],
set: operands[2],
instruction: operands[3],
operands: operands[4..].to_owned(),
},
14 => Instruction::MemoryModel(
AddressingModel::from_num(operands[0])?,
MemoryModel::from_num(operands[1])?,
@ -367,13 +573,61 @@ fn decode_instruction(opcode: u16, operands: &[u32]) -> Result<Instruction, Pars
result_id: operands[1],
data: operands[2..].to_owned(),
},
54 => Instruction::Function {
result_type_id: operands[0],
result_id: operands[1],
function_control: operands[2],
function_type_id: operands[3],
},
56 => Instruction::FunctionEnd,
57 => Instruction::FunctionCall {
result_type_id: operands[0],
result_id: operands[1],
function_id: operands[2],
args: operands[3..].to_owned(),
},
59 => Instruction::Variable {
result_type_id: operands[0],
result_id: operands[1],
storage_class: StorageClass::from_num(operands[2])?,
initializer: operands.get(3).map(|&v| v),
},
60 => Instruction::ImageTexelPointer {
result_type_id: operands[0],
result_id: operands[1],
image: operands[2],
coordinate: operands[3],
sample: operands[4],
},
61 => Instruction::Load {
result_type_id: operands[0],
result_id: operands[1],
pointer: operands[2],
memory_operands: operands.get(3).map(|&v| v),
},
62 => Instruction::Store {
pointer: operands[0],
object: operands[1],
memory_operands: operands.get(2).map(|&v| v),
},
63 => Instruction::CopyMemory {
target_id: operands[0],
source_id: operands[1],
memory_operands: operands.get(2).map(|&v| v),
source_memory_operands: operands.get(3).map(|&v| v),
},
65 => Instruction::AccessChain {
result_type_id: operands[0],
result_id: operands[1],
base_id: operands[2],
indexes: operands[3..].iter().map(|&v| v as i32).collect(),
},
66 => Instruction::InBoundsAccessChain {
result_type_id: operands[0],
result_id: operands[1],
base_id: operands[2],
indexes: operands[3..].iter().map(|&v| v as i32).collect(),
},
71 => Instruction::Decorate {
target_id: operands[0],
decoration: Decoration::from_num(operands[1])?,
@ -396,6 +650,138 @@ fn decode_instruction(opcode: u16, operands: &[u32]) -> Result<Instruction, Pars
decoration_group: operands[0],
targets: operands.chunks(2).map(|x| (x[0], x[1])).collect(),
},
83 => Instruction::CopyObject {
result_type_id: operands[0],
result_id: operands[1],
operand_id: operands[2],
},
227 => Instruction::AtomicLoad {
result_type_id: operands[0],
result_id: operands[1],
pointer: operands[2],
scope_id: operands[3],
memory_semantics_id: operands[4],
},
228 => Instruction::AtomicStore {
pointer: operands[0],
scope_id: operands[1],
memory_semantics_id: operands[2],
value_id: operands[3],
},
229 => Instruction::AtomicExchange {
result_type_id: operands[0],
result_id: operands[1],
pointer: operands[2],
scope_id: operands[3],
memory_semantics_id: operands[4],
value_id: operands[5],
},
230 => Instruction::AtomicCompareExchange {
result_type_id: operands[0],
result_id: operands[1],
pointer: operands[2],
scope_id: operands[3],
memory_semantics_equal_id: operands[4],
memory_semantics_unequal_id: operands[5],
value_id: operands[6],
comparator_id: operands[7],
},
231 => Instruction::AtomicCompareExchangeWeak {
result_type_id: operands[0],
result_id: operands[1],
pointer: operands[2],
scope_id: operands[3],
memory_semantics_equal_id: operands[4],
memory_semantics_unequal_id: operands[5],
value_id: operands[6],
comparator_id: operands[7],
},
232 => Instruction::AtomicIIncrement {
result_type_id: operands[0],
result_id: operands[1],
pointer: operands[2],
scope_id: operands[3],
memory_semantics_id: operands[4],
},
233 => Instruction::AtomicIDecrement {
result_type_id: operands[0],
result_id: operands[1],
pointer: operands[2],
scope_id: operands[3],
memory_semantics_id: operands[4],
},
234 => Instruction::AtomicIAdd {
result_type_id: operands[0],
result_id: operands[1],
pointer: operands[2],
scope_id: operands[3],
memory_semantics_id: operands[4],
value_id: operands[5],
},
235 => Instruction::AtomicISub {
result_type_id: operands[0],
result_id: operands[1],
pointer: operands[2],
scope_id: operands[3],
memory_semantics_id: operands[4],
value_id: operands[5],
},
236 => Instruction::AtomicSMin {
result_type_id: operands[0],
result_id: operands[1],
pointer: operands[2],
scope_id: operands[3],
memory_semantics_id: operands[4],
value_id: operands[5],
},
237 => Instruction::AtomicUMin {
result_type_id: operands[0],
result_id: operands[1],
pointer: operands[2],
scope_id: operands[3],
memory_semantics_id: operands[4],
value_id: operands[5],
},
238 => Instruction::AtomicSMax {
result_type_id: operands[0],
result_id: operands[1],
pointer: operands[2],
scope_id: operands[3],
memory_semantics_id: operands[4],
value_id: operands[5],
},
239 => Instruction::AtomicUMax {
result_type_id: operands[0],
result_id: operands[1],
pointer: operands[2],
scope_id: operands[3],
memory_semantics_id: operands[4],
value_id: operands[5],
},
240 => Instruction::AtomicAnd {
result_type_id: operands[0],
result_id: operands[1],
pointer: operands[2],
scope_id: operands[3],
memory_semantics_id: operands[4],
value_id: operands[5],
},
241 => Instruction::AtomicOr {
result_type_id: operands[0],
result_id: operands[1],
pointer: operands[2],
scope_id: operands[3],
memory_semantics_id: operands[4],
value_id: operands[5],
},
242 => Instruction::AtomicXor {
result_type_id: operands[0],
result_id: operands[1],
pointer: operands[2],
scope_id: operands[3],
memory_semantics_id: operands[4],
value_id: operands[5],
},
248 => Instruction::Label {
result_id: operands[0],
},
@ -404,6 +790,18 @@ fn decode_instruction(opcode: u16, operands: &[u32]) -> Result<Instruction, Pars
},
252 => Instruction::Kill,
253 => Instruction::Return,
318 => Instruction::AtomicFlagTestAndSet {
result_type_id: operands[0],
result_id: operands[1],
pointer: operands[2],
scope_id: operands[3],
memory_semantics_id: operands[4],
},
319 => Instruction::AtomicFlagClear {
pointer: operands[0],
scope_id: operands[1],
memory_semantics_id: operands[2],
},
_ => Instruction::Unknown(opcode, operands.to_owned()),
})
}

Binary file not shown.