shader: emit dynamic sample offsets with the enabled maintenance8 feature

This commit is contained in:
Ali Almohaya
2026-10-12 19:11:00 +02:00
committed by nmzik
parent 652154aead
commit 460a196fca
11 changed files with 135 additions and 26 deletions
+1 -1
View File
@@ -33,7 +33,7 @@ struct GraphicContext {
// bool fp64_denorm_preserve = false; // Temporarily disabled.
bool attachment_feedback_loop_enabled = false;
bool provoking_vertex_last_enabled = false;
bool depth_color_copy_enabled = false;
bool maintenance8_enabled = false;
bool supports_block_texel_view = false;
bool mesh_shader_enabled = false;
vk::PhysicalDeviceMeshShaderPropertiesEXT mesh_shader_properties = {};
@@ -148,7 +148,7 @@ void DumpShaderOriginal(const char* stage_name, uint64_t shader_hash,
}
bool ValidateShaderSpirv(const char* label, uint64_t shader_hash,
const std::vector<uint32_t>& spirv) {
const std::vector<uint32_t>& spirv, bool maintenance8_enabled) {
if (!Config::ShaderValidationEnabled()) {
return true;
}
@@ -160,7 +160,9 @@ bool ValidateShaderSpirv(const char* label, uint64_t shader_hash,
static_cast<int>(position.column), static_cast<int>(position.index),
message);
});
if (tools.Validate(spirv)) {
spvtools::ValidatorOptions validator_options;
validator_options.SetAllowOffsetTextureOperand(maintenance8_enabled);
if (tools.Validate(spirv.data(), spirv.size(), validator_options)) {
return true;
}
spvtools::SpirvTools disassembler(SPV_ENV_VULKAN_1_2);
@@ -296,14 +298,15 @@ struct PipelineCache::ProgramCache {
std::span<const uint32_t> function_code) {
auto result = ShaderRecompiler::CompileProgram(std::move(translated), options,
specialization, push_data_start_dword);
if (!ValidateShaderSpirv(options.dump_label, options.shader_hash, result.spirv)) {
if (!ValidateShaderSpirv(options.dump_label, options.shader_hash, result.spirv,
options.maintenance8_enabled)) {
DumpShaderSpirv(stage_name, options.shader_hash, result.spirv);
EXIT("%s failed hash=0x%016" PRIx64 ": SPIR-V validation failed\n", options.dump_label,
options.shader_hash);
}
DumpShaderSpirv(stage_name, options.shader_hash, result.spirv);
const auto module = CompileSPV(result.spirv, device);
const auto module = CompileSPV(result.spirv, graphics.device);
EXIT_IF(module == nullptr);
if (options.dump_ir) {
LOGF("%s SPIR-V words=%" PRIu64 " wave_size=%u\n", options.dump_label,
@@ -384,6 +387,7 @@ struct PipelineCache::ProgramCache {
}
ShaderRecompiler::CompileOptions options;
options.stage = stage;
options.maintenance8_enabled = graphics.maintenance8_enabled;
options.shader_hash = params.hash;
options.user_data = user_data;
options.back_code = params.back_code;
@@ -456,26 +460,26 @@ struct PipelineCache::ProgramCache {
return permutation.handle;
}
explicit ProgramCache(vk::Device device): device(device) {
explicit ProgramCache(GraphicContext& graphics): graphics(graphics) {
lookup_key.static_state.reserve(MaxStaticKeyWords);
}
~ProgramCache() {
for (const auto& [key, entry]: programs) {
(void)key;
for (const auto& permutation: entry.permutations) {
device.destroyShaderModule(permutation.handle.module, nullptr);
graphics.device.destroyShaderModule(permutation.handle.module, nullptr);
}
}
}
std::unordered_map<ProgramKey, SourceEntry, ProgramKeyHash> programs;
ProgramKey lookup_key;
vk::Device device;
GraphicContext& graphics;
uint64_t next_shader_id = 0;
};
PipelineCache::PipelineCache(GraphicContext& graphics)
: m_graphics(graphics), m_program_cache(std::make_unique<ProgramCache>(graphics.device)) {
: m_graphics(graphics), m_program_cache(std::make_unique<ProgramCache>(graphics)) {
EXIT_NOT_IMPLEMENTED(!Common::Thread::IsMainThread());
InitializeDriverCache();
}
@@ -1038,7 +1038,7 @@ bool RenderExecutor::CopyDepthToColor(CommandBuffer& buffer, const DrawCallInfo&
const auto& hw = buffer.GetRegisters();
auto& depth = state.depth_info;
auto& color = state.color_info[0];
EXIT_NOT_IMPLEMENTED(!m_context.GetGraphics().depth_color_copy_enabled ||
EXIT_NOT_IMPLEMENTED(!m_context.GetGraphics().maintenance8_enabled ||
!Prospero::IsRectList(ucfg.GetPrimType()) ||
draw.IsIndexed() || draw.index_count != 3 || draw.instance_count != 1 ||
emit.first_vertex != 0 || emit.first_instance != 0 ||
@@ -553,9 +553,9 @@ static vk::Device VulkanCreateDevice(GraphicContext& graphics,
graphics.attachment_feedback_loop_enabled =
feedback_extensions && feedback_layout.attachmentFeedbackLoopLayout &&
feedback_dynamic.attachmentFeedbackLoopDynamicState;
graphics.depth_color_copy_enabled = maintenance8_extension && maintenance8.maintenance8;
LOGF("Vulkan depth/color copy support: %s\n",
graphics.depth_color_copy_enabled ? "true" : "false");
graphics.maintenance8_enabled = maintenance8_extension && maintenance8.maintenance8;
LOGF("Vulkan maintenance8 support: %s\n",
graphics.maintenance8_enabled ? "true" : "false");
LOGF("Vulkan depth feedback support: %s\n",
graphics.attachment_feedback_loop_enabled ? "true" : "false");
if (graphics.mesh_shader_enabled) {
@@ -652,7 +652,7 @@ static vk::Device VulkanCreateDevice(GraphicContext& graphics,
image_atomic_int64.sparseImageInt64Atomics = VK_FALSE;
create_info.pNext = &image_atomic_int64;
}
if (graphics.depth_color_copy_enabled) {
if (graphics.maintenance8_enabled) {
maintenance8.pNext = const_cast<void*>(create_info.pNext);
create_info.pNext = &maintenance8;
}
@@ -885,7 +885,8 @@ CompileResult CompileProgram(TranslateResult translated, const CompileOptions& o
LOGF("%s phase begin: stage=%s hash=0x%016" PRIx64 " SPIR-V EmitProgram\n",
GetDumpLabel(options), StageName(ir.stage), ir.shader_hash);
auto spirv = Spirv::EmitProgram(ir, options.input_info, push_data_start_dword);
auto spirv = Spirv::EmitProgram(ir, options.input_info, push_data_start_dword,
options.maintenance8_enabled);
LOGF("%s phase end: stage=%s hash=0x%016" PRIx64 " SPIR-V EmitProgram words=%" PRIu64
" elapsed_ms=%" PRIu64 "\n",
GetDumpLabel(options), StageName(ir.stage), ir.shader_hash,
@@ -21,6 +21,7 @@ struct CompileOptions {
uint64_t shader_hash = 0;
bool dump_ir = true;
bool early_dump = false;
bool maintenance8_enabled = false;
const char* dump_label = nullptr;
std::span<const uint32_t> user_data;
std::span<const uint32_t> back_code;
@@ -197,7 +197,7 @@ void ValidateNativeProgram(const IR::Program& program, bool lds_storage) {
} // namespace
std::vector<uint32_t> EmitProgram(IR::Program& program, ShaderStageInputInfo input_info,
uint32_t push_data_start_dword) {
uint32_t push_data_start_dword, bool maintenance8_enabled) {
using namespace Emitter;
if (program.stage != ShaderType::Compute && program.stage != ShaderType::Vertex &&
@@ -215,7 +215,7 @@ std::vector<uint32_t> EmitProgram(IR::Program& program, ShaderStageInputInfo inp
}
IR::ValidateProgram(program, true);
program.bindings = {.push_data_start_dword = push_data_start_dword};
EmitterState state(program, input_info);
EmitterState state(program, input_info, maintenance8_enabled);
const auto* workgroup = ShaderWorkgroupInput(program.stage, input_info);
state.lane_count =
workgroup != nullptr && program.wave_size == 64u && workgroup->host_subgroup_size == 32u
@@ -11,7 +11,8 @@ namespace Libs::Graphics::ShaderRecompiler::Spirv {
// Resets and writes backend IDs and descriptor indices; do not emit the same program concurrently.
std::vector<uint32_t> EmitProgram(IR::Program& program, ShaderStageInputInfo input_info,
uint32_t push_data_start_dword = 0);
uint32_t push_data_start_dword = 0,
bool maintenance8_enabled = false);
} // namespace Libs::Graphics::ShaderRecompiler::Spirv
@@ -333,6 +333,7 @@ uint32_t PackedOffset(ValueEmitContext& ctx, IR::Value value, ImageDimension dim
TypeI32Vector(ctx.state, components),
std::span(values).first(components));
}
ctx.state.builder.RequireCapability(spv::CapabilityImageGatherExtended);
mask |= spv::ImageOperandsOffsetMask;
const auto packed = Unary(ctx.state, spv::OpBitcast, TypeI32(ctx.state), ctx.Def(value));
for (uint32_t index = 0; index < components; index++) {
@@ -937,8 +938,8 @@ void EmitImage(ValueEmitContext& ctx, const IR::Inst& inst) {
operand_count = 2;
}
if (layout.offset != NoImageComponent &&
!offset_value.IsImmediate()) {
ctx.Fail(inst, "has a nonconstant sample texel offset");
!offset_value.IsImmediate() && !state.maintenance8_enabled) {
ctx.Fail(inst, "requires maintenance8 for a dynamic sample texel offset");
}
const auto samplers = EmitImageSamplers(ctx, inst);
const auto EmitSample = [&](uint32_t resource, uint32_t array_index) {
@@ -73,9 +73,9 @@ struct ImageDefinition {
};
struct EmitterState {
EmitterState(IR::Program& program_, ShaderStageInputInfo input_info_)
EmitterState(IR::Program& program_, ShaderStageInputInfo input_info_, bool maintenance8_enabled_)
: builder(program_.stage == ShaderType::Mesh ? 0x00010400u : 0x00010300u),
program(program_), input_info(input_info_) {
program(program_), input_info(input_info_), maintenance8_enabled(maintenance8_enabled_) {
if (ShaderWorkgroupInput(program.stage, input_info) != nullptr) {
lds_storage_class = spv::StorageClassWorkgroup;
}
@@ -88,6 +88,7 @@ struct EmitterState {
Builder builder;
IR::Program& program;
ShaderStageInputInfo input_info;
bool maintenance8_enabled;
uint32_t void_type = 0;
uint32_t bool_type = 0;
uint32_t u32_type = 0;