Skip to content

Conversation

@anishmgoyal
Copy link
Contributor

Currently, in Vulkan, we set the pipeline layout when binding the pipeline, which doesn't happen until draw time when external samplers are present. In cases like that, we push constants with the layout VK_NULL_HANDLE, which is not valid. Instead, we can wait until the pipeline layout is known, and then flush all push constants.

@anishmgoyal
Copy link
Contributor Author

}
// If we're doing bind in draw, it's possible there are some push constants that weren't
// written out because we didn't have the pipelineLayout yet. Push them now.
program->flushPushConstants(pipelineLayout);
Copy link
Contributor

Choose a reason for hiding this comment

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

this should be inside the above if, only need to do this when we bind a new pipeline

Copy link
Contributor Author

Choose a reason for hiding this comment

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

technically we probably shouldn't even have that if-statement, because it's guaranteed that condition will be true with the code written as it is today. that said, i'm happy to move this into the if block.

Copy link
Contributor

Choose a reason for hiding this comment

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

I do think that if block is still necessary, because you could have multiple draw calls for one pipeline. It's just an optomization.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ahhhhh ok - then in that case, yes, this absolutely should be in the if block.

Copy link
Contributor

Choose a reason for hiding this comment

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

sorry, thinking about it some more. I think it needs to be outside the conditional. This is also valid

bindPipeline
setPushConstant
draw
setPushConstant
draw

We need to be able do that second update of push constants.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

in the second case, we wouldn't have the push constants in a list that needs to be flushed. we're only storing values if the layout is null. in the second case, the layout should not be null. unless i'm missing something?

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah yes you're right. The mPipelinState.pipelineLayout thing. Can you add a comment to that effect above the program->flushPushConstants(pipelineLayout);? Something like, "Further push constant updates after the pipeline is bound will immediately set the constants because the pipeline is known (see VulkanProgram::writePushConstant)".

Because this logic is getting harder to follow with intertwining paths.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

added exactly that comment, sums it up fairly well.

auto const& [doBindInDraw, bundle] = mPipelineState.bindInDraw;

fvkutils::DescriptorSetMask setsWithExternalSamplers = {};
if (doBindInDraw) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

is this expected to be the common case? If not, it should be UTILS_UNLIKELY and in fact I think this should all be moved into a separate function.

Copy link
Contributor

Choose a reason for hiding this comment

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

it's common if you have externally sampled textures, otherwise will never be hit. So not sure how one would quantify that with UTILS_LIKELY/UNLIKELY

Copy link
Contributor Author

@anishmgoyal anishmgoyal Nov 21, 2025

Choose a reason for hiding this comment

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

i think this is out of scope for this change, and a much broader question

Comment on lines +289 to +290
// It's possible that we don't have the layout yet. But, we don't want to "forget"
// about the push constant! We can flush the constants later, once the layout is set.
Copy link
Collaborator

Choose a reason for hiding this comment

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

can you explain in which scenario we have or don't have a layout?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

for external samplers, in bindPipeline(), there's an early return - prior to setting the layout. push constants are then set after that, and finally, the layout is selected and bound in draw2() (called after binding push constants in the render pass).

Copy link
Collaborator

Choose a reason for hiding this comment

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

I meant, in the comment :-)


PipelineInfo* mInfo;
VkDevice mDevice = VK_NULL_HANDLE;
std::vector<PushConstantInfo> mQueuedPushConstants;
Copy link
Collaborator

Choose a reason for hiding this comment

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

should we do a "reasonable" .reserve() for this, so we avoid too many heap allocations?

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 did consider that, but it's hard to know what a reasonable number is. the driver api takes one push constant at a time, and only the frontend knows how many push constants will be pushed. it's possible that actually, only the app knows about the push constants.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Actually, we have MAX_PUSH_CONSTANT_COUNT (32). So I think we can be pragmatic here and maybe do a reserve with 8 for instance.

Copy link
Contributor

Choose a reason for hiding this comment

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

can we just use an array with the fixed MAX_PUSH_CONSTANT_COUNT size?

Currently, in Vulkan, we set the pipeline layout when binding the
pipeline, which doesn't happen until draw time when external samplers
are present. In cases like that, we push constants with the layout
VK_NULL_HANDLE, which is not valid. Instead, we can wait until the
pipeline layout is known, and then flush all push constants.
@anishmgoyal anishmgoyal force-pushed the fix_external_samplers branch from 5a5cb7e to 91a9b26 Compare November 21, 2025 18:03
Some of the logic related to external samplers is getting to be a bit
convoluted.
@anishmgoyal anishmgoyal force-pushed the fix_external_samplers branch from 8035247 to 191ed71 Compare November 21, 2025 19:12
Copy link
Contributor

@poweifeng poweifeng left a comment

Choose a reason for hiding this comment

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

lgtm

@anishmgoyal
Copy link
Contributor Author

@pixelflinger please re-review when you get a chance!

// added to a list of pending constants, so we'll flush them now.
// Further push constant updates after the pipeline is bound will immediately set
// the constants because the layout is non-null (see VulkanProgram::writePushConstant).
program->flushPushConstants(pipelineLayout);
Copy link
Contributor

Choose a reason for hiding this comment

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

On a different approach and this for me is better, is to just use the RGB layout of the pipeline to set the push constants?

I expect the pipeline layouts to be compatible as described here https://registry.khronos.org/VulkanSC/specs/1.0-extensions/html/vkspec.html#descriptorsets-compatibility

Copy link
Contributor Author

@anishmgoyal anishmgoyal Dec 15, 2025

Choose a reason for hiding this comment

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

vkCmdPushConstants takes layout as a parameter. i don't see it implied in the docs that it's sufficient to use a compatible layout instead of the actual layout that the constants are being pushed for, so i don't believe we should make that change.

while they technically might currently have the same push constant layouts, it'd be fairly flimsy. any change where, say, we intro a push constant specific to external samplers (for some reason we can't anticipate today), and we'd likely be existing in the realm of undefined behavior.

Copy link
Contributor

Choose a reason for hiding this comment

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

Youre right, it does say it have to identically layout, with all the creation parameters the same, which is not true for external samplers.

Too bad :(

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.

4 participants