Skip to content

Commit

Permalink
tests: Add inline uniform buffer for GPU-AV
Browse files Browse the repository at this point in the history
  • Loading branch information
spencer-lunarg committed Nov 11, 2024
1 parent e95a8c7 commit cbdfb0e
Show file tree
Hide file tree
Showing 3 changed files with 221 additions and 4 deletions.
15 changes: 13 additions & 2 deletions layers/core_checks/cc_descriptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
* limitations under the License.
*/

#include <sstream>
#include <valarray>

#include "containers/custom_containers.h"
Expand Down Expand Up @@ -1896,14 +1897,24 @@ bool CoreChecks::ValidateWriteUpdate(const DescriptorSet &dst_set, const VkWrite
if (current_binding->count > 0) {
// Check for consistent stageFlags and descriptorType
if ((current_binding->stage_flags != stage_flags) || (current_binding->type != descriptor_type)) {
std::stringstream extra;
// If using inline, easy to go outside of its range and not realize you are in the next descriptor
if (descriptor_type == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) {
extra << " For inline uniforms blocks, you might have your VkWriteDescriptorSet::dstArrayElement ("
<< update.dstArrayElement << ") plus VkWriteDescriptorSet::descriptorCount ("
<< update.descriptorCount
<< ") larger than your VkDescriptorSetLayoutBinding::descriptorCount so this is trying to update the "
"next binding.";
}

skip |= LogError(
"VUID-VkWriteDescriptorSet-descriptorCount-00317", objlist, write_loc,
"binding #%" PRIu32 " (started on dstBinding [%" PRIu32 "] plus %" PRIu32
" descriptors offset) has stageFlags of %s and descriptorType of %s, but previous binding was %s and %s.",
" descriptors offset) has stageFlags of %s and descriptorType of %s, but previous binding was %s and %s.%s",
current_binding->binding, update.dstBinding, i,
string_VkShaderStageFlags(current_binding->stage_flags).c_str(),
string_VkDescriptorType(current_binding->type), string_VkShaderStageFlags(stage_flags).c_str(),
string_VkDescriptorType(descriptor_type));
string_VkDescriptorType(descriptor_type), extra.str().c_str());
}
// Check if all immutableSamplers or not
if (current_binding->has_immutable_samplers != immutable_samplers) {
Expand Down
68 changes: 68 additions & 0 deletions tests/unit/debug_printf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4168,3 +4168,71 @@ TEST_F(NegativeDebugPrintf, SpecConstant) {
m_default_queue->Wait();
m_errorMonitor->VerifyFound();
}

TEST_F(NegativeDebugPrintf, InlineUniformBlock) {
AddRequiredExtensions(VK_EXT_INLINE_UNIFORM_BLOCK_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::inlineUniformBlock);
RETURN_IF_SKIP(InitDebugPrintfFramework());
RETURN_IF_SKIP(InitState());

char const *shader_source = R"glsl(
#version 450
#extension GL_EXT_debug_printf : enable
layout(set = 0, binding = 0) uniform UBO0 { uint ubo_0; };
layout(set = 0, binding = 1) uniform InlineUBO { uint dummy; uint inline_value; };
layout(set = 0, binding = 2) uniform UBO2 { uint ubo_2; };
void main() {
debugPrintfEXT("binding [0] = %u | [1] = %u | [2] = %u", ubo_0, inline_value, ubo_2);
}
)glsl";

vkt::Buffer buffer(*m_device, 16, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, kHostVisibleMemProps);
VkDeviceAddress *buffer_ptr = (VkDeviceAddress *)buffer.Memory().Map();
buffer_ptr[0] = 3;
buffer.Memory().Unmap();

VkDescriptorPoolInlineUniformBlockCreateInfo pool_inline_info = vku::InitStructHelper();
pool_inline_info.maxInlineUniformBlockBindings = 1;

OneOffDescriptorSet descriptor_set(m_device,
{
{0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr},
{1, VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT, 8, VK_SHADER_STAGE_ALL, nullptr},
{2, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr},
},
0, nullptr, 0, nullptr, &pool_inline_info);
const vkt::PipelineLayout pipeline_layout(*m_device, {&descriptor_set.layout_});
descriptor_set.WriteDescriptorBufferInfo(0, buffer.handle(), 0, VK_WHOLE_SIZE);
descriptor_set.WriteDescriptorBufferInfo(2, buffer.handle(), 0, VK_WHOLE_SIZE);
descriptor_set.UpdateDescriptorSets();

const uint32_t print_value = 5;
VkWriteDescriptorSetInlineUniformBlockEXT write_inline_uniform = vku::InitStructHelper();
write_inline_uniform.dataSize = 4;
write_inline_uniform.pData = &print_value;

VkWriteDescriptorSet descriptor_writes = vku::InitStructHelper(&write_inline_uniform);
descriptor_writes.dstSet = descriptor_set.set_;
descriptor_writes.dstBinding = 1;
descriptor_writes.dstArrayElement = 4; // offset 4 bytes
descriptor_writes.descriptorCount = 4; // Write 4 bytes
descriptor_writes.descriptorType = VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT;
vk::UpdateDescriptorSets(device(), 1, &descriptor_writes, 0, nullptr);

CreateComputePipelineHelper pipe(*this);
pipe.cs_ = std::make_unique<VkShaderObj>(this, shader_source, VK_SHADER_STAGE_COMPUTE_BIT);
pipe.cp_ci_.layout = pipeline_layout.handle();
pipe.CreateComputePipeline();

m_command_buffer.Begin();
vk::CmdBindPipeline(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_COMPUTE, pipe.Handle());
vk::CmdBindDescriptorSets(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_COMPUTE, pipeline_layout.handle(), 0, 1,
&descriptor_set.set_, 0, nullptr);
vk::CmdDispatch(m_command_buffer.handle(), 1, 1, 1);
m_command_buffer.End();

m_errorMonitor->SetDesiredInfo("binding [0] = 3 | [1] = 5 | [2] = 3");
m_default_queue->Submit(m_command_buffer);
m_default_queue->Wait();
m_errorMonitor->VerifyFound();
}
142 changes: 140 additions & 2 deletions tests/unit/gpu_av_positive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,144 @@ TEST_F(PositiveGpuAV, InlineUniformBlockAndRecovery) {
}
}

// TODO - This test "could" trigger a warning, but we would have to allocate 4k buffer and track each DWORD updated/written to it,
// so currently we just make sure we don't generate false positives.
TEST_F(PositiveGpuAV, InlineUniformBlockUninitialized) {
TEST_DESCRIPTION(
"If you don't update an inline uniform block and accessed it, you get undefined values, but it is not invalid and we "
"should not crash or trigger an error.");
AddRequiredExtensions(VK_EXT_INLINE_UNIFORM_BLOCK_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::inlineUniformBlock);
RETURN_IF_SKIP(InitGpuAvFramework());
RETURN_IF_SKIP(InitState());

char const *shader_source = R"glsl(
#version 450
layout(set = 0, binding = 0) buffer SSBO { uint out_buffer; };
layout(set = 0, binding = 1) uniform InlineUBO {
uint updated_value;
uint non_updated_value; // will be undefined value
};
void main() {
out_buffer = non_updated_value;
}
)glsl";

vkt::Buffer buffer(*m_device, 16, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);

VkDescriptorPoolInlineUniformBlockCreateInfo pool_inline_info = vku::InitStructHelper();
pool_inline_info.maxInlineUniformBlockBindings = 1;

OneOffDescriptorSet descriptor_set(m_device,
{
{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr},
{1, VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT, 8, VK_SHADER_STAGE_ALL, nullptr},
},
0, nullptr, 0, nullptr, &pool_inline_info);
const vkt::PipelineLayout pipeline_layout(*m_device, {&descriptor_set.layout_});
descriptor_set.WriteDescriptorBufferInfo(0, buffer.handle(), 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
descriptor_set.UpdateDescriptorSets();

const uint32_t update_value = 0;
VkWriteDescriptorSetInlineUniformBlockEXT write_inline_uniform = vku::InitStructHelper();
write_inline_uniform.dataSize = 4;
write_inline_uniform.pData = &update_value;
VkWriteDescriptorSet descriptor_writes = vku::InitStructHelper(&write_inline_uniform);
descriptor_writes.dstSet = descriptor_set.set_;
descriptor_writes.dstBinding = 1;
descriptor_writes.dstArrayElement = 0;
descriptor_writes.descriptorCount = 4;
descriptor_writes.descriptorType = VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT;
vk::UpdateDescriptorSets(device(), 1, &descriptor_writes, 0, nullptr);

CreateComputePipelineHelper pipe(*this);
pipe.cs_ = std::make_unique<VkShaderObj>(this, shader_source, VK_SHADER_STAGE_COMPUTE_BIT);
pipe.cp_ci_.layout = pipeline_layout.handle();
pipe.CreateComputePipeline();

m_command_buffer.Begin();
vk::CmdBindPipeline(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_COMPUTE, pipe.Handle());
vk::CmdBindDescriptorSets(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_COMPUTE, pipeline_layout.handle(), 0, 1,
&descriptor_set.set_, 0, nullptr);
vk::CmdDispatch(m_command_buffer.handle(), 1, 1, 1);
m_command_buffer.End();

m_default_queue->Submit(m_command_buffer);
m_default_queue->Wait();
}

TEST_F(PositiveGpuAV, InlineUniformBlockUninitializedUpdateAfterBind) {
TEST_DESCRIPTION(
"If you don't update an inline uniform block and accessed it, you get undefined values, but it is not invalid and we "
"should not crash or trigger an error.");
AddRequiredExtensions(VK_EXT_INLINE_UNIFORM_BLOCK_EXTENSION_NAME);
AddRequiredExtensions(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME);
AddRequiredFeature(vkt::Feature::inlineUniformBlock);
AddRequiredFeature(vkt::Feature::descriptorBindingInlineUniformBlockUpdateAfterBind);
RETURN_IF_SKIP(InitGpuAvFramework());
RETURN_IF_SKIP(InitState());

char const *shader_source = R"glsl(
#version 450
layout(set = 0, binding = 0) buffer SSBO { uint out_buffer; };
layout(set = 0, binding = 1) uniform InlineUBO {
uint updated_value;
uint non_updated_value; // will be undefined value
};
void main() {
out_buffer = non_updated_value;
}
)glsl";

vkt::Buffer buffer(*m_device, 16, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);

VkDescriptorPoolInlineUniformBlockCreateInfo pool_inline_info = vku::InitStructHelper();
pool_inline_info.maxInlineUniformBlockBindings = 1;

VkDescriptorBindingFlags ds_binding_flags[2] = {0, VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT};
VkDescriptorSetLayoutBindingFlagsCreateInfo flags_create_info = vku::InitStructHelper();
flags_create_info.bindingCount = 2;
flags_create_info.pBindingFlags = ds_binding_flags;

OneOffDescriptorSet descriptor_set(m_device,
{
{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr},
{1, VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT, 8, VK_SHADER_STAGE_ALL, nullptr},
},
VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT_EXT, &flags_create_info,
VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT_EXT, nullptr, &pool_inline_info);
const vkt::PipelineLayout pipeline_layout(*m_device, {&descriptor_set.layout_});
descriptor_set.WriteDescriptorBufferInfo(0, buffer.handle(), 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
descriptor_set.UpdateDescriptorSets();

CreateComputePipelineHelper pipe(*this);
pipe.cs_ = std::make_unique<VkShaderObj>(this, shader_source, VK_SHADER_STAGE_COMPUTE_BIT);
pipe.cp_ci_.layout = pipeline_layout.handle();
pipe.CreateComputePipeline();

m_command_buffer.Begin();
vk::CmdBindPipeline(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_COMPUTE, pipe.Handle());
vk::CmdBindDescriptorSets(m_command_buffer.handle(), VK_PIPELINE_BIND_POINT_COMPUTE, pipeline_layout.handle(), 0, 1,
&descriptor_set.set_, 0, nullptr);
vk::CmdDispatch(m_command_buffer.handle(), 1, 1, 1);
m_command_buffer.End();

const uint32_t update_value = 0;
VkWriteDescriptorSetInlineUniformBlockEXT write_inline_uniform = vku::InitStructHelper();
write_inline_uniform.dataSize = 4;
write_inline_uniform.pData = &update_value;
VkWriteDescriptorSet descriptor_writes = vku::InitStructHelper(&write_inline_uniform);
descriptor_writes.dstSet = descriptor_set.set_;
descriptor_writes.dstBinding = 1;
descriptor_writes.dstArrayElement = 0;
descriptor_writes.descriptorCount = 4;
descriptor_writes.descriptorType = VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT;
vk::UpdateDescriptorSets(device(), 1, &descriptor_writes, 0, nullptr);

m_default_queue->Submit(m_command_buffer);
m_default_queue->Wait();
}

TEST_F(PositiveGpuAV, SetSSBOBindDescriptor) {
TEST_DESCRIPTION("Makes sure we can use vkCmdBindDescriptorSets()");
RETURN_IF_SKIP(InitGpuAvFramework());
Expand Down Expand Up @@ -1825,9 +1963,9 @@ TEST_F(PositiveGpuAV, SharedPipelineLayoutSubsetWithUnboundDescriptorSet) {

char const *cs_source = R"glsl(
#version 450
layout(set = 0, binding = 0) buffer foo_0 {
layout(set = 0, binding = 0) buffer foo_0 {
int a;
int b;
int b;
};
void main() {
a = b;
Expand Down

0 comments on commit cbdfb0e

Please sign in to comment.