shader: add viewport-index exports and indexed raster state

Co-authored-by: Ettore Sartori <85788613+Ekt0re@users.noreply.github.com>
This commit is contained in:
nmzik
2026-09-06 10:16:38 +04:00
parent dcaf4205cb
commit f9f8e12c35
11 changed files with 140 additions and 84 deletions
+3 -2
View File
@@ -939,7 +939,8 @@ static bool ScissorClipRuleToIntersectionMask(uint16_t rule, uint8_t* mask) {
} }
ScissorRect calc_final_scissor(const HW::ScreenViewport& vp, const HW::ScanModeControl& smc, ScissorRect calc_final_scissor(const HW::ScreenViewport& vp, const HW::ScanModeControl& smc,
vk::Extent2D extent) { vk::Extent2D extent, uint32_t viewport_index) {
EXIT_IF(viewport_index >= std::size(vp.viewports));
ScissorRect screen {vp.screen_scissor_left, vp.screen_scissor_top, vp.screen_scissor_right, ScissorRect screen {vp.screen_scissor_left, vp.screen_scissor_top, vp.screen_scissor_right,
vp.screen_scissor_bottom}; vp.screen_scissor_bottom};
ScissorRect final = screen; ScissorRect final = screen;
@@ -972,7 +973,7 @@ ScissorRect calc_final_scissor(const HW::ScreenViewport& vp, const HW::ScanModeC
final = ScissorRectIntersect(final, generic); final = ScissorRectIntersect(final, generic);
} }
const auto& viewport = vp.viewports[0]; const auto& viewport = vp.viewports[viewport_index];
ScissorRect viewport_scissor {viewport.viewport_scissor_left, viewport.viewport_scissor_top, ScissorRect viewport_scissor {viewport.viewport_scissor_left, viewport.viewport_scissor_top,
viewport.viewport_scissor_right, viewport.viewport_scissor_right,
viewport.viewport_scissor_bottom}; viewport.viewport_scissor_bottom};
+1 -1
View File
@@ -39,7 +39,7 @@ void hw_print(const CommandBuffer& buffer);
void hw_check(const CommandBuffer& buffer); void hw_check(const CommandBuffer& buffer);
void LogDrawPhase(const char* draw_name, const char* phase); void LogDrawPhase(const char* draw_name, const char* phase);
ScissorRect calc_final_scissor(const HW::ScreenViewport& vp, const HW::ScanModeControl& smc, ScissorRect calc_final_scissor(const HW::ScreenViewport& vp, const HW::ScanModeControl& smc,
vk::Extent2D extent); vk::Extent2D extent, uint32_t viewport_index);
} // namespace Libs::Graphics } // namespace Libs::Graphics
@@ -573,9 +573,7 @@ void CreatePipelineInternal(
depth_clip_control.negativeOneToOne = (static_params.negative_one_to_one ? VK_TRUE : VK_FALSE); depth_clip_control.negativeOneToOne = (static_params.negative_one_to_one ? VK_TRUE : VK_FALSE);
vk::PipelineViewportStateCreateInfo viewport_state {}; vk::PipelineViewportStateCreateInfo viewport_state {};
viewport_state.pNext = &depth_clip_control; viewport_state.pNext = &depth_clip_control;
viewport_state.viewportCount = 1;
viewport_state.scissorCount = 1;
vk::CullModeFlags cull_mode = vk::CullModeFlagBits::eNone; vk::CullModeFlags cull_mode = vk::CullModeFlagBits::eNone;
if (static_params.cull_back) { if (static_params.cull_back) {
@@ -704,8 +702,8 @@ void CreatePipelineInternal(
depth_stencil_info.maxDepthBounds = static_params.depth_max_bounds; depth_stencil_info.maxDepthBounds = static_params.depth_max_bounds;
const vk::DynamicState dynamic_states[] = { const vk::DynamicState dynamic_states[] = {
vk::DynamicState::eViewport, vk::DynamicState::eViewportWithCount,
vk::DynamicState::eScissor, vk::DynamicState::eScissorWithCount,
vk::DynamicState::eLineWidth, vk::DynamicState::eLineWidth,
vk::DynamicState::eDepthTestEnable, vk::DynamicState::eDepthTestEnable,
vk::DynamicState::eDepthWriteEnable, vk::DynamicState::eDepthWriteEnable,
+42 -26
View File
@@ -210,7 +210,7 @@ static void LogDrawTargetState(const char* draw_name, const RenderColorInfo& col
}); });
const auto extent = color.Extent(); const auto extent = color.Extent();
const auto sc = calc_final_scissor(vp, ctx.GetScanModeControl(), extent); const auto sc = calc_final_scissor(vp, ctx.GetScanModeControl(), extent, 0);
LOGF( LOGF(
"DrawTargetState[%u]: frame=%d %s target=%s addr=0x%010" PRIx64 "DrawTargetState[%u]: frame=%d %s target=%s addr=0x%010" PRIx64
@@ -315,6 +315,7 @@ static void LogDrawInputState(const CommandBuffer& buffer, const RenderColorInfo
} }
static void SetGraphicsDynamicParams(const CommandBuffer& buffer, vk::CommandBuffer vk_buffer, static void SetGraphicsDynamicParams(const CommandBuffer& buffer, vk::CommandBuffer vk_buffer,
const ShaderVertexInputInfo& vs_input_info,
const RenderColorInfo* colors, uint32_t color_count, const RenderColorInfo* colors, uint32_t color_count,
const RenderDepthInfo& depth) { const RenderDepthInfo& depth) {
KYTY_PROFILER_FUNCTION(); KYTY_PROFILER_FUNCTION();
@@ -333,31 +334,46 @@ static void SetGraphicsDynamicParams(const CommandBuffer& buffer, vk::CommandBuf
framebuffer_extent = {limits.maxFramebufferWidth, limits.maxFramebufferHeight}; framebuffer_extent = {limits.maxFramebufferWidth, limits.maxFramebufferHeight};
} }
const auto final_scissor = calc_final_scissor(vp, ctx.GetScanModeControl(), framebuffer_extent); const auto& outputs = vs_input_info.stage.program->info.outputs;
const bool indexed_viewports =
std::any_of(outputs.begin(), outputs.end(), [](const auto& output) {
return output.kind == ShaderRecompiler::IR::StageOutputKind::ViewportIndex;
});
constexpr uint32_t viewport_slots = std::size(HW::ScreenViewport {}.viewports);
std::array<vk::Viewport, viewport_slots> viewports {};
std::array<vk::Rect2D, viewport_slots> scissors {};
const uint32_t viewport_count = indexed_viewports ? viewport_slots : 1;
for (uint32_t i = 0; i < viewport_count; i++) {
const auto& guest = vp.viewports[i];
auto& viewport = viewports[i];
if (ctx.GetClipControl().clip_disable) {
const auto& limits = buffer.GetGraphics().GetPhysicalDeviceProperties().limits;
viewport.width = static_cast<float>(std::min(limits.maxViewportDimensions[0], 16384u));
viewport.height = static_cast<float>(std::min(limits.maxViewportDimensions[1], 16384u));
} else {
viewport.x = guest.xoffset - guest.xscale;
viewport.y = guest.yoffset - guest.yscale;
viewport.width = guest.xscale * 2.0f;
viewport.height = guest.yscale * 2.0f;
}
viewport.minDepth =
guest.zoffset - (ctx.GetClipControl().dx_clip_space ? 0.0f : guest.zscale);
viewport.maxDepth = guest.zscale + guest.zoffset;
vk::Viewport viewport {}; const auto final_scissor =
if (ctx.GetClipControl().clip_disable) { calc_final_scissor(vp, ctx.GetScanModeControl(), framebuffer_extent, i);
const auto& limits = buffer.GetGraphics().GetPhysicalDeviceProperties().limits; auto& scissor = scissors[i];
viewport.x = 0.0f; scissor.offset = {final_scissor.left, final_scissor.top};
viewport.y = 0.0f; scissor.extent = {static_cast<uint32_t>(final_scissor.right - final_scissor.left),
viewport.width = static_cast<float>(std::min(limits.maxViewportDimensions[0], 16384u)); static_cast<uint32_t>(final_scissor.bottom - final_scissor.top)};
viewport.height = static_cast<float>(std::min(limits.maxViewportDimensions[1], 16384u)); if (viewport.width == 0.0f) {
} else { // Keep empty slots at their guest index; Vulkan requires a positive viewport width.
viewport.x = vp.viewports[0].xoffset - vp.viewports[0].xscale; viewport.width = 1.0f;
viewport.y = vp.viewports[0].yoffset - vp.viewports[0].yscale; scissor.extent = {0, 0};
viewport.width = vp.viewports[0].xscale * 2.0f; }
viewport.height = vp.viewports[0].yscale * 2.0f;
} }
viewport.minDepth = vp.viewports[0].zoffset - vk_buffer.setViewportWithCount(viewport_count, viewports.data());
(ctx.GetClipControl().dx_clip_space ? 0.0f : vp.viewports[0].zscale); vk_buffer.setScissorWithCount(viewport_count, scissors.data());
viewport.maxDepth = vp.viewports[0].zscale + vp.viewports[0].zoffset;
vk_buffer.setViewport(0, 1, &viewport);
vk::Rect2D scissor {};
scissor.offset = {final_scissor.left, final_scissor.top};
scissor.extent = {static_cast<uint32_t>(final_scissor.right - final_scissor.left),
static_cast<uint32_t>(final_scissor.bottom - final_scissor.top)};
vk_buffer.setScissor(0, 1, &scissor);
float line_width = ctx.GetLineWidth(); float line_width = ctx.GetLineWidth();
if (line_width != 1.0f) { if (line_width != 1.0f) {
@@ -1125,8 +1141,8 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff
CommitIndexBuffer(vk_buffer, index_binding); CommitIndexBuffer(vk_buffer, index_binding);
} }
SetGraphicsDynamicParams(buffer, vk_buffer, state.color_info, state.color_count, SetGraphicsDynamicParams(buffer, vk_buffer, state.vs_input_info, state.color_info,
state.depth_info); state.color_count, state.depth_info);
LogDrawPhase(draw.name, "BeginRendering"); LogDrawPhase(draw.name, "BeginRendering");
if (set_auto_debug) { if (set_auto_debug) {
@@ -65,12 +65,13 @@ struct VulkanExtensions {
vk::PhysicalDeviceVulkan12Features WindowContext::RequiredVulkan12Features() noexcept { vk::PhysicalDeviceVulkan12Features WindowContext::RequiredVulkan12Features() noexcept {
vk::PhysicalDeviceVulkan12Features features {}; vk::PhysicalDeviceVulkan12Features features {};
features.sType = vk::StructureType::ePhysicalDeviceVulkan12Features; features.sType = vk::StructureType::ePhysicalDeviceVulkan12Features;
features.samplerMirrorClampToEdge = VK_TRUE; features.samplerMirrorClampToEdge = VK_TRUE;
features.timelineSemaphore = VK_TRUE; features.timelineSemaphore = VK_TRUE;
features.shaderOutputLayer = VK_TRUE; features.shaderOutputLayer = VK_TRUE;
features.bufferDeviceAddress = VK_TRUE; features.shaderOutputViewportIndex = VK_TRUE;
features.shaderBufferInt64Atomics = VK_TRUE; features.bufferDeviceAddress = VK_TRUE;
features.shaderBufferInt64Atomics = VK_TRUE;
return features; return features;
} }
@@ -276,6 +277,11 @@ static void VulkanFindPhysicalDevice(vk::Instance instance, vk::SurfaceKHR surfa
LOGF("shaderOutputLayer is not supported\n"); LOGF("shaderOutputLayer is not supported\n");
skip_device = true; skip_device = true;
} }
if (required_features12.shaderOutputViewportIndex == VK_TRUE &&
features12.shaderOutputViewportIndex != VK_TRUE) {
LOGF("shaderOutputViewportIndex is not supported\n");
skip_device = true;
}
if (required_features12.bufferDeviceAddress == VK_TRUE && if (required_features12.bufferDeviceAddress == VK_TRUE &&
features12.bufferDeviceAddress != VK_TRUE) { features12.bufferDeviceAddress != VK_TRUE) {
LOGF("bufferDeviceAddress is not supported\n"); LOGF("bufferDeviceAddress is not supported\n");
@@ -320,6 +326,10 @@ static void VulkanFindPhysicalDevice(vk::Instance instance, vk::SurfaceKHR surfa
LOGF("largePoints is not supported\n"); LOGF("largePoints is not supported\n");
skip_device = true; skip_device = true;
} }
if (device_features2.features.multiViewport != VK_TRUE) {
LOGF("multiViewport is not supported\n");
skip_device = true;
}
if (device_features2.features.fragmentStoresAndAtomics != VK_TRUE) { if (device_features2.features.fragmentStoresAndAtomics != VK_TRUE) {
LOGF("fragmentStoresAndAtomics is not supported\n"); LOGF("fragmentStoresAndAtomics is not supported\n");
skip_device = true; skip_device = true;
@@ -636,10 +646,13 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, const V
EXIT_NOT_IMPLEMENTED(supported_features2.features.shaderClipDistance != VK_TRUE); EXIT_NOT_IMPLEMENTED(supported_features2.features.shaderClipDistance != VK_TRUE);
EXIT_NOT_IMPLEMENTED(supported_features2.features.shaderCullDistance != VK_TRUE); EXIT_NOT_IMPLEMENTED(supported_features2.features.shaderCullDistance != VK_TRUE);
EXIT_NOT_IMPLEMENTED(supported_features2.features.largePoints != VK_TRUE); EXIT_NOT_IMPLEMENTED(supported_features2.features.largePoints != VK_TRUE);
EXIT_NOT_IMPLEMENTED(supported_features2.features.multiViewport != VK_TRUE);
EXIT_NOT_IMPLEMENTED(supported_features2.features.shaderInt64 != VK_TRUE); EXIT_NOT_IMPLEMENTED(supported_features2.features.shaderInt64 != VK_TRUE);
EXIT_NOT_IMPLEMENTED(supported_features2.features.vertexPipelineStoresAndAtomics != VK_TRUE); EXIT_NOT_IMPLEMENTED(supported_features2.features.vertexPipelineStoresAndAtomics != VK_TRUE);
EXIT_NOT_IMPLEMENTED(required_features12.shaderOutputLayer == VK_TRUE && EXIT_NOT_IMPLEMENTED(required_features12.shaderOutputLayer == VK_TRUE &&
supported_features12.shaderOutputLayer != VK_TRUE); supported_features12.shaderOutputLayer != VK_TRUE);
EXIT_NOT_IMPLEMENTED(required_features12.shaderOutputViewportIndex == VK_TRUE &&
supported_features12.shaderOutputViewportIndex != VK_TRUE);
EXIT_NOT_IMPLEMENTED(required_features12.bufferDeviceAddress == VK_TRUE && EXIT_NOT_IMPLEMENTED(required_features12.bufferDeviceAddress == VK_TRUE &&
supported_features12.bufferDeviceAddress != VK_TRUE); supported_features12.bufferDeviceAddress != VK_TRUE);
EXIT_NOT_IMPLEMENTED(required_features12.shaderBufferInt64Atomics == VK_TRUE && EXIT_NOT_IMPLEMENTED(required_features12.shaderBufferInt64Atomics == VK_TRUE &&
@@ -663,6 +676,7 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, const V
device_features.shaderClipDistance = VK_TRUE; device_features.shaderClipDistance = VK_TRUE;
device_features.shaderCullDistance = VK_TRUE; device_features.shaderCullDistance = VK_TRUE;
device_features.largePoints = VK_TRUE; device_features.largePoints = VK_TRUE;
device_features.multiViewport = VK_TRUE;
device_features.vertexPipelineStoresAndAtomics = VK_TRUE; device_features.vertexPipelineStoresAndAtomics = VK_TRUE;
graphics.sample_rate_shading_enabled = true; graphics.sample_rate_shading_enabled = true;
device_features.shaderInt64 = VK_TRUE; device_features.shaderInt64 = VK_TRUE;
@@ -338,15 +338,24 @@ void EmitAuxPositionExport(ValueEmitContext& ctx, uint32_t data, const IR::Expor
} }
const auto output = IR::DecodePositionExportComponent( const auto output = IR::DecodePositionExportComponent(
state.input_info.vertex->pa_cl_vs_out_cntl, exp.index, component); state.input_info.vertex->pa_cl_vs_out_cntl, exp.index, component);
if (output.layer) { if (output.layer || output.viewport) {
const auto raw = ExportRawComponent(ctx, data, component); const auto raw = ExportRawComponent(ctx, data, component);
const auto layer = state.builder.AllocateId(); if (output.layer) {
state.builder.AddFunction({OpBitwiseAnd, TypeU32(state), layer, raw, const auto layer = state.builder.AllocateId();
ConstantU32(state, 0x7ffu)}); state.builder.AddFunction({OpBitwiseAnd, TypeU32(state), layer, raw,
const auto pointer = state.stage == ShaderType::Mesh ConstantU32(state, 0x7ffu)});
? MeshOutputPointer(state, IR::StageOutputKind::Layer) const auto pointer = state.stage == ShaderType::Mesh
: state.layer_variable; ? MeshOutputPointer(state, IR::StageOutputKind::Layer)
state.builder.AddFunction({OpStore, pointer, layer}); : state.layer_variable;
state.builder.AddFunction({OpStore, pointer, layer});
}
if (output.viewport) {
// GFX10 MISC.z packs the viewport index in bits 16..19 alongside the layer.
const auto viewport = state.builder.AllocateId();
state.builder.AddFunction({OpBitFieldUExtract, TypeU32(state), viewport, raw,
ConstantU32(state, 16), ConstantU32(state, 4)});
state.builder.AddFunction({OpStore, state.viewport_index_variable, viewport});
}
continue; continue;
} }
if (!output.point_size && output.clip_distance == UINT32_MAX && if (!output.point_size && output.clip_distance == UINT32_MAX &&
@@ -52,6 +52,7 @@ enum : uint32_t {
CapabilityGroupNonUniformBallot = 64, CapabilityGroupNonUniformBallot = 64,
CapabilityGroupNonUniformShuffle = 65, CapabilityGroupNonUniformShuffle = 65,
CapabilityShaderLayer = 69, CapabilityShaderLayer = 69,
CapabilityShaderViewportIndex = 70,
CapabilitySignedZeroInfNanPreserve = 4466, CapabilitySignedZeroInfNanPreserve = 4466,
CapabilityFragmentBarycentricKHR = 5284, CapabilityFragmentBarycentricKHR = 5284,
CapabilityComputeDerivativeGroupQuadsKHR = 5288, CapabilityComputeDerivativeGroupQuadsKHR = 5288,
@@ -91,6 +92,7 @@ enum : uint32_t {
BuiltInClipDistance = 3, BuiltInClipDistance = 3,
BuiltInCullDistance = 4, BuiltInCullDistance = 4,
BuiltInLayer = 9, BuiltInLayer = 9,
BuiltInViewportIndex = 10,
BuiltInFragCoord = 15, BuiltInFragCoord = 15,
BuiltInFrontFacing = 17, BuiltInFrontFacing = 17,
BuiltInSampleId = 18, BuiltInSampleId = 18,
@@ -391,6 +393,7 @@ struct EmitterState {
uint32_t clip_distance_variable = 0; uint32_t clip_distance_variable = 0;
uint32_t cull_distance_variable = 0; uint32_t cull_distance_variable = 0;
uint32_t layer_variable = 0; uint32_t layer_variable = 0;
uint32_t viewport_index_variable = 0;
uint32_t clip_distance_count = 0; uint32_t clip_distance_count = 0;
uint32_t cull_distance_count = 0; uint32_t cull_distance_count = 0;
uint32_t depth_variable = 0; uint32_t depth_variable = 0;
@@ -490,6 +490,10 @@ void AllocateOutputVariables(EmitterState& state) {
case IR::StageOutputKind::Layer: case IR::StageOutputKind::Layer:
binding.variable_id = AllocateSharedOutputVariable(state, state.layer_variable); binding.variable_id = AllocateSharedOutputVariable(state, state.layer_variable);
break; break;
case IR::StageOutputKind::ViewportIndex:
binding.variable_id =
AllocateSharedOutputVariable(state, state.viewport_index_variable);
break;
case IR::StageOutputKind::Depth: case IR::StageOutputKind::Depth:
binding.variable_id = AllocateSharedOutputVariable(state, state.depth_variable); binding.variable_id = AllocateSharedOutputVariable(state, state.depth_variable);
break; break;
@@ -583,6 +587,7 @@ void AddOutputAnnotationsAndNames(EmitterState& state) {
AddBuiltIn(state.clip_distance_variable, "gl_ClipDistance", BuiltInClipDistance); AddBuiltIn(state.clip_distance_variable, "gl_ClipDistance", BuiltInClipDistance);
AddBuiltIn(state.cull_distance_variable, "gl_CullDistance", BuiltInCullDistance); AddBuiltIn(state.cull_distance_variable, "gl_CullDistance", BuiltInCullDistance);
AddBuiltIn(state.layer_variable, "gl_Layer", BuiltInLayer); AddBuiltIn(state.layer_variable, "gl_Layer", BuiltInLayer);
AddBuiltIn(state.viewport_index_variable, "gl_ViewportIndex", BuiltInViewportIndex);
if (state.depth_variable != 0) { if (state.depth_variable != 0) {
state.builder.AddName(state.depth_variable, "gl_FragDepth"); state.builder.AddName(state.depth_variable, "gl_FragDepth");
state.builder.AddAnnotation( state.builder.AddAnnotation(
@@ -712,6 +717,10 @@ void DefineModule(EmitterState& state) {
state.builder.RequireVersion(0x00010500u); state.builder.RequireVersion(0x00010500u);
state.builder.RequireCapability(CapabilityShaderLayer); state.builder.RequireCapability(CapabilityShaderLayer);
} }
if (state.viewport_index_variable != 0) {
state.builder.RequireVersion(0x00010500u);
state.builder.RequireCapability(CapabilityShaderViewportIndex);
}
if (InputVariableForKind(state, IR::StageInputKind::SampleId) != 0) { if (InputVariableForKind(state, IR::StageInputKind::SampleId) != 0) {
state.builder.RequireCapability(CapabilitySampleRateShading); state.builder.RequireCapability(CapabilitySampleRateShading);
} }
@@ -874,6 +883,11 @@ void DefineModule(EmitterState& state) {
state.layer_variable, TypePointer(state, StorageClassOutput, TypeU32(state)), state.layer_variable, TypePointer(state, StorageClassOutput, TypeU32(state)),
StorageClassOutput); StorageClassOutput);
} }
if (state.viewport_index_variable != 0) {
state.builder.DefineGlobalVariable(
state.viewport_index_variable, TypePointer(state, StorageClassOutput, TypeU32(state)),
StorageClassOutput);
}
for (const auto& binding: state.outputs) { for (const auto& binding: state.outputs) {
if (binding.kind == IR::StageOutputKind::Parameter || if (binding.kind == IR::StageOutputKind::Parameter ||
binding.kind == IR::StageOutputKind::Mrt) { binding.kind == IR::StageOutputKind::Mrt) {
+2 -1
View File
@@ -181,7 +181,8 @@ enum class StageOutputKind {
PointSize, PointSize,
ClipDistance, ClipDistance,
CullDistance, CullDistance,
Layer Layer,
ViewportIndex
}; };
struct PositionExportComponent { struct PositionExportComponent {
@@ -324,7 +324,7 @@ void CollectOutputs(const Program& program, const ShaderVertexInputInfo* vertex,
const auto output = DecodePositionExportComponent( const auto output = DecodePositionExportComponent(
vertex->pa_cl_vs_out_cntl, export_info.index, component); vertex->pa_cl_vs_out_cntl, export_info.index, component);
if (output.viewport) { if (output.viewport) {
return Fail("vertex viewport-index export is unsupported"); AddOutput(info, StageOutputKind::ViewportIndex, 0, 0, "gl_ViewportIndex");
} }
if (output.point_size) { if (output.point_size) {
AddOutput(info, StageOutputKind::PointSize, 0, 0, "gl_PointSize"); AddOutput(info, StageOutputKind::PointSize, 0, 0, "gl_PointSize");
+33 -33
View File
@@ -37,6 +37,7 @@
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <deque> #include <deque>
#include <initializer_list>
#include <iterator> #include <iterator>
#include <span> #include <span>
#include <sstream> #include <sstream>
@@ -907,12 +908,12 @@ SpirvStoredBuiltInElements(const std::vector<uint32_t> &binary,
return elements; return elements;
} }
bool SpirvBuiltInStoreUsesAndConstant(const std::vector<uint32_t> &binary, bool SpirvBuiltInStoreUsesOperation(const std::vector<uint32_t> &binary,
uint32_t builtin, uint32_t builtin, uint32_t operation,
uint32_t constant) { std::initializer_list<uint32_t> literals) {
std::unordered_set<uint32_t> variables; std::unordered_set<uint32_t> variables;
std::unordered_map<uint32_t, uint32_t> constants; std::unordered_map<uint32_t, uint32_t> constants;
std::unordered_map<uint32_t, std::array<uint32_t, 2>> bitwise_ands; std::unordered_map<uint32_t, std::vector<uint32_t>> operations;
std::unordered_map<uint32_t, uint32_t> stores; std::unordered_map<uint32_t, uint32_t> stores;
for (size_t i = 5; i < binary.size();) { for (size_t i = 5; i < binary.size();) {
const uint32_t opcode = binary[i] & 0xffffu; const uint32_t opcode = binary[i] & 0xffffu;
@@ -925,8 +926,8 @@ bool SpirvBuiltInStoreUsesAndConstant(const std::vector<uint32_t> &binary,
variables.insert(binary[i + 1]); variables.insert(binary[i + 1]);
} else if (opcode == 43u && count >= 4u) { } else if (opcode == 43u && count >= 4u) {
constants[binary[i + 2]] = binary[i + 3]; constants[binary[i + 2]] = binary[i + 3];
} else if (opcode == 199u && count == 5u) { } else if (opcode == operation && count == 4u + literals.size()) {
bitwise_ands[binary[i + 2]] = {binary[i + 3], binary[i + 4]}; operations[binary[i + 2]] = {binary.begin() + i + 4, binary.begin() + i + count};
} else if (opcode == 62u && count >= 3u) { } else if (opcode == 62u && count >= 3u) {
stores[binary[i + 1]] = binary[i + 2]; stores[binary[i + 1]] = binary[i + 2];
} }
@@ -937,15 +938,16 @@ bool SpirvBuiltInStoreUsesAndConstant(const std::vector<uint32_t> &binary,
if (store == stores.end()) { if (store == stores.end()) {
continue; continue;
} }
const auto value = bitwise_ands.find(store->second); const auto value = operations.find(store->second);
if (value == bitwise_ands.end()) { if (value == operations.end()) {
continue; continue;
} }
for (const auto operand : value->second) { if (std::equal(value->second.begin(), value->second.end(), literals.begin(),
const auto literal = constants.find(operand); [&](uint32_t id, uint32_t expected) {
if (literal != constants.end() && literal->second == constant) { const auto literal = constants.find(id);
return true; return literal != constants.end() && literal->second == expected;
} })) {
return true;
} }
} }
return false; return false;
@@ -9722,7 +9724,7 @@ void TestNewShaderRecompilerAuxPositionExports() {
const auto all_source = DisassembleSpirvBinary(all.spirv); const auto all_source = DisassembleSpirvBinary(all.spirv);
Check(all.spirv[1] == 0x00010500u && Check(all.spirv[1] == 0x00010500u &&
!Common::ContainsStr(all_source, "SPV_EXT_shader_viewport_index_layer") && !Common::ContainsStr(all_source, "SPV_EXT_shader_viewport_index_layer") &&
SpirvBuiltInStoreUsesAndConstant(all.spirv, 9u, 0x7ffu), SpirvBuiltInStoreUsesOperation(all.spirv, 9u, 199u, {0x7ffu}),
"layer export module version or GFX10 layer mask is incorrect"); "layer export module version or GFX10 layer mask is incorrect");
const auto positions = std::count_if( const auto positions = std::count_if(
all.program.info.outputs.begin(), all.program.info.outputs.end(), all.program.info.outputs.begin(), all.program.info.outputs.end(),
@@ -9776,25 +9778,23 @@ void TestNewShaderRecompilerAuxPositionExports() {
ShaderRecompiler::IR::StageOutputKind::Position, ShaderRecompiler::IR::StageOutputKind::Position,
"unmapped auxiliary position export was not ignored"); "unmapped auxiliary position export was not ignored");
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS // R-Type Final 3 packs a dynamic viewport index into POS1.z bits 16..19.
const auto rejects = [](uint32_t control, uint32_t en, // The same component can carry an independent render-target layer in bits 0..10.
const char *message) { for (const uint32_t control : {0x01280000u, 0x012c0000u}) {
const uint32_t shader[] = { const auto indexed = compile(unmapped, control);
EncodeExp0(0x0c, 0xf, false), EncodeExp1(0, 1, 2, 3), CheckSpirvBinaryValidates(indexed.spirv);
EncodeExp0(0x0d, en), EncodeExp1(4, 5, 6, 7), Check(indexed.spirv[1] == 0x00010500u &&
0xbf810000u, SpirvContainsCapability(indexed.spirv, 70u) &&
}; SpirvBuiltInStoreUsesOperation(indexed.spirv, 10u, 203u, {16u, 4u}),
ShaderVertexInputInfo vertex{}; "viewport export did not store the GFX10 four-bit index");
vertex.pa_cl_vs_out_cntl = control; const bool layered = (control & (1u << 18u)) != 0;
auto options = MakeCompileOptions(ShaderType::Vertex); Check(SpirvBuiltInStoreUsesOperation(indexed.spirv, 9u, 199u, {0x7ffu}) == layered,
options.input_info.vertex = &vertex; "packed layer/viewport export dropped or invented a layer store");
ExpectFatal([&] { (void)RecompileForTest(shader, options); }, Check(std::ranges::any_of(indexed.program.info.outputs, [](const auto &output) {
message); return output.kind == ShaderRecompiler::IR::StageOutputKind::ViewportIndex;
}; }),
rejects(0x00280000u, 0x4u, "viewport export was omitted from shader reflection");
"viewport-index auxiliary position export did not terminate " }
"compilation");
#endif
ShaderVertexInputInfo key0{}; ShaderVertexInputInfo key0{};
ShaderVertexInputInfo key1{}; ShaderVertexInputInfo key1{};