Skip to content
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

discard(), update rspirv, better capability computation #380

Merged
merged 1 commit into from
Jan 18, 2021
Merged

Conversation

khyperia
Copy link
Contributor

We could bikeshed on the names of discard() and demote_to_helper_invocation(), but I hope with the doc comments it's pretty clear to users regardless of the exact names.

Capability computation was apparently borked, as we were including e.g. OpCapability DerivativeControl in our shaders even without calling the derivative functions. We should probably rethink this area, the current system is still a "temporary" hack. I cleaned up the hack a little bit, but it's still pretty gross and weird.

@khyperia khyperia requested a review from eddyb January 18, 2021 09:38
@khyperia
Copy link
Contributor Author

and to answer questions about "why not just default to demote to helper invocation always, not even support OpKill", I get this error message on my modern desktop GPU with up-to-date drivers:

Shader requires VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT::shaderDemoteToHelperInvocation but is not enabled on the device

}

/// Calls the `OpKill` instruction, which corresponds to discard() in GLSL
pub fn discard() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be consistent with the other function, should we have this one called kill so that it matches the convention of name of instruction except Op.

Suggested change
pub fn discard() {
pub fn kill() {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to match user's expectations of having a function/intrinsic called discard(). Not having it seems problematic, and causes unnecessary friction - blindly copying the names of SPIR-V things is something we'd like to avoid (@Jasper-Bekkers believes this much stronger than I, haha). I chose to default the discard() implementation to the GLSL one instead of the HLSL one because it's supported by default instead of requiring an extension (see error message).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something we could do is have a

mod glsl { fn discard(); }
mod hlsl { fn discard(); }
fn discard() { glsl::discard(); }

not sure if that's better/worse

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blindly copying the names of SPIR-V things is something we'd like to avoid

I mean this is ostensibly this is spirv-std so I think matching their naming makes more sense rather than having a mix of semantics from different specs and languages.

If discoverability is a concern, we can add a doc alias so that when users search for discard they find this function. E.g.

#[doc(alias = "discard")]
fn kill() {}

Copy link
Contributor

@eddyb eddyb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some comments for posterity (feel free to file some issues about some of these things if you want to) but I don't have anything against landing the PR as-is.

rspirv = { git = "https://github.com/gfx-rs/rspirv.git", rev = "01ca0d2e5b667a0e4ff1bc1804511e38f9a08759" }
rspirv = { git = "https://github.com/gfx-rs/rspirv.git", rev = "7111e6c5a8bb43563d280825fa65623032ee052d" }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, looks like this resulted in some deduplication/version convergence in Cargo.lock.

pub fn discard() {
#[cfg(target_arch = "spirv")]
unsafe {
asm!("OpKill", "%unused = OpLabel");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hah, I didn't think of this when we needed discard() for a shadertoy - I'm guessing this just bypasses rspirv::dr's representation of blocks but still results in correct SPIR-V output? I wonder if it has a risk of breaking some of the linker algorithms, tho that won't happen if it gets serialized and reparsed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

haha yeah it's extremely cursed. only reason it works is because we serialize to object files and re-parse in the linker.

}

/// Calls the `OpKill` instruction, which corresponds to discard() in GLSL
pub fn discard() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Besides the naming discussion, there's two things I want to add here (tho neither should block the PR):

  • would fn discard() -> ! make sense? i.e. is execution guaranteed to end, unlike demote_to_helper_invocation?
  • long-term we probably want to have some kind of token to avoid being able to call this outside of a fragment shader, right? (if demote_to_helper_invocation also needs that, it could be a demote_to_helper method on a ShaderInvocation token, which I find nice naming-wise, but that's just bikeshedding)

Comment on lines +55 to +61
pub fn demote_to_helper_invocation() {
#[cfg(target_arch = "spirv")]
unsafe {
asm!(
"OpExtension \"SPV_EXT_demote_to_helper_invocation\"",
"OpCapability DemoteToHelperInvocationEXT",
"OpDemoteToHelperInvocationEXT"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, right, the other reason to have a token is because we could support it in the CPU runner if it just had a &AtomicBool (or just &Cell<bool>) indicating it's a helper invocation and the output shouldn't be written.

Far less sure about what to do with OpKill tho, short of a panic! with a special type that can be caught and acted upon. Anyway that's just more tangents.

@mergify mergify bot merged commit 225d89d into main Jan 18, 2021
@mergify mergify bot deleted the discard branch January 18, 2021 11:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants