-
Notifications
You must be signed in to change notification settings - Fork 74
Erase explicit layout decorations (Offset
/ArrayStride
) when disallowed by Vulkan.
#379
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Erase explicit layout decorations (Offset
/ArrayStride
) when disallowed by Vulkan.
#379
Conversation
0fd77cb
to
707bdcc
Compare
53cf51d
to
8d01e85
Compare
8d01e85
to
84ed68f
Compare
I just want to note that you're upgrading CI's Vulkan SDK to 1.4.321.0, but |
@Firestar99 I 100% agree, that's why I still have the above quoted bit in the PR description, and it's why this PR was in draft state even before rebasing it on top of #400 (I know it'd be slower but it'd be nice to test EDIT: fixed the |
84ed68f
to
5a77372
Compare
As the I was able to get 10 failures in diff --git a/Cargo.toml b/Cargo.toml
index 7cf09984100..9da910407c9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -49,3 +49,3 @@ spirv-std-types = { path = "./crates/spirv-std/shared", version = "=0.9.0" }
spirv-std-macros = { path = "./crates/spirv-std/macros", version = "=0.9.0" }
-spirv-tools = { version = "0.12.1", default-features = false }
+spirv-tools = { version = "0.12.1", git = "https://github.com/Rust-GPU/spirv-tools-rs", default-features = false }
rustc_codegen_spirv = { path = "./crates/rustc_codegen_spirv", version = "=0.9.0", default-features = false }
diff --git a/crates/rustc_codegen_spirv/src/linker/mod.rs b/crates/rustc_codegen_spirv/src/linker/mod.rs
index d289c8e5fce..89c46e11f03 100644
--- a/crates/rustc_codegen_spirv/src/linker/mod.rs
+++ b/crates/rustc_codegen_spirv/src/linker/mod.rs
@@ -564,3 +564,3 @@ pub fn link(
- {
+ if false {
let timer = before_pass("spirt_passes::explicit_layout::erase_when_invalid"); Re-enabling the pass (removing So we should be able to land this once we release another version of |
Should I release spirv-tools for you? |
Yes, that would be great, thanks! |
(welp and I completely missed your message) Anyway, I thought I'd give spirv-tools some love before we release: Afterwards, a release should just be a trivial |
5a77372
to
9b425ac
Compare
This is very confusing. It doesn't seem to actually ignore the cache if it's the wrong version. Not sure why only Linux seems to be affected. Will purge GHA caches for old version and see what happens. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like I can't really comment too much on the code, as I'm totally unfamiliar with spirt. I should probably start reading into the library a bit at some point, since I assume most of our current passes will eventually end up in spirt anyway.
} | ||
} | ||
|
||
fn in_place_transform_global_var_decl(&mut self, gv_decl: &mut GlobalVarDecl) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
spirt question: I don't fully understand why some visitor functions are *_in_place
and others are not and returned Transformed<T>
instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a combination of 2-3 things:
- wanting
Transformed
's distinction of whether a chance is actually needed, to bypass work
(but that requires by-value returns to avoid misuse) - anything interned (
AttrSet
/Const
/Type
) can't do in-place mutation
(so they use&FooDef -> Transformed<FooDef>
+ only re-interning when changed) - module-owned entities (
GlobalVar
/Func
and intra-func regions/nodes) can do in-place mutation
(and cloning the whole definition to return it viaTransformed
would be prohibitively expensive)
assert_eq!(sc_kind, wk.StorageClass); | ||
!self.addr_space_allows_explicit_layout(AddrSpace::SpvStorageClass(sc)) | ||
} | ||
_ => unreachable!(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
spirt question: I also have no idea if this unreachable is actually unreachable, or how exactly imms
and type_and_const_inputs
works, since they're unfortunately not documented.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A SPIR-V "immediate" (in the spirt::spv::Imm
sense) is similar to rspirv::dr::Operand
w/o the Id
variants (as SPIR-V IDs always correspond to some Type
/Const
/GlobalVar
/Func
/etc. interned/owned entity in SPIR-T - in the case of types, type_and_const_inputs
lets them refer to other Type
s and/or Const
s, through SPIR-V IDs, but nothing else).
So hitting this unreachable!()
would require interning a type with a spirt::TypeKind::SpvInst
that:
- couldn't naturally be produced by SPIR-V -> SPIR-T (
spirt::spv::lower
) - would fail SPIR-T -> SPIR-V (
spirt::spv::lift
) - might error/panic in other parts of SPIR-T (that e.g. decode a
spv::Inst
'sspv::Imm
s according to the SPIR-V "grammar")
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From what I could gleam from spirt source:
- spirt does not use the vulkan headers to extract spec information
- instead of running codegen from spec detail, it treats spec information as runtime values
- only a few basic types that are needed for legalization are found in the source code
- the rest of the operations are just forwarded as unknown entities it won't touch (like texture accesses)
- you are still including the spec and loading it at runtime to know all possible instructions and what type they are (OpType, OpConst) for lift and lower, without actually knowing exactly what they do
Imm::Short(u32)
is sufficient for all usual operands, as they are all just u32-sized enums- Exception: string literals, for which you use one
Imm::LongStart
with manyImm::LongCont[inued]
to encode a stream of N-many bytes- I have not found any other case using
Long
- I have not found any other case using
Thoughts
- With
Imm::Long*
rarely used butImm::Short
ubiquitous, I wonder if anImm::unwrap_short()
could make code more readable? And maintainable, since it would allow you to replace the backing datastructure later without much code breakage. - With
Imm::Long
being so rare and I assume untouched by spirt, I wonder if a plainVec<u8>
or&'cx [u8]
is more efficient and simpler? That would also makeOpString
only have 2Imm
s, which makes it fit inInst.imms: SmallVec<[Imm; 2]>
and only be one indirection to access, as previously.
pub enum Imm {
Short(spec::OperandKind, u32),
Long(spec::OperandKind, Vec<u32>),
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are all good ideas (I also thought of interning immediates, and a few other possible designs, but without proper benchmarking I didn't want to start fine-tuning too much), could you copy that into an issue on the spirt
repo? (it already feels formulated like close to that)
(this also reminds me there were a few issues on the old repo, idk if they got auto-migrated or not, I don't remember checking)
spirt does not use the vulkan headers to extract spec information
instead of running codegen from spec detail, it treats spec information as runtime values
...
the rest of the operations are just forwarded as unknown entities it won't touch (like texture accesses)
you are still including the spec and loading it at runtime
As for this part, the reason the JSON is still dynamically loaded is that I found some really neat ways to exploit the way Khronos assigns numbers (opcode, enumerands, etc. - see spirt::spv::spec::indexed
), and some of that data could be neatly loaded as a binary blob (emitted by e.g. a build script), but it's hard to do it for everything and you easily run into build-host-vs-target potential issues etc.
(I know of at least one place tackling this kind of problem in its generality - icu4x
and I believe the implementation is mostly in zerovec
, which is very cool but somewhat daunting)
By now the SPIR-V spec is so large (in a "breadth" sense) that generating Rust code for everything feels quite wasteful - most of it is closer to schema/protocol/interface descriptions, and I still wish they had made the format self-descriptive for the most part.
let is_explicit_layout_decoration = match attr { | ||
Attr::SpvAnnotation(attr_spv_inst) | ||
if (attr_spv_inst.opcode == wk.OpDecorate | ||
&& [wk.ArrayStride, wk.MatrixStride] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, phew, I was worried this only mentioned ArrayStride
, but I did include MatrixStride
just in case (I guess the one instance of non-explicit-layout matrices would have to bein the context of raytracing pipelines - e.g. Ctrl+F "matrix" on https://github.khronos.org/SPIRV-Registry/extensions/KHR/SPV_KHR_ray_tracing.html - though an even simpler example is just a local variable containing a matrix).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wanted to try out if I could somehow verify that MatrixStride
is actually removed from the struct...
error: error:0:0 - Structure id 12 decorated as Block must be explicitly laid out with MatrixStride decorations.
%_struct_12 = OpTypeStruct %mat4v3float
Well.. you can't remove what isn't there in the first place...
Our matrix support is really bad and has similar screw up like the Vec's before #380, but since matrix are barely useful I think it just isn't a priority rn. The usecases are:
- that RT extension
- there's a few basic matrix instructions in SPIRV we don't emit but build them from basic instructions, just search
OpTypeMatrix
in the specOpTranspose
OpMatrixTimesScalar
OpVectorTimesMatrix
OpMatrixTimesVector
OpMatrixTimesMatrix
OpOuterProduct
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't fully verify everything due to lack of knowledge on both spirt and pointer handling. But I can write you some compiletests to verify this patch works, since we have almost none for shared memory! Feel free to cherry-pick fe55d88 on top before merging (again, no push perms).
(Locally, I reversed the order of commits to have the spirv-tools upgrade first, then my compiletests and finally your fix, so I can test properly)
… and as shared memory
CI failed on |
I was worried about that, I guess I should've kept an eye on it - it's just a cache filtering issue, for some reason on Linux it doesn't ignore the wrong version cache (even though I can't find a Linux/not-Linux conditional in that Vulkan SDK action, that could explain this). Nuking old GHA caches and retrying, if it starts working it should keep working AFAICT. EDIT: retry passed, that should be it for now. |
More recent Vulkan SDK versions have started complaining about types having explicit memory layout, when used with memory that doesn't inherently require explicit layouts (only push constants and buffers really do), e.g.:
The solution used here is simpler and more specialized (a pass that erases the explicit layout decorations from types behind pointers in storage classes that don't support them, combined with updating affected loads/stores) than what I originally described in #266 (comment), so we can hopefully use this for now and not worry about
spirt::{mem,qptr}
for the next release.(not actually tested with MoltenVK, we should make sure it actually works now)