blob: c286e99d55b1fb50177af2ed02ceef52b22e6279 [file]
/*
* Copyright (c) 2015-2026 The Khronos Group Inc.
* Copyright (c) 2015-2026 Valve Corporation
* Copyright (c) 2015-2026 LunarG, Inc.
* Copyright (c) 2015-2026 Google, Inc.
* Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*/
#include "../framework/layer_validation_tests.h"
#include "../framework/pipeline_helper.h"
#include "../framework/descriptor_helper.h"
class NegativeShaderStorageTexel : public VkLayerTest {};
TEST_F(NegativeShaderStorageTexel, WriteLessComponent) {
TEST_DESCRIPTION("Test writing to texel buffer with less components.");
SetTargetApiVersion(VK_API_VERSION_1_2);
RETURN_IF_SKIP(Init());
// not valid GLSL, but would look like:
// layout(set = 0, binding = 0, Rgba8ui) uniform uimageBuffer storageTexelBuffer;
// imageStore(storageTexelBuffer, 1, uvec3(1, 1, 1));
//
// Rgba8ui == 4-component but only writing 3 texels to it
const char* source = R"(
OpCapability Shader
OpCapability ImageBuffer
OpCapability StorageImageExtendedFormats
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main" %var
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %var DescriptorSet 0
OpDecorate %var Binding 0
%void = OpTypeVoid
%func = OpTypeFunction %void
%int = OpTypeInt 32 1
%uint = OpTypeInt 32 0
%image = OpTypeImage %uint Buffer 0 0 0 2 Rgba8ui
%ptr = OpTypePointer UniformConstant %image
%var = OpVariable %ptr UniformConstant
%v3uint = OpTypeVector %uint 3
%int_1 = OpConstant %int 1
%uint_1 = OpConstant %uint 1
%texelU3 = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1
%main = OpFunction %void None %func
%label = OpLabel
%load = OpLoad %image %var
OpImageWrite %load %int_1 %texelU3 ZeroExtend
OpReturn
OpFunctionEnd
)";
const VkFormat format = VK_FORMAT_R8G8B8A8_UINT; // Rgba8ui
if (!BufferFormatAndFeaturesSupported(Gpu(), format, VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT)) {
GTEST_SKIP() << "Format doesn't support storage texel buffer";
}
const auto set_info = [&](CreateComputePipelineHelper& helper) {
helper.cs_ = VkShaderObj(*m_device, source, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_2, SPV_SOURCE_ASM);
helper.dsl_bindings_[0] = {0, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr};
};
CreateComputePipelineHelper::OneshotTest(*this, set_info, kErrorBit, "VUID-RuntimeSpirv-OpImageWrite-07112");
}
TEST_F(NegativeShaderStorageTexel, UnknownWriteLessComponent) {
TEST_DESCRIPTION("Test writing to texel buffer unknown format with less components.");
SetTargetApiVersion(VK_API_VERSION_1_2);
AddRequiredExtensions(VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME);
RETURN_IF_SKIP(Init());
// not valid GLSL, but would look like:
// layout(set = 0, binding = 0, Unknown) uniform uimageBuffer storageTexelBuffer;
// imageStore(storageTexelBuffer, 1, uvec3(1, 1, 1));
//
// Unknown will become a 4-component but writing 3 texels to it
const char* source = R"(
OpCapability Shader
OpCapability ImageBuffer
OpCapability StorageImageWriteWithoutFormat
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main" %var
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %var DescriptorSet 0
OpDecorate %var Binding 0
OpDecorate %var NonReadable
%void = OpTypeVoid
%func = OpTypeFunction %void
%int = OpTypeInt 32 1
%uint = OpTypeInt 32 0
%image = OpTypeImage %uint Buffer 0 0 0 2 Unknown
%ptr = OpTypePointer UniformConstant %image
%var = OpVariable %ptr UniformConstant
%v3uint = OpTypeVector %uint 3
%v4uint = OpTypeVector %uint 4
%int_1 = OpConstant %int 1
%uint_1 = OpConstant %uint 1
%texelU3 = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1
%texelU4 = OpConstantComposite %v4uint %uint_1 %uint_1 %uint_1 %uint_1
%main = OpFunction %void None %func
%label = OpLabel
%load = OpLoad %image %var
OpImageWrite %load %int_1 %texelU4 ZeroExtend ;; Valid
OpImageWrite %load %int_1 %texelU3 ZeroExtend ;; Invalid
OpImageWrite %load %int_1 %texelU4 ZeroExtend ;; Valid
OpImageWrite %load %int_1 %texelU3 ZeroExtend ;; Invalid
OpReturn
OpFunctionEnd
)";
OneOffDescriptorSet ds(m_device, {
{0, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr},
});
const VkFormat format = VK_FORMAT_R8G8B8A8_UINT; // Rgba8ui
if (!BufferFormatAndFeaturesSupported(Gpu(), format, VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT)) {
GTEST_SKIP() << "Format doesn't support storage texel buffer";
}
if ((m_device->FormatFeaturesBuffer(format) & VK_FORMAT_FEATURE_2_STORAGE_WRITE_WITHOUT_FORMAT_BIT) == 0) {
GTEST_SKIP() << "Format doesn't support storage write without format";
}
vkt::Buffer buffer(*m_device, 1024, VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT);
vkt::BufferView buffer_view(*m_device, buffer, format);
ds.WriteDescriptorBufferView(0, buffer_view);
ds.UpdateDescriptorSets();
CreateComputePipelineHelper pipe(*this);
pipe.cs_ = VkShaderObj(*m_device, source, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_2, SPV_SOURCE_ASM);
pipe.pipeline_layout_ = vkt::PipelineLayout(*m_device, {&ds.layout_});
pipe.CreateComputePipeline();
m_command_buffer.Begin();
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe);
vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe.pipeline_layout_, 0, 1, &ds.set_, 0, nullptr);
m_errorMonitor->SetDesiredError("VUID-vkCmdDispatch-OpImageWrite-04469", 2);
vk::CmdDispatch(m_command_buffer, 1, 1, 1);
m_errorMonitor->VerifyFound();
m_command_buffer.End();
}
TEST_F(NegativeShaderStorageTexel, ComponentTypeMismatch) {
SetTargetApiVersion(VK_API_VERSION_1_2);
AddRequiredExtensions(VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME);
RETURN_IF_SKIP(Init());
const VkFormat format = VK_FORMAT_R8G8B8A8_UINT; // Rgba8ui
if (!BufferFormatAndFeaturesSupported(Gpu(), format, VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT)) {
GTEST_SKIP() << "Format doesn't support storage texel buffer";
}
if ((m_device->FormatFeaturesBuffer(format) & VK_FORMAT_FEATURE_2_STORAGE_WRITE_WITHOUT_FORMAT_BIT) == 0) {
GTEST_SKIP() << "Format doesn't support storage write without format";
}
const char* cs_source = R"glsl(
#version 450
layout(set = 0, binding = 0) writeonly uniform iimageBuffer storageTexelBuffer;
void main() {
imageStore(storageTexelBuffer, 0, ivec4(0));
}
)glsl";
vkt::Buffer buffer(*m_device, 1024, VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT);
VkBufferViewCreateInfo buff_view_ci = vku::InitStructHelper();
buff_view_ci.buffer = buffer;
buff_view_ci.format = format;
buff_view_ci.range = VK_WHOLE_SIZE;
vkt::BufferView buffer_view(*m_device, buff_view_ci);
OneOffDescriptorSet descriptor_set(m_device, {
{0, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr},
});
vkt::PipelineLayout pipeline_layout(*m_device, {&descriptor_set.layout_});
descriptor_set.WriteDescriptorBufferView(0, buffer_view);
descriptor_set.UpdateDescriptorSets();
CreateComputePipelineHelper pipe(*this);
pipe.cs_ = VkShaderObj(*m_device, cs_source, VK_SHADER_STAGE_COMPUTE_BIT);
pipe.cp_ci_.layout = pipeline_layout;
pipe.CreateComputePipeline();
m_command_buffer.Begin();
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe);
vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline_layout, 0, 1, &descriptor_set.set_, 0,
nullptr);
m_errorMonitor->SetDesiredError("VUID-vkCmdDispatch-format-07753");
vk::CmdDispatch(m_command_buffer, 1, 1, 1);
m_errorMonitor->VerifyFound();
m_command_buffer.End();
}
TEST_F(NegativeShaderStorageTexel, FormatComponentTypeMismatch) {
RETURN_IF_SKIP(Init());
const VkFormat format = VK_FORMAT_R8G8B8A8_UINT; // Rgba8ui
if (!BufferFormatAndFeaturesSupported(Gpu(), format, VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT)) {
GTEST_SKIP() << "Format doesn't support storage texel buffer";
}
const char* cs_source = R"glsl(
#version 450
layout(set = 0, binding = 0, R8ui) writeonly uniform uimageBuffer storageTexelBuffer;
void main() {
imageStore(storageTexelBuffer, 0, uvec4(0));
}
)glsl";
vkt::Buffer buffer(*m_device, 1024, VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT);
VkBufferViewCreateInfo buff_view_ci = vku::InitStructHelper();
buff_view_ci.buffer = buffer;
buff_view_ci.format = format;
buff_view_ci.range = VK_WHOLE_SIZE;
vkt::BufferView buffer_view(*m_device, buff_view_ci);
OneOffDescriptorSet descriptor_set(m_device, {
{0, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr},
});
vkt::PipelineLayout pipeline_layout(*m_device, {&descriptor_set.layout_});
descriptor_set.WriteDescriptorBufferView(0, buffer_view);
descriptor_set.UpdateDescriptorSets();
CreateComputePipelineHelper pipe(*this);
pipe.cs_ = VkShaderObj(*m_device, cs_source, VK_SHADER_STAGE_COMPUTE_BIT);
pipe.cp_ci_.layout = pipeline_layout;
pipe.CreateComputePipeline();
m_command_buffer.Begin();
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe);
vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline_layout, 0, 1, &descriptor_set.set_, 0,
nullptr);
m_errorMonitor->SetDesiredWarning("Undefined-Value-StorageImage-FormatMismatch-BufferView");
vk::CmdDispatch(m_command_buffer, 1, 1, 1);
m_errorMonitor->VerifyFound();
m_command_buffer.End();
}
TEST_F(NegativeShaderStorageTexel, FormatComponentTypeMismatch2) {
RETURN_IF_SKIP(Init());
const VkFormat format = VK_FORMAT_R8_UINT;
if (!BufferFormatAndFeaturesSupported(Gpu(), format, VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT)) {
GTEST_SKIP() << "Format doesn't support storage texel buffer";
}
const char* cs_source = R"glsl(
#version 450
layout(set = 0, binding = 0, R32ui) writeonly uniform uimageBuffer storageTexelBuffer;
void main() {
imageStore(storageTexelBuffer, 0, uvec4(0));
}
)glsl";
vkt::Buffer buffer(*m_device, 1024, VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT);
VkBufferViewCreateInfo buff_view_ci = vku::InitStructHelper();
buff_view_ci.buffer = buffer;
buff_view_ci.format = format;
buff_view_ci.range = VK_WHOLE_SIZE;
vkt::BufferView buffer_view(*m_device, buff_view_ci);
OneOffDescriptorSet descriptor_set(m_device, {
{0, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr},
});
vkt::PipelineLayout pipeline_layout(*m_device, {&descriptor_set.layout_});
descriptor_set.WriteDescriptorBufferView(0, buffer_view);
descriptor_set.UpdateDescriptorSets();
CreateComputePipelineHelper pipe(*this);
pipe.cs_ = VkShaderObj(*m_device, cs_source, VK_SHADER_STAGE_COMPUTE_BIT);
pipe.cp_ci_.layout = pipeline_layout;
pipe.CreateComputePipeline();
m_command_buffer.Begin();
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe);
vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline_layout, 0, 1, &descriptor_set.set_, 0,
nullptr);
m_errorMonitor->SetDesiredWarning("Undefined-Value-StorageImage-FormatMismatch-BufferView");
vk::CmdDispatch(m_command_buffer, 1, 1, 1);
m_errorMonitor->VerifyFound();
m_command_buffer.End();
}
TEST_F(NegativeShaderStorageTexel, MissingFormatWriteForFormat) {
TEST_DESCRIPTION("Create a shader writing a storage texel buffer without an image format");
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredExtensions(VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME);
RETURN_IF_SKIP(Init());
PFN_vkSetPhysicalDeviceFormatProperties2EXT fpvkSetPhysicalDeviceFormatProperties2EXT = nullptr;
PFN_vkGetOriginalPhysicalDeviceFormatProperties2EXT fpvkGetOriginalPhysicalDeviceFormatProperties2EXT = nullptr;
if (!LoadDeviceProfileLayer(fpvkSetPhysicalDeviceFormatProperties2EXT, fpvkGetOriginalPhysicalDeviceFormatProperties2EXT)) {
GTEST_SKIP() << "Failed to load device profile layer.";
}
const VkFormat format = VK_FORMAT_R8G8B8A8_UNORM;
VkFormatProperties3 fmt_props_3 = vku::InitStructHelper();
VkFormatProperties2 fmt_props = vku::InitStructHelper(&fmt_props_3);
// set so format can be used as a storage texel buffer, but no WITHOUT_FORMAT support
fpvkGetOriginalPhysicalDeviceFormatProperties2EXT(Gpu(), format, &fmt_props);
fmt_props.formatProperties.bufferFeatures |= VK_FORMAT_FEATURE_2_STORAGE_TEXEL_BUFFER_BIT;
fmt_props.formatProperties.bufferFeatures &= ~VK_FORMAT_FEATURE_2_STORAGE_WRITE_WITHOUT_FORMAT_BIT;
fmt_props_3.bufferFeatures |= VK_FORMAT_FEATURE_2_STORAGE_TEXEL_BUFFER_BIT;
fmt_props_3.bufferFeatures &= ~VK_FORMAT_FEATURE_2_STORAGE_WRITE_WITHOUT_FORMAT_BIT;
fpvkSetPhysicalDeviceFormatProperties2EXT(Gpu(), format, fmt_props);
const char* csSource = R"(
OpCapability Shader
OpCapability ImageBuffer
OpCapability StorageImageWriteWithoutFormat
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpSource GLSL 450
OpDecorate %img DescriptorSet 0
OpDecorate %img Binding 0
OpDecorate %img NonWritable
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%7 = OpTypeImage %float Buffer 0 0 0 2 Unknown
%_ptr_UniformConstant_7 = OpTypePointer UniformConstant %7
%img = OpVariable %_ptr_UniformConstant_7 UniformConstant
%int = OpTypeInt 32 1
%v2int = OpTypeVector %int 2
%int_0 = OpConstant %int 0
%14 = OpConstantComposite %v2int %int_0 %int_0
%v4float = OpTypeVector %float 4
%float_0 = OpConstant %float 0
%17 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
%uint = OpTypeInt 32 0
%v3uint = OpTypeVector %uint 3
%uint_1 = OpConstant %uint 1
%main = OpFunction %void None %3
%5 = OpLabel
%10 = OpLoad %7 %img
OpImageWrite %10 %14 %17
OpReturn
OpFunctionEnd
)";
OneOffDescriptorSet ds(m_device, {
{0, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr},
});
CreateComputePipelineHelper cs_pipeline(*this);
cs_pipeline.cs_ = VkShaderObj(*m_device, csSource, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_0, SPV_SOURCE_ASM);
cs_pipeline.pipeline_layout_ = vkt::PipelineLayout(*m_device, {&ds.layout_});
cs_pipeline.LateBindPipelineInfo();
cs_pipeline.cp_ci_.stage.stage = VK_SHADER_STAGE_COMPUTE_BIT; // override with wrong value
cs_pipeline.CreateComputePipeline(false); // need false to prevent late binding
vkt::Buffer buffer(*m_device, 1024, VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT);
vkt::BufferView buffer_view(*m_device, buffer, format);
if (!buffer_view.initialized()) {
// device profile layer might hide fact this is not a supported buffer view format
GTEST_SKIP() << "Device will not be able to initialize buffer view skipped";
}
ds.WriteDescriptorBufferView(0, buffer_view);
ds.UpdateDescriptorSets();
m_command_buffer.Reset();
m_command_buffer.Begin();
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, cs_pipeline);
vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, cs_pipeline.pipeline_layout_, 0, 1, &ds.set_, 0,
nullptr);
m_errorMonitor->SetDesiredError("VUID-vkCmdDispatch-OpTypeImage-07029");
vk::CmdDispatch(m_command_buffer, 1, 1, 1);
m_errorMonitor->VerifyFound();
m_command_buffer.End();
}