From 79c9aa587bc87f52dcdfa031abe4c6177b26ddd7 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Tue, 22 Sep 2026 11:43:39 +0300 Subject: [PATCH] feat(agc): add fragment shader barycentric support --- .../Execution/src/VulkanDevice.cpp | 19 ++++- .../Graphics/include/Context.hpp | 1 + .../Graphics/include/Pipeline.hpp | 2 +- .../prx/libSceAgcDriver/Graphics/src/Draw.cpp | 2 +- .../libSceAgcDriver/Graphics/src/Pipeline.cpp | 2 +- .../Graphics/src/ShaderValidation.cpp | 36 +++++++- .../prx/libSceAgcDriver/tests/Graphics.cpp | 83 +++++++++++++++++-- 7 files changed, 130 insertions(+), 15 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp index 66e95ddb2..f58bc882f 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp @@ -60,6 +60,7 @@ struct VulkanDevice::State { std::vector spirvExtensions; bool tessellationShader = false; bool meshShader = false; + bool fragmentShaderBarycentric = false; bool depthClipControl = false; bool depthRangeUnrestricted = false; VkPhysicalDeviceMeshShaderPropertiesEXT meshLimits{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_PROPERTIES_EXT}; @@ -306,8 +307,19 @@ VulkanDevice::VulkanDevice(const PresentationWindow* window) : state(std::make_u } meshFeatures = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_EXT}; meshFeatures.meshShader = state->meshShader; + VkPhysicalDeviceFragmentShaderBarycentricFeaturesKHR barycentricFeatures{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_BARYCENTRIC_FEATURES_KHR}; + if (hasExtension(VK_KHR_FRAGMENT_SHADER_BARYCENTRIC_EXTENSION_NAME)) { + VkPhysicalDeviceFeatures2 features{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, &barycentricFeatures}; + state->InstanceFunction("vkGetPhysicalDeviceFeatures2")(selected, &features); + state->fragmentShaderBarycentric = barycentricFeatures.fragmentShaderBarycentric == VK_TRUE; + } std::vector deviceExtensions; if (window != nullptr) deviceExtensions.assign(presentationExtensions.begin(), presentationExtensions.end()); + if (state->fragmentShaderBarycentric) { + deviceExtensions.push_back(VK_KHR_FRAGMENT_SHADER_BARYCENTRIC_EXTENSION_NAME); + state->capabilities.push_back(spv::CapabilityFragmentBarycentricKHR); + state->spirvExtensions.push_back("SPV_KHR_fragment_shader_barycentric"); + } deviceExtensions.push_back(VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME); state->capabilities.push_back(spv::CapabilitySignedZeroInfNanPreserve); state->spirvExtensions.push_back("SPV_KHR_float_controls"); @@ -371,6 +383,10 @@ VulkanDevice::VulkanDevice(const PresentationWindow* window) : state(std::make_u deviceInfo.pNext = &depthClipFeatures; } byteFeatures.pNext = const_cast(deviceInfo.pNext); + if (state->fragmentShaderBarycentric) { + barycentricFeatures.pNext = byteFeatures.pNext; + byteFeatures.pNext = &barycentricFeatures; + } bdaFeatures.pNext = &byteFeatures; deviceInfo.pNext = &bdaFeatures; check(state->InstanceFunction("vkCreateDevice")(selected, &deviceInfo, nullptr, &state->device), "vkCreateDevice"); @@ -617,7 +633,8 @@ Graphics::Context VulkanDevice::graphicsContext() const { state->depthClipControl, state->depthRangeUnrestricted, true, - state->subgroup + state->subgroup, + state->fragmentShaderBarycentric }; } diff --git a/core/libs/prx/libSceAgcDriver/Graphics/include/Context.hpp b/core/libs/prx/libSceAgcDriver/Graphics/include/Context.hpp index c25270e04..bc49d28f7 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/include/Context.hpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/include/Context.hpp @@ -36,6 +36,7 @@ struct Context { bool depthRangeUnrestricted = false; bool bufferDeviceAddress = false; VkPhysicalDeviceSubgroupProperties subgroup{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES}; + bool fragmentShaderBarycentric = false; template TFunction Function(const char* name) const { diff --git a/core/libs/prx/libSceAgcDriver/Graphics/include/Pipeline.hpp b/core/libs/prx/libSceAgcDriver/Graphics/include/Pipeline.hpp index 312cdd550..32718853c 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/include/Pipeline.hpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/include/Pipeline.hpp @@ -26,7 +26,7 @@ private: }; void ValidateShaderPair(const ShaderRecompiler::RecompileResult& vertex, const ShaderRecompiler::RecompileResult& fragment); -void ValidateShaders(std::span shaders, const State& state, const VkPhysicalDeviceSubgroupProperties& subgroup); +void ValidateShaders(std::span shaders, const State& state, const VkPhysicalDeviceSubgroupProperties& subgroup, bool fragmentShaderBarycentric); } diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp index fa520ebbb..501519419 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp @@ -39,7 +39,7 @@ void DrawIndexed(const Context& context, const State& state, const Pm4::IndexedD APS5_LOG_CHARS_OUT("Index buffer range OK"); Require(!state.hasColorTarget || draw.indexAddress + indexBytes <= state.color.address || state.color.address + state.color.bytes <= draw.indexAddress, "index buffer aliases the render target"); APS5_LOG_CHARS_OUT("ValidateShaders"); - ValidateShaders(shaders, state, context.subgroup); + ValidateShaders(shaders, state, context.subgroup, context.fragmentShaderBarycentric); APS5_LOG_CHARS_OUT("ValidateShaders OK"); const auto shaderStages = PipelineStages(shaders); APS5_LOG_OUT("PipelineStages=0x%x", static_cast(shaderStages)); diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp index 272a74afb..6e45716cc 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp @@ -10,7 +10,7 @@ Pipeline::Pipeline(const Context& context, const State& state, const RenderTarge Require((target != nullptr) == state.hasColorTarget, "render target does not match decoded color state"); Require(state.renderExtent.width != 0 && state.renderExtent.height != 0 && state.renderExtent.width <= context.limits.maxFramebufferWidth && state.renderExtent.height <= context.limits.maxFramebufferHeight, "framebuffer extent exceeds device limits"); Require(state.hasColorTarget || (context.limits.framebufferNoAttachmentsSampleCounts & VK_SAMPLE_COUNT_1_BIT) != 0, "device does not support single-sample rendering without attachments"); - ValidateShaders(shaders, state, context.subgroup); + ValidateShaders(shaders, state, context.subgroup, context.fragmentShaderBarycentric); Require(!state.negativeOneToOne || context.depthClipControl, "negative-one-to-one depth clipping requires VK_EXT_depth_clip_control with depthClipControl enabled"); if (state.stages.tessellation) { Require(context.tessellationShader, "device does not support tessellation shaders"); diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderValidation.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderValidation.cpp index d95a285df..285e29bc9 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderValidation.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderValidation.cpp @@ -21,6 +21,7 @@ struct Decoration { bool block = false; bool patch = false; bool perPrimitive = false; + bool perVertex = false; }; struct Variable { @@ -40,6 +41,7 @@ struct Module { std::map outputs; bool position = false; bool primitiveIndices = false; + bool fragmentBarycentric = false; std::map> modes; const std::vector& Type(std::uint32_t id) const { @@ -78,6 +80,10 @@ struct Module { const bool vertex = stage == Stage::Vertex || stage == Stage::Local; const bool input = storage == spv::StorageClassInput; const auto& raw = Type(type); + if (value == spv::BuiltInBaryCoordKHR || value == spv::BuiltInBaryCoordNoPerspKHR) { + Require(fragmentBarycentric && stage == Stage::Fragment && input && Signature(type) == "f32x3", "invalid barycentric built-in: expected fragment Float32 vec3 input with FragmentBarycentricKHR"); + return; + } if (value == spv::BuiltInSubgroupLocalInvocationId) { Require(input && Signature(type) == "u32", "invalid subgroup local invocation ID input"); Require((subgroup.supportedStages & VulkanStage(stage)) != 0 && (subgroup.supportedOperations & VK_SUBGROUP_FEATURE_BASIC_BIT) != 0, "subgroup local invocation ID is unsupported for this shader stage"); @@ -117,7 +123,7 @@ struct Module { } }; -Module Inspect(const CompiledShader& compiled, const State& state, const VkPhysicalDeviceSubgroupProperties& subgroup) { +Module Inspect(const CompiledShader& compiled, const State& state, const VkPhysicalDeviceSubgroupProperties& subgroup, bool fragmentShaderBarycentric) { using Stage = ShaderRecompiler::ShaderStage; Require(compiled.program != nullptr, "missing compiled shader"); const auto& shader = *compiled.program; @@ -146,6 +152,12 @@ Module Inspect(const CompiledShader& compiled, const State& state, const VkPhysi Require(count == 2, "invalid OpCapability instruction"); const auto capability = static_cast(instruction[1]); + const bool isBarycentricCapability = capability == spv::CapabilityFragmentBarycentricKHR; + if (isBarycentricCapability) { + Require(fragment, "FragmentBarycentricKHR requires a fragment shader"); + Require(fragmentShaderBarycentric, "device does not support enabled VK_KHR_fragment_shader_barycentric with fragmentShaderBarycentric"); + module.fragmentBarycentric = true; + } VkSubgroupFeatureFlags subgroupOperations = 0; switch (capability) { case spv::CapabilityGroupNonUniform: subgroupOperations = VK_SUBGROUP_FEATURE_BASIC_BIT; break; @@ -179,6 +191,7 @@ Module Inspect(const CompiledShader& compiled, const State& state, const VkPhysi Require( isBaseCapability || + isBarycentricCapability || isSubgroupCapability || isBdaCapability || isTessellationCapability || @@ -195,6 +208,10 @@ Module Inspect(const CompiledShader& compiled, const State& state, const VkPhysi const auto end = std::find(text, text + bytes.size(), '\0'); Require(end != text + bytes.size(), "unterminated SPIR-V extension"); const std::string_view extension(text, static_cast(end - text)); + if (extension == "SPV_KHR_fragment_shader_barycentric") { + Require(fragment && fragmentShaderBarycentric, "SPV_KHR_fragment_shader_barycentric requires enabled fragmentShaderBarycentric in a fragment shader"); + break; + } Require(extension == "SPV_KHR_float_controls" || (mesh && extension == "SPV_EXT_mesh_shader") || (shader.bdaAbiVersion == ShaderRecompiler::BdaAbi::Version && (extension == "SPV_KHR_physical_storage_buffer" || extension == "SPV_KHR_8bit_storage")), "unsupported SPIR-V extension"); break; } @@ -238,6 +255,10 @@ Module Inspect(const CompiledShader& compiled, const State& state, const VkPhysi Require(kind != spv::DecorationComponent && kind != spv::DecorationIndex && kind != spv::DecorationStream && kind != spv::DecorationXfbBuffer && kind != spv::DecorationXfbStride, "unsupported shader interface packing or transform feedback"); if (kind == spv::DecorationPatch) decoration.patch = true; if (kind == spv::DecorationPerPrimitiveEXT) decoration.perPrimitive = true; + if (kind == spv::DecorationPerVertexKHR) { + Require(count == 3, "malformed PerVertexKHR decoration"); + decoration.perVertex = true; + } if (kind == spv::DecorationBlock) decoration.block = true; break; } @@ -314,6 +335,13 @@ Module Inspect(const CompiledShader& compiled, const State& state, const VkPhysi const bool tessellationLevels = decoration.builtin && (*decoration.builtin == spv::BuiltInTessLevelOuter || *decoration.builtin == spv::BuiltInTessLevelInner); const bool vertexArray = !tessellationLevels && ((control && (input || output)) || (evaluation && input) || (mesh && output)) && !decoration.patch; const auto& outer = module.Type(typeId); + if (decoration.perVertex) { + Require(fragment && input && module.fragmentBarycentric && !decoration.patch && !decoration.perPrimitive && !decoration.builtin && decoration.location.has_value(), "PerVertexKHR requires a fragment location input with FragmentBarycentricKHR"); + Require(outer.size() == 4 && (outer[0] & 0xffffu) == spv::OpTypeArray, "PerVertexKHR input must be an array"); + const auto length = module.constants.find(outer[3]); + Require(length != module.constants.end() && length->second == 3, "PerVertexKHR input must contain three vertices"); + typeId = outer[2]; + } if (vertexArray && (outer[0] & 0xffffu) == spv::OpTypeArray) { Require(outer.size() == 4, "malformed per-vertex interface array"); const auto length = module.constants.find(outer[3]); @@ -408,7 +436,7 @@ Module Inspect(const CompiledShader& compiled, const State& state, const VkPhysi } -void ValidateShaders(std::span shaders, const State& state, const VkPhysicalDeviceSubgroupProperties& subgroup) { +void ValidateShaders(std::span shaders, const State& state, const VkPhysicalDeviceSubgroupProperties& subgroup, bool fragmentShaderBarycentric) { using Stage = ShaderRecompiler::ShaderStage; const bool tessellation = state.stages.path == ShaderPath::Tessellation; const bool mesh = state.stages.path == ShaderPath::Geometry; @@ -423,7 +451,7 @@ void ValidateShaders(std::span shaders, const State& state Require(shaders[i].program != nullptr, "missing compiled shader"); Require(shaders[i].stage == expected, "graphics stage order disagrees"); for (const auto& binding : shaders[i].program->bindings) Require(binding.descriptorSet == 0, "graphics resource uses a descriptor set other than zero"); - const auto current = Inspect(shaders[i], state, subgroup); + const auto current = Inspect(shaders[i], state, subgroup, fragmentShaderBarycentric); if (i != 0) { for (const auto& [location, signature] : current.inputs) { const auto output = previous.outputs.find(location); @@ -439,7 +467,7 @@ void ValidateShaderPair(const ShaderRecompiler::RecompileResult& vertex, const S const std::array shaders{{{ShaderRecompiler::ShaderStage::Vertex, &vertex, 0}, {ShaderRecompiler::ShaderStage::Fragment, &fragment, static_cast(vertex.pushConstants.size())}}}; State state{}; state.stages.path = ShaderPath::Vertex; - ValidateShaders(shaders, state, VkPhysicalDeviceSubgroupProperties{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES}); + ValidateShaders(shaders, state, VkPhysicalDeviceSubgroupProperties{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES}, false); } } diff --git a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp index 3e9a19ff2..20546fc7d 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Graphics.cpp @@ -629,6 +629,12 @@ struct ModuleShape { bool plainBuffer = false; bool shaderData = false; bool vertexInput = false; + bool barycentric = false; + bool barycentricNoPerspective = false; + std::uint32_t barycentricComponents = 3; + bool perVertex = false; + std::uint32_t perVertexLength = 3; + bool parameterOutput = false; }; void emit(std::vector& out, spv::Op op, std::initializer_list operands) { @@ -653,6 +659,7 @@ std::vector makeModule(const ModuleShape& shape) { const auto label = id(); const auto inputPointer = id(); const auto input = id(); + std::vector extraInterface; emit(declarations, spv::OpTypeVoid, {voidType}); emit(declarations, spv::OpTypeFunction, {functionType, voidType}); emit(declarations, spv::OpTypeFloat, {floatType, 32}); @@ -660,6 +667,35 @@ std::vector makeModule(const ModuleShape& shape) { emit(declarations, spv::OpTypeInt, {uintType, 32, 0}); emit(declarations, spv::OpTypePointer, {outputPointer, spv::StorageClassOutput, vectorType}); emit(declarations, spv::OpVariable, {outputPointer, output, spv::StorageClassOutput}); + if (shape.parameterOutput) { + const auto parameter = id(); + emit(declarations, spv::OpVariable, {outputPointer, parameter, spv::StorageClassOutput}); + emit(annotations, spv::OpDecorate, {parameter, spv::DecorationLocation, 0}); + extraInterface.push_back(parameter); + } + if (shape.barycentric) { + const auto vector = id(); + const auto pointer = id(); + const auto variable = id(); + emit(declarations, spv::OpTypeVector, {vector, floatType, shape.barycentricComponents}); + emit(declarations, spv::OpTypePointer, {pointer, spv::StorageClassInput, vector}); + emit(declarations, spv::OpVariable, {pointer, variable, spv::StorageClassInput}); + emit(annotations, spv::OpDecorate, {variable, spv::DecorationBuiltIn, shape.barycentricNoPerspective ? spv::BuiltInBaryCoordNoPerspKHR : spv::BuiltInBaryCoordKHR}); + extraInterface.push_back(variable); + } + if (shape.perVertex) { + const auto length = id(); + const auto array = id(); + const auto pointer = id(); + const auto variable = id(); + emit(declarations, spv::OpConstant, {uintType, length, shape.perVertexLength}); + emit(declarations, spv::OpTypeArray, {array, vectorType, length}); + emit(declarations, spv::OpTypePointer, {pointer, spv::StorageClassInput, array}); + emit(declarations, spv::OpVariable, {pointer, variable, spv::StorageClassInput}); + emit(annotations, spv::OpDecorate, {variable, spv::DecorationLocation, 0}); + emit(annotations, spv::OpDecorate, {variable, spv::DecorationPerVertexKHR}); + extraInterface.push_back(variable); + } if (shape.vertexInput) { emit(declarations, spv::OpTypePointer, {inputPointer, spv::StorageClassInput, vectorType}); emit(declarations, spv::OpVariable, {inputPointer, input, spv::StorageClassInput}); @@ -714,9 +750,21 @@ std::vector makeModule(const ModuleShape& shape) { emit(function, spv::OpFunctionEnd, {}); std::vector words{spv::MagicNumber, 0x10300, 0, next, 0}; emit(words, spv::OpCapability, {spv::CapabilityShader}); + if (shape.barycentric) { + emit(words, spv::OpCapability, {spv::CapabilityFragmentBarycentricKHR}); + const std::string extension = "SPV_KHR_fragment_shader_barycentric"; + const auto count = (extension.size() + 4) / 4; + words.push_back((static_cast(count + 1) << 16u) | spv::OpExtension); + const auto start = words.size(); + words.resize(start + count, 0); + for (std::size_t i = 0; i < extension.size(); ++i) words[start + i / 4] |= static_cast(static_cast(extension[i])) << ((i % 4) * 8); + } emit(words, spv::OpMemoryModel, {spv::AddressingModelLogical, spv::MemoryModelGLSL450}); + const auto entryPointOffset = words.size(); if (shape.vertexInput) emit(words, spv::OpEntryPoint, {spv::ExecutionModelVertex, main, 0x6e69616du, 0, output, input}); else emit(words, spv::OpEntryPoint, {shape.fragment ? spv::ExecutionModelFragment : spv::ExecutionModelVertex, main, 0x6e69616du, 0, output}); + words[entryPointOffset] += static_cast(extraInterface.size()) << 16u; + words.insert(words.end(), extraInterface.begin(), extraInterface.end()); if (shape.fragment) emit(words, spv::OpExecutionMode, {main, spv::ExecutionModeOriginUpperLeft}); words.insert(words.end(), annotations.begin(), annotations.end()); words.insert(words.end(), declarations.begin(), declarations.end()); @@ -729,17 +777,38 @@ void validationTests() { state.stages.path = AgcDriver::Graphics::ShaderPath::Vertex; ShaderRecompiler::RecompileResult fragment; fragment.spirv = makeModule({.fragment = true}); + { + ShaderRecompiler::RecompileResult vertex; + vertex.spirv = makeModule({.parameterOutput = true}); + ShaderRecompiler::RecompileResult pixel; + const std::array shaders{{{ShaderRecompiler::ShaderStage::Vertex, &vertex, 0}, {ShaderRecompiler::ShaderStage::Fragment, &pixel, 0}}}; + const VkPhysicalDeviceSubgroupProperties subgroup{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES}; + for (const auto noPerspective : {false, true}) { + pixel.spirv = makeModule({.fragment = true, .barycentric = true, .barycentricNoPerspective = noPerspective, .perVertex = true}); + AgcDriver::Graphics::ValidateShaders(shaders, state, subgroup, true); + expectFailure([&] { AgcDriver::Graphics::ValidateShaders(shaders, state, subgroup, false); }, "fragmentShaderBarycentric"); + } + pixel.spirv = makeModule({.fragment = true, .barycentric = true, .barycentricComponents = 4}); + expectFailure([&] { AgcDriver::Graphics::ValidateShaders(shaders, state, subgroup, true); }, "invalid barycentric built-in"); + pixel.spirv = makeModule({.fragment = true, .barycentric = true, .perVertex = true, .perVertexLength = 2}); + expectFailure([&] { AgcDriver::Graphics::ValidateShaders(shaders, state, subgroup, true); }, "three vertices"); + pixel.spirv = makeModule({.fragment = true, .perVertex = true}); + expectFailure([&] { AgcDriver::Graphics::ValidateShaders(shaders, state, subgroup, true); }, "PerVertexKHR requires"); + vertex.spirv = makeModule({.barycentric = true}); + pixel.spirv = makeModule({.fragment = true}); + expectFailure([&] { AgcDriver::Graphics::ValidateShaders(shaders, state, subgroup, true); }, "requires a fragment shader"); + } { ShaderRecompiler::RecompileResult vertex; vertex.spirv = makeModule({.vertexInput = true}); ShaderRecompiler::VertexAttribute attribute{0, 4, {{0x1000, 32u << 16u, 3, 77u << 12u}}, 0}; const std::array shaders{{{ShaderRecompiler::ShaderStage::Vertex, &vertex, 0}, {ShaderRecompiler::ShaderStage::Fragment, &fragment, 0}}}; const VkPhysicalDeviceSubgroupProperties subgroup{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES}; - expectFailure([&] { AgcDriver::Graphics::ValidateShaders(shaders, state, subgroup); }, "missing attribute metadata"); + expectFailure([&] { AgcDriver::Graphics::ValidateShaders(shaders, state, subgroup, false); }, "missing attribute metadata"); vertex.vertexAttributes.push_back(attribute); - AgcDriver::Graphics::ValidateShaders(shaders, state, subgroup); + AgcDriver::Graphics::ValidateShaders(shaders, state, subgroup, false); vertex.vertexAttributes[0].components = 2; - expectFailure([&] { AgcDriver::Graphics::ValidateShaders(shaders, state, subgroup); }, "metadata disagrees"); + expectFailure([&] { AgcDriver::Graphics::ValidateShaders(shaders, state, subgroup, false); }, "metadata disagrees"); Require(AgcDriver::Graphics::VertexBufferReadSize(attribute, 2, 1) == 80, "incorrect strided vertex range"); expectFailure([&] { AgcDriver::Graphics::VertexBufferReadSize(attribute, 3, 1); }, "record count"); attribute.fetchIndex = 1; @@ -779,17 +848,17 @@ void validationTests() { VkPhysicalDeviceSubgroupProperties subgroup{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES}; subgroup.supportedStages = VK_SHADER_STAGE_VERTEX_BIT; subgroup.supportedOperations = VK_SUBGROUP_FEATURE_BASIC_BIT | VK_SUBGROUP_FEATURE_BALLOT_BIT | VK_SUBGROUP_FEATURE_SHUFFLE_BIT; - AgcDriver::Graphics::ValidateShaders(shaders, state, subgroup); + AgcDriver::Graphics::ValidateShaders(shaders, state, subgroup, false); subgroup.supportedStages = VK_SHADER_STAGE_FRAGMENT_BIT; - expectFailure([&] { AgcDriver::Graphics::ValidateShaders(shaders, state, subgroup); }, "unsupported for shader stage"); + expectFailure([&] { AgcDriver::Graphics::ValidateShaders(shaders, state, subgroup, false); }, "unsupported for shader stage"); subgroup.supportedStages = VK_SHADER_STAGE_VERTEX_BIT; subgroup.supportedOperations = capability == spv::CapabilityGroupNonUniform ? 0u : VK_SUBGROUP_FEATURE_BASIC_BIT; - expectFailure([&] { AgcDriver::Graphics::ValidateShaders(shaders, state, subgroup); }, "device lacks operations"); + expectFailure([&] { AgcDriver::Graphics::ValidateShaders(shaders, state, subgroup, false); }, "device lacks operations"); } const std::vector words(8, 0); const auto validate = [&](const ShaderRecompiler::RecompileResult& vertex, std::uint32_t fragmentOffset) { const std::array shaders{{{ShaderRecompiler::ShaderStage::Vertex, &vertex, 0}, {ShaderRecompiler::ShaderStage::Fragment, &fragment, fragmentOffset}}}; - AgcDriver::Graphics::ValidateShaders(shaders, state, VkPhysicalDeviceSubgroupProperties{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES}); + AgcDriver::Graphics::ValidateShaders(shaders, state, VkPhysicalDeviceSubgroupProperties{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES}, false); }; const auto pushed = [&](const ModuleShape& shape) { ShaderRecompiler::RecompileResult vertex;