-
Notifications
You must be signed in to change notification settings - Fork 58
Fixing Fragment Shader Silent Optimization Issue (Issue #284) #289
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
base: main
Are you sure you want to change the base?
Conversation
Replace deprecated egrep command with the modern equivalent 'grep -E' to eliminate warnings about egrep being obsolescent. This improves the lint script output clarity without changing functionality. Also add codegen-backend override to prevent cranelift conflicts from parent directory configurations, ensuring rust-gpu builds with the required LLVM backend.
Addresses issue Rust-GPU#284 where fragment shaders were optimized to have no output operations, causing silent rendering failures. The real problem is not DCE eliminating entry points (they are always rooted), but rather fragment shaders being optimized to have no meaningful output writes to StorageClass::Output variables. Implementation: - Add validation after DCE to detect fragment shaders with no output - Provide helpful error messages with examples - Detect partial component writes that may have been optimized away - Recursively check function calls for output operations Test case: - issue-284-dead-fragment.rs: Fragment shader with no output (fails with clear error) The validation catches cases where developers write partial component assignments that get optimized away, providing clear guidance on how to fix the issue.
.cargo/config.toml
Outdated
# Override parent directory's cranelift backend setting for rust-gpu compatibility | ||
[profile.dev] | ||
codegen-backend = "llvm" | ||
|
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.
This addition is failing CI, it's probably something that should remain a local change on your machine.
Thanks for your contribution, will review the rest later :D
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.
Thanks for the feedback! Understood, I'll revert this change and keep it local on my machine. I'll wait for the review of the rest of the changes.
Thanks for your help! :D
@zmr-233 You don't need to close the PR, just update this one and keep pushing to the same branch |
This addresses maintainer feedback to keep the codegen-backend setting as a local-only configuration rather than in the committed repository.
I’ve removed the local Some existing tests are now failing because of the “no output in fragment shader” error. |
This PR addresses Issue #284, where the fragment shader successfully compiles but is silently optimized to have no output operations, causing silent rendering without errors or warnings, making debugging very difficult.
Root Cause Analysis
After a deep analysis of DCE (Dead Code Elimination), we discovered that Issue #284 is actually a "false scenario," based on the following two core facts:
The Entry-point function is never deleted: In the current DCE implementation, all functions in the module’s OpEntryPoint list are unconditionally marked as "root," becoming reachable rooted objects. In other words, even if all instructions in the function body are identified as pure calculations and removed, the function definition and OpEntryPoint declaration are still retained. DCE will not remove the entire entry-point function.
The phenomenon of "silently compiling out" is actually the simplification of the function body, not the disappearance of the entire entry-point: When no "obvious output" is written, DCE will remove internal calculations (such as OpCompositeConstruct, temporary OpLoad, etc.) and leave only a direct OpReturn. However, it will not remove %main_fs = OpFunction... or OpEntryPoint Fragment %main_fs "main_fs" .... In the SPIR-V module generated by the compiler, this fragment shader still exists—it's just that it no longer writes to the Output variable.
Therefore:
The phenomenon in Issue Fragment shader can be silently compiled out #284 where "the entire fragment shader is silently compiled out" is actually a misunderstanding of the behavior "function body is simplified to not write outputs" ⇢ "no color is written."
The initially proposed "silent compile-out" didn’t actually happen: The SPIR-V still contains empty functions and entry-point declarations; it’s just that nothing is done during the rendering phase.
Solution
Thus, the solution is to add a check/error for fragment shaders with "empty outputs" so that users can immediately know: "Hey, this fragment shader doesn't write anything to output, it’s definitely a bug," instead of generating an invalid SPIR-V that "renders nothing."
Therefore, I added a validation step after DCE to check for fragment shaders with no output operations:
validate_fragment_shader_outputs()
: The main validation function called after DCE.fragment_shader_writes_output()
: Recursively checks for output operations.has_partial_output_writes()
: Detects evidence of partial writes that were optimized away.is_output_storage_variable()
: Identifies output variables in different scopes.Example Error Message
A test case that always fails was added to verify that the silent failure would trigger an error message:
tests/compiletests/ui/dis/issue-284-dead-fragment.rs
: A fragment shader with no output (correctly fails and provides a clear error).Before the fix (silent failure): The shader compiled successfully but rendered nothing.
After the fix (clear error and guidance):
Since the validation only runs after DCE, doesn't affect the hot compilation path, and only checks fragment shaders (not vertex/compute shaders), it has a minimal performance impact.