-
Notifications
You must be signed in to change notification settings - Fork 170
Add ConvertUBOToPushConstants and corresponding tests #737
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
Add ConvertUBOToPushConstants and corresponding tests #737
Conversation
TheMostDiligent
left a comment
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 is the critical part of the push constants implementation, and is very complicated, so it needs to be very thoroughly tested.
I suggest two tests:
First, add tests to SPIRVShaderResourcesTest:
- Compile the shader with GLSLang/DXC
- Patch UBO to push constants
- Create
SPIRVShaderResourcesand verify the push constants block
This, however, is not enough as the byte code may still be incorrect. So the second test will actually use the patched shader to draw an image:
- The test will use Vulkan directly (since we are testing low-level byte code patching logic) and should be added to Vulkan-specific folder
- Similar to other tests, it will render the reference two-triangle image
- The shader with UBO will be compiled to SPIRV and patched
- Try to cover as many possible paths in the patching logic as possible (e.g. name refers to a variable or a struct, nested structs, propagate storage class, etc.)
- The PSO will be created in native Vulkan and used to render test image
- Test image will be compared to the reference
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.
You should set DILIGENT_NO_FORMAT_VALIDATION CMake option to NO. This will add format validation targets to the build. The build will fail if there any formatting issues.
will do in the next pr, once this get merged. |
TheMostDiligent
left a comment
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.
Can you add a few more tests that test more paths through the patching logic?
I don't know what exactly is need, looks like maybe nested structs?
| namespace SPIRVToolsUtil | ||
| { |
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'd suggest removing this namespace, and adding SpvOptimizerMessageConsumer and SpvTargetEnvFromSPIRV to the SPIRVTools.hpp
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.
SpvOptimizerMessageConsumer and SpvTargetEnvFromSPIRV should be internally used in SPIRVTools.cpp/ConvertUBOToPushConstant.cpp and not be exposed to other compile units where SPIRVTools.hpp might be included.
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.
Btw adding SpvOptimizerMessageConsumer and SpvTargetEnvFromSPIRV to the SPIRVTools.hpp leads to unused function warning on gcc
Tests/DiligentCoreTest/src/ShaderTools/SPIRVShaderResourcesTest.cpp
Outdated
Show resolved
Hide resolved
|
Can you please check these AI review points: Overall, this pass is conceptually correct and aligns well with how SPIRV-Tools handles storage-class fixes internally. It correctly:
That said, there are several correctness and robustness issues that should be addressed before relying on this pass long-term. ✅ What Looks Correct
Potential issuesIssue 1: Analyses/managers may be stale after mutating IRThis pass changes the module in ways that invalidate cached analyses:
However, after these mutations the code continues using cached managers ( Why this matters
Suggested fix // After changing OpVariable and pointer types:
context()->InvalidateAnalyses(spvtools::opt::IRContext::kAnalysisAll);
// Reacquire managers if you store pointers/references to them
auto* def_use = context()->get_def_use_mgr();
auto* type_mgr = context()->get_type_mgr();
auto* deco_mgr = context()->get_decoration_mgr();At minimum, invalidate the analyses that are relied upon later in the pass (def-use, type, decoration), but Issue 2: Reordering the newly created pointer type is fragile and unnecessaryThe pass attempts to manually adjust type ordering by moving the newly created pointer type instruction using InsertAfter(pointee_type_inst). This is risky for several reasons:
Suggested fix:
In most cases, relying on SPIRV-Tools’ own type creation and placement behavior is the most stable and maintainable approach. Issue 3: OpName-based lookup is ambiguous and may target the wrong variableThe pass identifies the target UBO by stopping at the first OpName instruction whose string matches the provided block name. This approach is inherently ambiguous and can select the wrong object. Why this is problematic:
As a result, the pass may convert the wrong variable or type, leading to invalid or unintended SPIR-V output. Suggested fix:
Using semantic properties (storage class and decorations) rather than debug names will make the pass significantly more robust and predictable Issue 4: Decoration removal is incomplete and may leave invalid metadataThe pass removes Binding and DescriptorSet decorations from the converted variable, which is required when switching a UBO to a push constant. However, this may not be sufficient in all cases. Why this can be a problem:
Suggested fix:
This will make the transformation safer and easier to reason about when integrating shaders from different toolchains. Issue 5: Pointer-type propagation is not comprehensive and may miss valid pointer flowsThe pass updates the storage class for pointer-typed results produced by a limited set of opcodes (for example, access chains, phi/select, and copy-object). This covers common compiler output, but it is not guaranteed to cover all valid pointer flows that can occur in SPIR-V, especially across different frontends, optimization levels, or extensions. Why this can be a problem:
Suggested fix:
This reduces the risk of “works for my shaders” behavior and makes the pass more resilient to future SPIR-V patterns. |
reordering logic has been removed. |
addressed by looping through all resources with matched OpName(s) and applying block storage class verification & decoration verification on each resource: for (uint32_t named_id : candidate_ids)
{
//......
//Only resource with StorageClass::Uniform matched
if (storage_class != spv::StorageClass::Uniform)
continue;
//Only resource with BlockDecoration matched
if (HasBlockDecoration(pointee_type_id))btw it's not legit to have two resources with same resource name but different resource type in the global scope in HLSL/GLSL. not really have to worry about "Struct types and variables commonly share the same debug name." |
No need to fix:
|
addressed by expanding opcode coverage and adding defensive assertions:
The current impl now follows the pattern used in |
nested structs test added: // Test for nested constant buffers
struct InnerConstant_t
{
float4 g_Color;
};
struct InnerConstant2_t
{
float4 g_MaterialParams;
};
struct MergedConstant_t
{
InnerConstant_t InnerConstant;
InnerConstant2_t InnerConstant2;
};
cbuffer CB3
{
MergedConstant_t g_MergedConstant;
};
cbuffer CB4
{
InnerConstant_t g_InnerConstant;
InnerConstant2_t g_InnerConstant2;
}; |
|
Do the tests cover all paths through |
dym also with failure paths ? |
|
For a missing uniform - yes, would be nice to have. |
Additional tests with |
03287fc to
e256723
Compare
…OToPushConstants.cpp" 2. rename "PatchSPIRVConvertUniformBufferToPushConstant" -> "ConvertUBOToPushConstants"
…essageConsumer': undeclared identifier
…onymous namespace)::SpvOptimizerMessageConsumer' has internal linkage but is not defined
…nvertUBOToPushConstantPass'; did you mean 'SPIRVToolsUtil::ConvertUBOToPushConstantPass'?
…PIRVTools.hpp, ConvertUBOToPushConstantPass-> move in the anonymous namespace
…raphics/ShaderTools/include/SPIRVTools.hpp:43:6: error: unused function 'SpvOptimizerMessageConsumer' [-Werror,-Wunused-function]
…ted structs included.
…oPushConstant_InvalidResourceType, so ConvertUBOToPushConstants with invalid block name, or invalid resource type error out properly.
555db66 to
029c4f7
Compare
029c4f7 to
815c5c6
Compare
aabb421
into
DiligentGraphics:inline_constants


anything else?