blob: 8b1234fb7673aade8a121cc2ab8f68636e0fcf99 [file] [log] [blame]
/* Copyright (c) 2024-2025 The Khronos Group Inc.
* Copyright (c) 2024-2025 Valve Corporation
* Copyright (c) 2024-2025 LunarG, Inc.
*
* 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
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "../framework/layer_validation_tests.h"
#include "../framework/pipeline_helper.h"
#include "../framework/descriptor_helper.h"
class PositiveGpuAVSpirv : public GpuAVTest {};
TEST_F(PositiveGpuAVSpirv, LoopPhi) {
TEST_DESCRIPTION("Loop that has the Phi parent pointed to itself");
RETURN_IF_SKIP(InitGpuAvFramework());
RETURN_IF_SKIP(InitState());
InitRenderTarget();
vkt::Buffer buffer_uniform(*m_device, 1024, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, kHostVisibleMemProps);
vkt::Buffer buffer_storage(*m_device, 1024, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, kHostVisibleMemProps);
uint32_t *data = (uint32_t *)buffer_uniform.Memory().Map();
data[0] = 4; // Scene.lightCount
OneOffDescriptorSet descriptor_set(m_device, {
{0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr},
{1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr},
});
const vkt::PipelineLayout pipeline_layout(*m_device, {&descriptor_set.layout_});
descriptor_set.WriteDescriptorBufferInfo(0, buffer_uniform, 0, VK_WHOLE_SIZE);
descriptor_set.WriteDescriptorBufferInfo(1, buffer_storage, 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
descriptor_set.UpdateDescriptorSets();
// compiled with
// dxc -spirv -T ps_6_0 -E psmain -fspv-target-env=vulkan1.1
//
// struct SceneData {
// uint lightCount;
// };
// struct Light {
// float3 position;
// };
// ConstantBuffer<SceneData> Scene : register(b0, space0);
// StructuredBuffer<Light> Lights : register(t1, space0);
//
// float4 main(float4 Position : SV_POSITION, float2 TexCoord : TEXCOORD) : SV_TARGET {
// float3 color = (float3)0;
// for (uint i = 0; i < Scene.lightCount; ++i) {
// color += normalize(Lights[i].position);
// }
// return float4(color, 1.0);
// }
//
// Produces this nasty loop pattern where it seems at first glance the Phi 2nd Parent is actually after it
//
// %1 = OpLabel
// OpBranch %2
//
// %2 = OpLabel
// %phi = OpPhi %int %A %1 %B %3
// OpLoopMerge %4 %3 None
// OpBranchConditional %bool %3 %4
//
// %3 = OpLabel
// %B = OpIAdd %int %_ %_
// OpBranch %2
//
// %4 = OpLabel
const char *fs_source = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %psmain "main" %out_var_SV_TARGET
OpExecutionMode %psmain OriginUpperLeft
OpDecorate %out_var_SV_TARGET Location 0
OpDecorate %Scene DescriptorSet 0
OpDecorate %Scene Binding 0
OpDecorate %Lights DescriptorSet 0
OpDecorate %Lights Binding 1
OpMemberDecorate %type_ConstantBuffer_SceneData 0 Offset 0
OpDecorate %type_ConstantBuffer_SceneData Block
OpMemberDecorate %Light 0 Offset 0
OpDecorate %_runtimearr_Light ArrayStride 16
OpMemberDecorate %type_StructuredBuffer_Light 0 Offset 0
OpMemberDecorate %type_StructuredBuffer_Light 0 NonWritable
OpDecorate %type_StructuredBuffer_Light BufferBlock
%float = OpTypeFloat 32
%float_0 = OpConstant %float 0
%v3float = OpTypeVector %float 3
%13 = OpConstantComposite %v3float %float_0 %float_0 %float_0
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%uint_1 = OpConstant %uint 1
%float_1 = OpConstant %float 1
%type_ConstantBuffer_SceneData = OpTypeStruct %uint
%_ptr_Uniform_type_ConstantBuffer_SceneData = OpTypePointer Uniform %type_ConstantBuffer_SceneData
%Light = OpTypeStruct %v3float
%_runtimearr_Light = OpTypeRuntimeArray %Light
%type_StructuredBuffer_Light = OpTypeStruct %_runtimearr_Light
%_ptr_Uniform_type_StructuredBuffer_Light = OpTypePointer Uniform %type_StructuredBuffer_Light
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%void = OpTypeVoid
%25 = OpTypeFunction %void
%_ptr_Uniform_uint = OpTypePointer Uniform %uint
%bool = OpTypeBool
%_ptr_Uniform_v3float = OpTypePointer Uniform %v3float
%Scene = OpVariable %_ptr_Uniform_type_ConstantBuffer_SceneData Uniform
%Lights = OpVariable %_ptr_Uniform_type_StructuredBuffer_Light Uniform
%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output
%psmain = OpFunction %void None %25
%29 = OpLabel
OpBranch %30
%30 = OpLabel
%31 = OpPhi %v3float %13 %29 %32 %33
%34 = OpPhi %uint %uint_0 %29 %35 %33
%36 = OpAccessChain %_ptr_Uniform_uint %Scene %int_0
%37 = OpLoad %uint %36
%38 = OpULessThan %bool %34 %37
OpLoopMerge %39 %33 None
OpBranchConditional %38 %33 %39
%33 = OpLabel
%40 = OpAccessChain %_ptr_Uniform_v3float %Lights %int_0 %34 %int_0
%41 = OpLoad %v3float %40
%42 = OpExtInst %v3float %1 Normalize %41
%32 = OpFAdd %v3float %31 %42
%35 = OpIAdd %uint %34 %uint_1
OpBranch %30
%39 = OpLabel
%43 = OpCompositeExtract %float %31 0
%44 = OpCompositeExtract %float %31 1
%45 = OpCompositeExtract %float %31 2
%46 = OpCompositeConstruct %v4float %43 %44 %45 %float_1
OpStore %out_var_SV_TARGET %46
OpReturn
OpFunctionEnd
)";
VkShaderObj fs(*m_device, fs_source, VK_SHADER_STAGE_FRAGMENT_BIT, SPV_ENV_VULKAN_1_0, SPV_SOURCE_ASM);
CreatePipelineHelper pipe(*this);
pipe.shader_stages_ = {pipe.vs_->GetStageCreateInfo(), fs.GetStageCreateInfo()};
pipe.gp_ci_.layout = pipeline_layout;
pipe.CreateGraphicsPipeline();
m_command_buffer.Begin();
m_command_buffer.BeginRenderPass(m_renderPassBeginInfo);
vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe);
vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_set.set_, 0,
nullptr);
vk::CmdDraw(m_command_buffer, 3, 1, 0, 0);
vk::CmdEndRenderPass(m_command_buffer);
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
}
TEST_F(PositiveGpuAVSpirv, LoopHeaderPhi) {
TEST_DESCRIPTION("Require injection in the Loop Header block that contains a Phi");
SetTargetApiVersion(VK_API_VERSION_1_2);
RETURN_IF_SKIP(InitGpuAvFramework());
RETURN_IF_SKIP(InitState());
// The folling HLSL:
//
// RWStructuredBuffer<int> data : register(u0);
// [numthreads(1, 1, 1)]
// void main() {
// int i = 0;
// for (i = 0; i < data[i]; i++) {
// data[0] += i;
// }
// }
const char *cs_source = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main" %data
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %data DescriptorSet 0
OpDecorate %data Binding 0
OpDecorate %runtimearr ArrayStride 4
OpMemberDecorate %type_RWStructuredBuffer 0 Offset 0
OpDecorate %type_RWStructuredBuffer Block
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%int_1 = OpConstant %int 1
%runtimearr = OpTypeRuntimeArray %int
%type_RWStructuredBuffer = OpTypeStruct %runtimearr
%ptr_RWStructuredBuffer = OpTypePointer StorageBuffer %type_RWStructuredBuffer
%void = OpTypeVoid
%12 = OpTypeFunction %void
%ptr_StorageBuffer = OpTypePointer StorageBuffer %int
%bool = OpTypeBool
%data = OpVariable %ptr_RWStructuredBuffer StorageBuffer
%main = OpFunction %void None %12
%15 = OpLabel
OpBranch %16
%16 = OpLabel
%17 = OpPhi %int %int_0 %15 %18 %19
%20 = OpBitcast %uint %17
%21 = OpAccessChain %ptr_StorageBuffer %data %int_0 %20
%22 = OpLoad %int %21
%23 = OpSLessThan %bool %17 %22
OpLoopMerge %24 %19 None
OpBranchConditional %23 %19 %24
%19 = OpLabel
%25 = OpAccessChain %ptr_StorageBuffer %data %int_0 %uint_0
%26 = OpLoad %int %25
%27 = OpIAdd %int %26 %17
OpStore %25 %27
%18 = OpIAdd %int %17 %int_1
OpBranch %16
%24 = OpLabel
OpReturn
OpFunctionEnd
)";
CreateComputePipelineHelper pipe(*this);
pipe.dsl_bindings_[0] = {0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr};
pipe.cs_ = VkShaderObj(*m_device, cs_source, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_2, SPV_SOURCE_ASM);
pipe.CreateComputePipeline();
vkt::Buffer buffer(*m_device, 16, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, kHostVisibleMemProps);
uint32_t *data = (uint32_t *)buffer.Memory().Map();
data[0] = 1; // data[0]
data[1] = 2; // data[1]
data[2] = 3; // data[2]
data[3] = 0; // data[3]
pipe.descriptor_set_.WriteDescriptorBufferInfo(0, buffer, 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
pipe.descriptor_set_.UpdateDescriptorSets();
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,
&pipe.descriptor_set_.set_, 0, nullptr);
vk::CmdDispatch(m_command_buffer, 1, 1, 1);
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
ASSERT_EQ(4u, data[0]);
}
TEST_F(PositiveGpuAVSpirv, VulkanMemoryModelDeviceScope) {
TEST_DESCRIPTION("Test adding VulkanMemoryModelDeviceScope support");
SetTargetApiVersion(VK_API_VERSION_1_2);
AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
AddRequiredFeature(vkt::Feature::vulkanMemoryModel);
AddRequiredFeature(vkt::Feature::vulkanMemoryModelDeviceScope);
RETURN_IF_SKIP(InitGpuAvFramework());
RETURN_IF_SKIP(InitState());
InitRenderTarget();
vkt::Buffer buffer(*m_device, 256, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, kHostVisibleMemProps);
uint32_t *data = (uint32_t *)buffer.Memory().Map();
data[0] = 1;
OneOffDescriptorSet descriptor_set(m_device, {
{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr},
});
const vkt::PipelineLayout pipeline_layout(*m_device, {&descriptor_set.layout_});
descriptor_set.WriteDescriptorBufferInfo(0, buffer, 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
descriptor_set.UpdateDescriptorSets();
// Simple shader with added MemoryModel capability
//
// layout(set=0, binding=0) buffer InOut {
// uint x;
// uint bar[8];
// } foo;
// void main() {
// foo.bar[0] = foo.bar[foo.x];
// }
const char *cs_source = R"(
OpCapability Shader
OpCapability VulkanMemoryModel
OpCapability PhysicalStorageBufferAddresses
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel PhysicalStorageBuffer64 Vulkan
OpEntryPoint GLCompute %main "main" %foo
OpExecutionMode %main LocalSize 1 1 1
OpDecorate %_arr_uint_uint_8 ArrayStride 4
OpMemberDecorate %InOut 0 Offset 0
OpMemberDecorate %InOut 1 Offset 4
OpDecorate %InOut Block
OpDecorate %foo DescriptorSet 0
OpDecorate %foo Binding 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%uint_8 = OpConstant %uint 8
%_arr_uint_uint_8 = OpTypeArray %uint %uint_8
%InOut = OpTypeStruct %uint %_arr_uint_uint_8
%_ptr_StorageBuffer_InOut = OpTypePointer StorageBuffer %InOut
%foo = OpVariable %_ptr_StorageBuffer_InOut StorageBuffer
%int = OpTypeInt 32 1
%int_1 = OpConstant %int 1
%int_0 = OpConstant %int 0
%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
%main = OpFunction %void None %3
%5 = OpLabel
%16 = OpAccessChain %_ptr_StorageBuffer_uint %foo %int_0
%17 = OpLoad %uint %16
%18 = OpAccessChain %_ptr_StorageBuffer_uint %foo %int_1 %17
%19 = OpLoad %uint %18
%20 = OpAccessChain %_ptr_StorageBuffer_uint %foo %int_1 %int_0
OpStore %20 %19
OpReturn
OpFunctionEnd
)";
CreateComputePipelineHelper pipe(*this);
pipe.cs_ = VkShaderObj(*m_device, cs_source, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_2, SPV_SOURCE_ASM);
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);
vk::CmdDispatch(m_command_buffer, 1, 1, 1);
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
}
TEST_F(PositiveGpuAVSpirv, FindMultipleStores) {
TEST_DESCRIPTION("Catches bug when various OpStore are in top of a function");
RETURN_IF_SKIP(InitGpuAvFramework());
RETURN_IF_SKIP(InitState());
const char shader_source[] = R"glsl(
#version 450
layout(set = 0, binding = 0) buffer StorageBuffer { uint data[]; } Data; // data[4]
int foo() {
return (gl_WorkGroupSize.x > 1) ? 1 : 0;
}
void main() {
int index = foo(); // first OpStore is not instrumented
Data.data[index] = 0xdeadca71;
Data.data[index + 1] = 0xdeadca71;
}
)glsl";
CreateComputePipelineHelper pipe(*this);
pipe.dsl_bindings_[0] = {0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr};
pipe.cs_ = VkShaderObj(*m_device, shader_source, VK_SHADER_STAGE_COMPUTE_BIT);
pipe.CreateComputePipeline();
vkt::Buffer write_buffer(*m_device, 64, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, kHostVisibleMemProps);
pipe.descriptor_set_.WriteDescriptorBufferInfo(0, write_buffer, 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
pipe.descriptor_set_.UpdateDescriptorSets();
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,
&pipe.descriptor_set_.set_, 0, nullptr);
vk::CmdDispatch(m_command_buffer, 1, 1, 1);
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
}