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

gpu: Check index buffer contents in pre-draw validation #8374

Closed

Conversation

jeremyg-lunarg
Copy link
Contributor

This involves splitting up draw.vert into separate shaders for non-indexed indirect, indexed (both indirect and direct) and mesh draw calls.

Fixes #2492

@ci-tester-lunarg
Copy link
Collaborator

CI Vulkan-ValidationLayers build queued with queue ID 232681.

@ci-tester-lunarg
Copy link
Collaborator

CI Vulkan-ValidationLayers build # 17215 running.

@jeremyg-lunarg
Copy link
Contributor Author

This has many TODOs but works well enough I want a backup just in case my computer gets hit with an EMP

@ci-tester-lunarg
Copy link
Collaborator

CI Vulkan-ValidationLayers build # 17215 failed.

@ci-tester-lunarg
Copy link
Collaborator

CI Vulkan-ValidationLayers build queued with queue ID 232715.

@ci-tester-lunarg
Copy link
Collaborator

CI Vulkan-ValidationLayers build # 17216 running.

@ci-tester-lunarg
Copy link
Collaborator

CI Vulkan-ValidationLayers build # 17216 failed.

This involves splitting up draw.vert into separate shaders for
non-indexed indirect, indexed (both indirect and direct) and mesh
draw calls.

Fixes KhronosGroup#2492
@ci-tester-lunarg
Copy link
Collaborator

CI Vulkan-ValidationLayers build queued with queue ID 235968.

@ci-tester-lunarg
Copy link
Collaborator

CI Vulkan-ValidationLayers build # 17242 running.

max_index = (size - offset - attrib.offset - attrib.size) / stride
*/
#if 0
static uint32_t MinVertexBufferLen(const vvl::CommandBuffer &cb_state, const vvl::Pipeline &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.

This was my WIP attempt to figure out the max vertex buffer length

Copy link
Contributor

Choose a reason for hiding this comment

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

Just thought about the case where there is an index buffer but no vertex buffer. I think given how you set min_vertex_count to UINT32_MAX, it is properly handled? Ideally in this case we should not even bother doing validation

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Then you can just turn off kPreDrawSelectIndexBuffer for indirect and skip the diagnostic draw for vkCmdDrawIndexed()

@ci-tester-lunarg
Copy link
Collaborator

CI Vulkan-ValidationLayers build # 17242 failed.

gpuav.InternalError(cb_state.Handle(), loc, "Unsupported indexType");
return;
}
push_constants.vertex_buffer_size = 3; // TODO vertex size calc
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is the important TODO, actually send down the max vertex buffer size that will work across all bindings

Copy link
Contributor

Choose a reason for hiding this comment

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

If I am not mistaken, multiple vertex buffers can be bound, each being an input to a different location in the shader, so we have to account for that?

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah but it seems your WIP is taking care of that!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes but the WIP is handling VK_VERTEX_INPUT_RATE_INSTANCE and the divisor. Those could be split out to a separate value. I think these can be checked on the CPU for direct calls but not for indirect.

m_commandBuffer->begin(&begin_info);
m_commandBuffer->BeginRenderPass(m_renderPassBeginInfo);
vk::CmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.Handle());
const uint32_t kNumVertices = 12;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This test makes sure the TODO gets fixed.

@jeremyg-lunarg
Copy link
Contributor Author

@arno-lunarg you may want to take a look at the latest push. I fixed up @spencer-lunarg's previous review comments

return false;
}

for (uint j = 0; j < ic; j++) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think having one GPU thread check all indices will make things really slow, I am thinking about moving the index check to a dedicated validation vertex shader, and dispatch a draw on it with a number of vertices specified on the CPU for non indirect draws, and computed on the GPU for indirect ones

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. You could just map the index buffer as a vertex buffer and have a separate thread check each index. And have the other checking only happen in thread 0. Or are you thinking about having 2 shaders?

@@ -38,13 +41,14 @@ struct SharedDrawValidationResources final {
VkDevice device = VK_NULL_HANDLE;

SharedDrawValidationResources(Validator &gpuav, VkDescriptorSetLayout error_output_desc_set_layout, bool use_shader_objects,
const Location &loc)
const Location &loc, const uint32_t *shader, uint32_t shader_size)
Copy link
Contributor

Choose a reason for hiding this comment

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

I should have added comments, but this will not work as expected x_x

Only one SharedDrawValidationResources object is created, it is then cached and retrieved when needed. So the first call to gpuav.shared_resources_manager.Get<SharedDrawValidationResources>( /*...*/, shader, shader_size); will set the validation shader, and calling it again with a different shader will be a no-op and just retrieve the cached entry......

Copy link
Contributor

Choose a reason for hiding this comment

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

The key in the cache is only based on the type of the stored object, ideally I would also use the passed in parameters to form the key, but I don't even know if that is possible

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It doesn't look like it is possible as written. If it was up to me, I'd ditch the typeid magic and just make a key enum.

if (gl_VertexIndex == 0) {
uint draw_count = 0;

if ((push_data.flags & kPreDrawSelectCountBuffer) != 0) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Validation code is now duplicated, I don't think this is ideal?
I really like how you broke up the logic in gpuav_draw.cpp, but here, the old Uber shader was not that bad I think

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The answer to this will depend on how much additional validation needs to happen in each draw type. Also how many push constants become necessary and when that starts to exceeds device limits. It seems to me that at least mesh shaders would benefit from being separate.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The other reason I split up the shaders was that there are fields with the same name but different offsets in VkDrawIndirectCommand and VkDrawIndexedIndirectCommand. And then VkDrawMeshTasksIndirectCommandEXT is completely different. The old shader code got down the offset of the firstInstance member in a push constant for whatever draw was being validated. But... I wanted to access multiple fields and not deal with the mess of managing different offsets in the same shader.

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.

GPU-AV check index buffer out-of-bounds
4 participants