mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-28 13:56:59 +01:00
shader: read sample IDs from pixel ancillary input
This commit is contained in:
@@ -72,7 +72,7 @@ uint32_t EmitBuiltinU32(ValueEmitContext& ctx, IR::StageInputKind kind, uint32_t
|
|||||||
return bits;
|
return bits;
|
||||||
}
|
}
|
||||||
if (kind == IR::StageInputKind::VertexIndex || kind == IR::StageInputKind::InstanceIndex ||
|
if (kind == IR::StageInputKind::VertexIndex || kind == IR::StageInputKind::InstanceIndex ||
|
||||||
kind == IR::StageInputKind::Layer) {
|
kind == IR::StageInputKind::Layer || kind == IR::StageInputKind::SampleId) {
|
||||||
const auto value = state.builder.AllocateId();
|
const auto value = state.builder.AllocateId();
|
||||||
const auto bits = state.builder.AllocateId();
|
const auto bits = state.builder.AllocateId();
|
||||||
state.builder.AddFunction({OpLoad, TypeI32(state), value, variable});
|
state.builder.AddFunction({OpLoad, TypeI32(state), value, variable});
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ enum : uint32_t {
|
|||||||
CapabilityImageGatherExtended = 25,
|
CapabilityImageGatherExtended = 25,
|
||||||
CapabilityClipDistance = 32,
|
CapabilityClipDistance = 32,
|
||||||
CapabilityCullDistance = 33,
|
CapabilityCullDistance = 33,
|
||||||
|
CapabilitySampleRateShading = 35,
|
||||||
CapabilitySampled1D = 43,
|
CapabilitySampled1D = 43,
|
||||||
CapabilityImage1D = 44,
|
CapabilityImage1D = 44,
|
||||||
CapabilityImageQuery = 50,
|
CapabilityImageQuery = 50,
|
||||||
@@ -92,6 +93,7 @@ enum : uint32_t {
|
|||||||
BuiltInLayer = 9,
|
BuiltInLayer = 9,
|
||||||
BuiltInFragCoord = 15,
|
BuiltInFragCoord = 15,
|
||||||
BuiltInFrontFacing = 17,
|
BuiltInFrontFacing = 17,
|
||||||
|
BuiltInSampleId = 18,
|
||||||
BuiltInSampleMask = 20,
|
BuiltInSampleMask = 20,
|
||||||
BuiltInFragDepth = 22,
|
BuiltInFragDepth = 22,
|
||||||
BuiltInWorkgroupId = 26,
|
BuiltInWorkgroupId = 26,
|
||||||
|
|||||||
@@ -512,6 +512,7 @@ uint32_t BuiltInForInput(IR::StageInputKind kind) {
|
|||||||
case IR::StageInputKind::FragCoord: return BuiltInFragCoord;
|
case IR::StageInputKind::FragCoord: return BuiltInFragCoord;
|
||||||
case IR::StageInputKind::FrontFacing: return BuiltInFrontFacing;
|
case IR::StageInputKind::FrontFacing: return BuiltInFrontFacing;
|
||||||
case IR::StageInputKind::Layer: return BuiltInLayer;
|
case IR::StageInputKind::Layer: return BuiltInLayer;
|
||||||
|
case IR::StageInputKind::SampleId: return BuiltInSampleId;
|
||||||
case IR::StageInputKind::BaryCoordSmooth: return BuiltInBaryCoordKHR;
|
case IR::StageInputKind::BaryCoordSmooth: return BuiltInBaryCoordKHR;
|
||||||
case IR::StageInputKind::BaryCoordNoPerspective: return BuiltInBaryCoordNoPerspKHR;
|
case IR::StageInputKind::BaryCoordNoPerspective: return BuiltInBaryCoordNoPerspKHR;
|
||||||
case IR::StageInputKind::WorkgroupId: return BuiltInWorkgroupId;
|
case IR::StageInputKind::WorkgroupId: return BuiltInWorkgroupId;
|
||||||
@@ -535,7 +536,7 @@ void AddInputAnnotationsAndNames(EmitterState& state) {
|
|||||||
}
|
}
|
||||||
for (const auto& input: state.inputs) {
|
for (const auto& input: state.inputs) {
|
||||||
state.builder.AddName(input.variable_id, input.debug_name.c_str());
|
state.builder.AddName(input.variable_id, input.debug_name.c_str());
|
||||||
if (input.kind == IR::StageInputKind::Layer) {
|
if (input.kind == IR::StageInputKind::Layer || input.kind == IR::StageInputKind::SampleId) {
|
||||||
state.builder.AddAnnotation({OpDecorate, input.variable_id, DecorationFlat});
|
state.builder.AddAnnotation({OpDecorate, input.variable_id, DecorationFlat});
|
||||||
}
|
}
|
||||||
if (input.kind == IR::StageInputKind::Parameter) {
|
if (input.kind == IR::StageInputKind::Parameter) {
|
||||||
@@ -711,6 +712,9 @@ void DefineModule(EmitterState& state) {
|
|||||||
state.builder.RequireVersion(0x00010500u);
|
state.builder.RequireVersion(0x00010500u);
|
||||||
state.builder.RequireCapability(CapabilityShaderLayer);
|
state.builder.RequireCapability(CapabilityShaderLayer);
|
||||||
}
|
}
|
||||||
|
if (InputVariableForKind(state, IR::StageInputKind::SampleId) != 0) {
|
||||||
|
state.builder.RequireCapability(CapabilitySampleRateShading);
|
||||||
|
}
|
||||||
if (state.requirements.image_gather_extended) {
|
if (state.requirements.image_gather_extended) {
|
||||||
state.builder.RequireCapability(CapabilityImageGatherExtended);
|
state.builder.RequireCapability(CapabilityImageGatherExtended);
|
||||||
}
|
}
|
||||||
@@ -803,6 +807,7 @@ void DefineModule(EmitterState& state) {
|
|||||||
case IR::StageInputKind::VertexIndex:
|
case IR::StageInputKind::VertexIndex:
|
||||||
case IR::StageInputKind::InstanceIndex:
|
case IR::StageInputKind::InstanceIndex:
|
||||||
case IR::StageInputKind::Layer:
|
case IR::StageInputKind::Layer:
|
||||||
|
case IR::StageInputKind::SampleId:
|
||||||
ptr_type = TypePointer(state, StorageClassInput, TypeI32(state));
|
ptr_type = TypePointer(state, StorageClassInput, TypeI32(state));
|
||||||
break;
|
break;
|
||||||
case IR::StageInputKind::WorkgroupId:
|
case IR::StageInputKind::WorkgroupId:
|
||||||
|
|||||||
@@ -162,6 +162,7 @@ enum class StageInputKind {
|
|||||||
FrontFacing,
|
FrontFacing,
|
||||||
PackedAncillary,
|
PackedAncillary,
|
||||||
Layer,
|
Layer,
|
||||||
|
SampleId,
|
||||||
BaryCoordSmooth,
|
BaryCoordSmooth,
|
||||||
BaryCoordNoPerspective,
|
BaryCoordNoPerspective,
|
||||||
WorkgroupId,
|
WorkgroupId,
|
||||||
|
|||||||
@@ -310,17 +310,24 @@ void FoldInstruction(Block& block, Block::iterator instruction) {
|
|||||||
const auto* source = value.TryInstruction();
|
const auto* source = value.TryInstruction();
|
||||||
if (source != nullptr && source->GetOpcode() == ValueOpcode::GetBuiltin &&
|
if (source != nullptr && source->GetOpcode() == ValueOpcode::GetBuiltin &&
|
||||||
source->Arg(0) == Value(static_cast<uint32_t>(StageInputKind::PackedAncillary)) &&
|
source->Arg(0) == Value(static_cast<uint32_t>(StageInputKind::PackedAncillary)) &&
|
||||||
IsImmediate(offset, Type::U32) && IsImmediate(count, Type::U32) &&
|
IsImmediate(offset, Type::U32) && IsImmediate(count, Type::U32) && count.U32() != 0u) {
|
||||||
offset.U32() >= 16u && offset.U32() < 27u && count.U32() != 0u &&
|
constexpr struct {
|
||||||
count.U32() <= 27u - offset.U32()) {
|
uint32_t start;
|
||||||
// Preserve the extraction (including sign extension) while exposing only its
|
uint32_t end;
|
||||||
// used hardware field. Other live ancillary fields remain unsupported.
|
StageInputKind kind;
|
||||||
const auto layer = block.PrependNewInst(
|
} fields[] = {{8u, 12u, StageInputKind::SampleId}, {16u, 27u, StageInputKind::Layer}};
|
||||||
instruction, ValueOpcode::GetBuiltin,
|
for (const auto& field: fields) {
|
||||||
{Value(static_cast<uint32_t>(StageInputKind::Layer)), Value(0u)});
|
if (offset.U32() >= field.start && offset.U32() < field.end &&
|
||||||
inst.SetArg(0, Value(&*layer));
|
count.U32() <= field.end - offset.U32()) {
|
||||||
inst.SetArg(1, Value(offset.U32() - 16u));
|
// Preserve extraction and sign extension while exposing only the used field.
|
||||||
return;
|
const auto input = block.PrependNewInst(
|
||||||
|
instruction, ValueOpcode::GetBuiltin,
|
||||||
|
{Value(static_cast<uint32_t>(field.kind)), Value(0u)});
|
||||||
|
inst.SetArg(0, Value(&*input));
|
||||||
|
inst.SetArg(1, Value(offset.U32() - field.start));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!IsImmediate(value, Type::U32) || !IsImmediate(offset, Type::U32) ||
|
if (!IsImmediate(value, Type::U32) || !IsImmediate(offset, Type::U32) ||
|
||||||
!IsImmediate(count, Type::U32) || offset.U32() > 32u ||
|
!IsImmediate(count, Type::U32) || offset.U32() > 32u ||
|
||||||
|
|||||||
@@ -118,8 +118,9 @@ void ValidateValueReferences(const Program& program, const ShaderInfoOptions& op
|
|||||||
case StageInputKind::PackedAncillary:
|
case StageInputKind::PackedAncillary:
|
||||||
return Fail("packed pixel ancillary input has an unsupported live use");
|
return Fail("packed pixel ancillary input has an unsupported live use");
|
||||||
case StageInputKind::Layer:
|
case StageInputKind::Layer:
|
||||||
|
case StageInputKind::SampleId:
|
||||||
if (program.stage != ShaderType::Pixel || component != 0u) {
|
if (program.stage != ShaderType::Pixel || component != 0u) {
|
||||||
return Fail("typed layer input is invalid");
|
return Fail("typed pixel scalar input is invalid");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case StageInputKind::VertexIndex:
|
case StageInputKind::VertexIndex:
|
||||||
@@ -259,6 +260,7 @@ void CollectBuiltinInputs(const Program& program, ShaderInfo& info) {
|
|||||||
AddInput(info, kind, 0, 1, "gl_FrontFacing");
|
AddInput(info, kind, 0, 1, "gl_FrontFacing");
|
||||||
break;
|
break;
|
||||||
case StageInputKind::Layer: AddInput(info, kind, 0, 1, "gl_Layer"); break;
|
case StageInputKind::Layer: AddInput(info, kind, 0, 1, "gl_Layer"); break;
|
||||||
|
case StageInputKind::SampleId: AddInput(info, kind, 0, 1, "gl_SampleID"); break;
|
||||||
case StageInputKind::BaryCoordSmooth:
|
case StageInputKind::BaryCoordSmooth:
|
||||||
AddInput(info, kind, 0, 3, "gl_BaryCoordKHR");
|
AddInput(info, kind, 0, 3, "gl_BaryCoordKHR");
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1048,6 +1048,7 @@ struct GraphicsCase {
|
|||||||
bool pixel_ancillary = false;
|
bool pixel_ancillary = false;
|
||||||
bool pixel_front_face = false;
|
bool pixel_front_face = false;
|
||||||
u32 layers = 1;
|
u32 layers = 1;
|
||||||
|
vk::SampleCountFlagBits samples = vk::SampleCountFlagBits::e1;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CompiledShader {
|
struct CompiledShader {
|
||||||
@@ -10252,7 +10253,8 @@ public:
|
|||||||
u32 dwords_per_pixel, vk::ImageLayout final_layout,
|
u32 dwords_per_pixel, vk::ImageLayout final_layout,
|
||||||
vk::ImageType image_type, vk::ImageViewType view_type,
|
vk::ImageType image_type, vk::ImageViewType view_type,
|
||||||
u32 layers, u32 view_base_layer = 0,
|
u32 layers, u32 view_base_layer = 0,
|
||||||
u32 view_layers = 0) {
|
u32 view_layers = 0,
|
||||||
|
vk::SampleCountFlagBits samples = vk::SampleCountFlagBits::e1) {
|
||||||
Image ret;
|
Image ret;
|
||||||
ret.format = format;
|
ret.format = format;
|
||||||
ret.width = width;
|
ret.width = width;
|
||||||
@@ -10270,7 +10272,7 @@ public:
|
|||||||
image_info.extent.depth = 1;
|
image_info.extent.depth = 1;
|
||||||
image_info.mipLevels = ret.mip_levels;
|
image_info.mipLevels = ret.mip_levels;
|
||||||
image_info.arrayLayers = layers;
|
image_info.arrayLayers = layers;
|
||||||
image_info.samples = vk::SampleCountFlagBits::e1;
|
image_info.samples = samples;
|
||||||
image_info.tiling = vk::ImageTiling::eOptimal;
|
image_info.tiling = vk::ImageTiling::eOptimal;
|
||||||
image_info.usage = usage | vk::ImageUsageFlagBits::eTransferDst |
|
image_info.usage = usage | vk::ImageUsageFlagBits::eTransferDst |
|
||||||
vk::ImageUsageFlagBits::eTransferSrc;
|
vk::ImageUsageFlagBits::eTransferSrc;
|
||||||
@@ -10914,7 +10916,15 @@ public:
|
|||||||
vk::ImageUsageFlagBits::eColorAttachment, {}, 4,
|
vk::ImageUsageFlagBits::eColorAttachment, {}, 4,
|
||||||
vk::ImageLayout::eGeneral, vk::ImageType::e2D,
|
vk::ImageLayout::eGeneral, vk::ImageType::e2D,
|
||||||
test.layers > 1 ? vk::ImageViewType::e2DArray : vk::ImageViewType::e2D,
|
test.layers > 1 ? vk::ImageViewType::e2DArray : vk::ImageViewType::e2D,
|
||||||
test.layers);
|
test.layers, 0, 0, test.samples);
|
||||||
|
Image resolved;
|
||||||
|
if (test.samples != vk::SampleCountFlagBits::e1) {
|
||||||
|
resolved = CreateImageMips(
|
||||||
|
test.name, 1, 1, target.format, vk::ImageUsageFlagBits::eColorAttachment,
|
||||||
|
{}, 4, vk::ImageLayout::eGeneral, vk::ImageType::e2D,
|
||||||
|
test.layers > 1 ? vk::ImageViewType::e2DArray : vk::ImageViewType::e2D,
|
||||||
|
test.layers);
|
||||||
|
}
|
||||||
const std::vector<u32> default_vertices = {
|
const std::vector<u32> default_vertices = {
|
||||||
0xbf800000u, 0xbf800000u, 0x3e800000u, 0x3f000000u, 0x3f400000u,
|
0xbf800000u, 0xbf800000u, 0x3e800000u, 0x3f000000u, 0x3f400000u,
|
||||||
0x3f800000u, 0x40400000u, 0xbf800000u, 0x3e800000u, 0x3f000000u,
|
0x3f800000u, 0x40400000u, 0xbf800000u, 0x3e800000u, 0x3f000000u,
|
||||||
@@ -11028,7 +11038,7 @@ public:
|
|||||||
|
|
||||||
vk::PipelineMultisampleStateCreateInfo multisample{};
|
vk::PipelineMultisampleStateCreateInfo multisample{};
|
||||||
multisample.sType = vk::StructureType::ePipelineMultisampleStateCreateInfo;
|
multisample.sType = vk::StructureType::ePipelineMultisampleStateCreateInfo;
|
||||||
multisample.rasterizationSamples = vk::SampleCountFlagBits::e1;
|
multisample.rasterizationSamples = test.samples;
|
||||||
|
|
||||||
vk::PipelineColorBlendAttachmentState color_attachment{};
|
vk::PipelineColorBlendAttachmentState color_attachment{};
|
||||||
color_attachment.colorWriteMask =
|
color_attachment.colorWriteMask =
|
||||||
@@ -11069,6 +11079,11 @@ public:
|
|||||||
color.imageLayout = vk::ImageLayout::eGeneral;
|
color.imageLayout = vk::ImageLayout::eGeneral;
|
||||||
color.loadOp = vk::AttachmentLoadOp::eClear;
|
color.loadOp = vk::AttachmentLoadOp::eClear;
|
||||||
color.storeOp = vk::AttachmentStoreOp::eStore;
|
color.storeOp = vk::AttachmentStoreOp::eStore;
|
||||||
|
if (resolved.image != nullptr) {
|
||||||
|
color.resolveMode = vk::ResolveModeFlagBits::eAverage;
|
||||||
|
color.resolveImageView = resolved.view;
|
||||||
|
color.resolveImageLayout = vk::ImageLayout::eGeneral;
|
||||||
|
}
|
||||||
vk::RenderingInfo rendering{};
|
vk::RenderingInfo rendering{};
|
||||||
rendering.sType = vk::StructureType::eRenderingInfo;
|
rendering.sType = vk::StructureType::eRenderingInfo;
|
||||||
rendering.renderArea.extent = {1, 1};
|
rendering.renderArea.extent = {1, 1};
|
||||||
@@ -11091,7 +11106,7 @@ public:
|
|||||||
EndSubmitAndFree(test.name, "graphics", cmd);
|
EndSubmitAndFree(test.name, "graphics", cmd);
|
||||||
target.layout = vk::ImageLayout::eGeneral;
|
target.layout = vk::ImageLayout::eGeneral;
|
||||||
|
|
||||||
auto pixel = ReadImage(test.name, &target);
|
auto pixel = ReadImage(test.name, resolved.image != nullptr ? &resolved : &target);
|
||||||
pixel.resize(4 * test.layers);
|
pixel.resize(4 * test.layers);
|
||||||
|
|
||||||
m_device.destroyPipeline(pipeline, nullptr);
|
m_device.destroyPipeline(pipeline, nullptr);
|
||||||
@@ -11099,6 +11114,7 @@ public:
|
|||||||
m_device.destroyShaderModule(fragment_module, nullptr);
|
m_device.destroyShaderModule(fragment_module, nullptr);
|
||||||
m_device.destroyShaderModule(vertex_module, nullptr);
|
m_device.destroyShaderModule(vertex_module, nullptr);
|
||||||
DestroyBuffer(&vertex_buffer);
|
DestroyBuffer(&vertex_buffer);
|
||||||
|
DestroyImage(&resolved);
|
||||||
DestroyImage(&target);
|
DestroyImage(&target);
|
||||||
return pixel;
|
return pixel;
|
||||||
}
|
}
|
||||||
@@ -22449,6 +22465,29 @@ GraphicsCase GraphicsAncillaryLayer(bool front_face) {
|
|||||||
return test;
|
return test;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GraphicsCase GraphicsAncillarySampleId() {
|
||||||
|
GraphicsCase test;
|
||||||
|
test.name = "GraphicsAncillarySampleId";
|
||||||
|
test.pixel_ancillary = true;
|
||||||
|
test.samples = vk::SampleCountFlagBits::e4;
|
||||||
|
AppendVop3(&test.fragment_code, 0x148, 4, Vgpr(2), InlineU32(8), InlineU32(4));
|
||||||
|
AppendVop3(&test.fragment_code, 0x149, 5, Vgpr(2), InlineU32(8), InlineU32(2));
|
||||||
|
AppendVop3(&test.fragment_code, 0x148, 6, Vgpr(2), InlineU32(9), InlineU32(1));
|
||||||
|
test.fragment_code.push_back(EncodeVop1(0x06, 0, Vgpr(4)));
|
||||||
|
test.fragment_code.push_back(EncodeVop1(0x05, 1, Vgpr(5)));
|
||||||
|
test.fragment_code.push_back(EncodeVop1(0x06, 2, Vgpr(6)));
|
||||||
|
AppendVMovLiteral(&test.fragment_code, 3, 0x3f800000u);
|
||||||
|
test.fragment_code.push_back(EncodeExp0(0x00, 0xf));
|
||||||
|
test.fragment_code.push_back(EncodeExp1(0, 1, 2, 3));
|
||||||
|
AppendEnd(&test.fragment_code);
|
||||||
|
test.opcodes = {ShaderOpcode::V_BFE_U32, ShaderOpcode::V_BFE_I32,
|
||||||
|
ShaderOpcode::V_CVT_F32_U32, ShaderOpcode::V_CVT_F32_I32,
|
||||||
|
ShaderOpcode::V_MOV_B32, ShaderOpcode::EXP, ShaderOpcode::S_ENDPGM};
|
||||||
|
// Average samples 0..3, their signed low two bits, and their second bit.
|
||||||
|
test.expected_pixel = {0x3fc00000u, 0xbf000000u, 0x3f000000u, 0x3f800000u};
|
||||||
|
return test;
|
||||||
|
}
|
||||||
|
|
||||||
GraphicsCase GraphicsFlatInterpolatorExport() {
|
GraphicsCase GraphicsFlatInterpolatorExport() {
|
||||||
using O = ShaderOpcode;
|
using O = ShaderOpcode;
|
||||||
|
|
||||||
@@ -23012,6 +23051,7 @@ std::vector<GraphicsCase> MakeGraphicsCases() {
|
|||||||
GraphicsInterpolationExport(),
|
GraphicsInterpolationExport(),
|
||||||
GraphicsAncillaryLayer(false),
|
GraphicsAncillaryLayer(false),
|
||||||
GraphicsAncillaryLayer(true),
|
GraphicsAncillaryLayer(true),
|
||||||
|
GraphicsAncillarySampleId(),
|
||||||
GraphicsFlatInterpolatorExport(),
|
GraphicsFlatInterpolatorExport(),
|
||||||
GraphicsDsAddtidScratchExport(),
|
GraphicsDsAddtidScratchExport(),
|
||||||
GraphicsDirectSgprPushConstantExport(),
|
GraphicsDirectSgprPushConstantExport(),
|
||||||
|
|||||||
@@ -6011,8 +6011,22 @@ void TestPixelAncillaryLayerInput() {
|
|||||||
unused_result.spirv[1] == 0x00010300u &&
|
unused_result.spirv[1] == 0x00010300u &&
|
||||||
!SpirvContainsCapability(unused_result.spirv, 69u),
|
!SpirvContainsCapability(unused_result.spirv, 69u),
|
||||||
"unused ancillary input changed the module requirements");
|
"unused ancillary input changed the module requirements");
|
||||||
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
|
|
||||||
shader[1] = EncodeVop3Word1(5 + 256, 128 + 8, 128 + 4);
|
shader[1] = EncodeVop3Word1(5 + 256, 128 + 8, 128 + 4);
|
||||||
|
auto sample_result = RecompileForTest(shader, options);
|
||||||
|
Check(ProgramHasInput(sample_result.program, StageInputKind::SampleId) &&
|
||||||
|
ProgramHasInput(sample_result.program, StageInputKind::Layer) &&
|
||||||
|
!ProgramHasInput(sample_result.program, StageInputKind::PackedAncillary),
|
||||||
|
"sample index extraction discarded another live ancillary field");
|
||||||
|
Check(SpirvHasDecorationValueWithDecoration(sample_result.spirv, 11u, 18u, 14u) &&
|
||||||
|
SpirvContainsCapability(sample_result.spirv, 35u),
|
||||||
|
"sample index lacks its flat builtin or sample-rate capability");
|
||||||
|
const auto sample_source = DisassembleSpirvBinary(sample_result.spirv);
|
||||||
|
Check(SpirvSourceHasInstructionUsing(sample_source, "OpLoad", "%int %gl_SampleID"),
|
||||||
|
"sample index was not loaded from its scalar integer input");
|
||||||
|
CheckSpirvBinaryValidates(sample_result.spirv);
|
||||||
|
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
|
||||||
|
shader[1] = EncodeVop3Word1(5 + 256, 128 + 12, 128 + 4);
|
||||||
ExpectFatal([&] { (void)RecompileForTest(shader, options); },
|
ExpectFatal([&] { (void)RecompileForTest(shader, options); },
|
||||||
"unsupported live ancillary field was silently replaced");
|
"unsupported live ancillary field was silently replaced");
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user