feat(agc): add fragment shader barycentric support
This commit is contained in:
parent
5e3a13041c
commit
79c9aa587b
|
|
@ -60,6 +60,7 @@ struct VulkanDevice::State {
|
|||
std::vector<std::string_view> 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<PFN_vkGetPhysicalDeviceFeatures2>("vkGetPhysicalDeviceFeatures2")(selected, &features);
|
||||
state->fragmentShaderBarycentric = barycentricFeatures.fragmentShaderBarycentric == VK_TRUE;
|
||||
}
|
||||
std::vector<const char*> 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<void*>(deviceInfo.pNext);
|
||||
if (state->fragmentShaderBarycentric) {
|
||||
barycentricFeatures.pNext = byteFeatures.pNext;
|
||||
byteFeatures.pNext = &barycentricFeatures;
|
||||
}
|
||||
bdaFeatures.pNext = &byteFeatures;
|
||||
deviceInfo.pNext = &bdaFeatures;
|
||||
check(state->InstanceFunction<PFN_vkCreateDevice>("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
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<typename TFunction>
|
||||
TFunction Function(const char* name) const {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ private:
|
|||
};
|
||||
|
||||
void ValidateShaderPair(const ShaderRecompiler::RecompileResult& vertex, const ShaderRecompiler::RecompileResult& fragment);
|
||||
void ValidateShaders(std::span<const CompiledShader> shaders, const State& state, const VkPhysicalDeviceSubgroupProperties& subgroup);
|
||||
void ValidateShaders(std::span<const CompiledShader> shaders, const State& state, const VkPhysicalDeviceSubgroupProperties& subgroup, bool fragmentShaderBarycentric);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<unsigned>(shaderStages));
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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<std::uint32_t, std::string> outputs;
|
||||
bool position = false;
|
||||
bool primitiveIndices = false;
|
||||
bool fragmentBarycentric = false;
|
||||
std::map<std::uint32_t, std::vector<std::uint32_t>> modes;
|
||||
|
||||
const std::vector<std::uint32_t>& 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<spv::Capability>(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<std::size_t>(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<const CompiledShader> shaders, const State& state, const VkPhysicalDeviceSubgroupProperties& subgroup) {
|
||||
void ValidateShaders(std::span<const CompiledShader> 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<const CompiledShader> 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<CompiledShader, 2> shaders{{{ShaderRecompiler::ShaderStage::Vertex, &vertex, 0}, {ShaderRecompiler::ShaderStage::Fragment, &fragment, static_cast<std::uint32_t>(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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<std::uint32_t>& out, spv::Op op, std::initializer_list<std::uint32_t> operands) {
|
||||
|
|
@ -653,6 +659,7 @@ std::vector<std::uint32_t> makeModule(const ModuleShape& shape) {
|
|||
const auto label = id();
|
||||
const auto inputPointer = id();
|
||||
const auto input = id();
|
||||
std::vector<std::uint32_t> extraInterface;
|
||||
emit(declarations, spv::OpTypeVoid, {voidType});
|
||||
emit(declarations, spv::OpTypeFunction, {functionType, voidType});
|
||||
emit(declarations, spv::OpTypeFloat, {floatType, 32});
|
||||
|
|
@ -660,6 +667,35 @@ std::vector<std::uint32_t> 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<std::uint32_t> makeModule(const ModuleShape& shape) {
|
|||
emit(function, spv::OpFunctionEnd, {});
|
||||
std::vector<std::uint32_t> 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<std::uint32_t>(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<std::uint32_t>(static_cast<unsigned char>(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<std::uint32_t>(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<AgcDriver::Graphics::CompiledShader, 2> 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<AgcDriver::Graphics::CompiledShader, 2> 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<std::uint32_t> words(8, 0);
|
||||
const auto validate = [&](const ShaderRecompiler::RecompileResult& vertex, std::uint32_t fragmentOffset) {
|
||||
const std::array<AgcDriver::Graphics::CompiledShader, 2> 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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue