Compare commits

..

2 Commits

Author SHA1 Message Date
PavelBARABANOV
8c99f0c166 Revert [shader_recompiler, spir-v] Adding INT64 emulation path 2025-11-18 21:01:17 +03:00
PavelBARABANOV
520e07e756 test 2025-11-17 17:45:36 +03:00
25 changed files with 415 additions and 1070 deletions

View File

@@ -546,15 +546,6 @@ struct Values {
Category::RendererExtensions,
Specialization::Scalar};
SwitchableSetting<bool> vertex_input_dynamic_state{linkage,
#if defined (ANDROID)
false, // Disabled by default on Android (some drivers have issues)
#else
false, // Disabled by default on desktop (some drivers have issues)
#endif
"vertex_input_dynamic_state",
Category::RendererExtensions};
SwitchableSetting<bool> provoking_vertex{linkage, false, "provoking_vertex", Category::RendererExtensions};
SwitchableSetting<bool> descriptor_indexing{linkage, false, "descriptor_indexing", Category::RendererExtensions};
SwitchableSetting<bool> sample_shading{linkage, false, "sample_shading", Category::RendererExtensions, Specialization::Paired};

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -321,23 +318,13 @@ void AddOffsetToCoordinates(EmitContext& ctx, const IR::TextureInstInfo& info, I
return;
}
// Mobile GPUs: 1D textures emulated as 2D with height=1
const bool emulate_1d = ctx.profile.needs_1d_texture_emulation;
Id result_type{};
switch (info.type) {
case TextureType::Buffer:
case TextureType::Color1D: {
result_type = ctx.U32[1];
break;
case TextureType::Color1D:
if (emulate_1d) {
// Treat as 2D: offset needs Y component
offset = ctx.OpCompositeConstruct(ctx.U32[2], offset, ctx.u32_zero_value);
result_type = ctx.U32[2];
} else {
result_type = ctx.U32[1];
}
break;
}
case TextureType::ColorArray1D:
offset = ctx.OpCompositeConstruct(ctx.U32[2], offset, ctx.u32_zero_value);
[[fallthrough]];
@@ -361,40 +348,6 @@ void AddOffsetToCoordinates(EmitContext& ctx, const IR::TextureInstInfo& info, I
}
coords = ctx.OpIAdd(result_type, coords, offset);
}
// Helper: Convert 1D coordinates to 2D when emulating 1D textures on mobile GPUs
[[nodiscard]] Id AdjustCoordinatesForEmulation(EmitContext& ctx, const IR::TextureInstInfo& info,
Id coords) {
if (!ctx.profile.needs_1d_texture_emulation) {
return coords;
}
switch (info.type) {
case TextureType::Color1D: {
// Convert scalar → vec2(x, 0.0)
return ctx.OpCompositeConstruct(ctx.F32[2], coords, ctx.f32_zero_value);
}
case TextureType::ColorArray1D: {
// Convert vec2(x, layer) → vec3(x, 0.0, layer)
// ColorArray1D coords are always vec2 in IR
const Id x = ctx.OpCompositeExtract(ctx.F32[1], coords, 0);
const Id layer = ctx.OpCompositeExtract(ctx.F32[1], coords, 1);
return ctx.OpCompositeConstruct(ctx.F32[3], x, ctx.f32_zero_value, layer);
}
case TextureType::Color2D:
case TextureType::ColorArray2D:
case TextureType::Color3D:
case TextureType::ColorCube:
case TextureType::ColorArrayCube:
case TextureType::Buffer:
case TextureType::Color2DRect:
// No adjustment needed for non-1D textures
return coords;
}
return coords; // Unreachable, but silences -Werror=return-type
}
} // Anonymous namespace
Id EmitBindlessImageSampleImplicitLod(EmitContext&) {
@@ -496,7 +449,6 @@ Id EmitBoundImageWrite(EmitContext&) {
Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
Id bias_lc, const IR::Value& offset) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
coords = AdjustCoordinatesForEmulation(ctx, info, coords);
if (ctx.stage == Stage::Fragment) {
const ImageOperands operands(ctx, info.has_bias != 0, false, info.has_lod_clamp != 0,
bias_lc, offset);
@@ -518,7 +470,6 @@ Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value&
Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
Id lod, const IR::Value& offset) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
coords = AdjustCoordinatesForEmulation(ctx, info, coords);
const ImageOperands operands(ctx, false, true, false, lod, offset);
return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
&EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
@@ -528,7 +479,6 @@ Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value&
Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
Id coords, Id dref, Id bias_lc, const IR::Value& offset) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
coords = AdjustCoordinatesForEmulation(ctx, info, coords);
if (ctx.stage == Stage::Fragment) {
const ImageOperands operands(ctx, info.has_bias != 0, false, info.has_lod_clamp != 0,
bias_lc, offset);
@@ -550,7 +500,6 @@ Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Va
Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
Id coords, Id dref, Id lod, const IR::Value& offset) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
coords = AdjustCoordinatesForEmulation(ctx, info, coords);
const ImageOperands operands(ctx, false, true, false, lod, offset);
return Emit(&EmitContext::OpImageSparseSampleDrefExplicitLod,
&EmitContext::OpImageSampleDrefExplicitLod, ctx, inst, ctx.F32[1],
@@ -560,7 +509,6 @@ Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Va
Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
const IR::Value& offset, const IR::Value& offset2) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
coords = AdjustCoordinatesForEmulation(ctx, info, coords);
const ImageOperands operands(ctx, offset, offset2);
if (ctx.profile.need_gather_subpixel_offset) {
coords = ImageGatherSubpixelOffset(ctx, info, TextureImage(ctx, info, index), coords);
@@ -573,7 +521,6 @@ Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id
Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
const IR::Value& offset, const IR::Value& offset2, Id dref) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
coords = AdjustCoordinatesForEmulation(ctx, info, coords);
const ImageOperands operands(ctx, offset, offset2);
if (ctx.profile.need_gather_subpixel_offset) {
coords = ImageGatherSubpixelOffset(ctx, info, TextureImage(ctx, info, index), coords);
@@ -586,7 +533,6 @@ Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset,
Id lod, Id ms) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
coords = AdjustCoordinatesForEmulation(ctx, info, coords);
AddOffsetToCoordinates(ctx, info, coords, offset);
if (info.type == TextureType::Buffer) {
lod = Id{};
@@ -613,20 +559,9 @@ Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& i
return uses_lod ? ctx.OpImageQuerySizeLod(type, image, lod)
: ctx.OpImageQuerySize(type, image);
}};
// Mobile GPUs: 1D textures emulated as 2D, query returns vec2 instead of scalar
const bool emulate_1d = ctx.profile.needs_1d_texture_emulation;
switch (info.type) {
case TextureType::Color1D:
if (emulate_1d) {
// Query as 2D, extract only X component for 1D size
const Id size_2d = query(ctx.U32[2]);
const Id width = ctx.OpCompositeExtract(ctx.U32[1], size_2d, 0);
return ctx.OpCompositeConstruct(ctx.U32[4], width, zero, zero, mips());
} else {
return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[1]), zero, zero, mips());
}
return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[1]), zero, zero, mips());
case TextureType::ColorArray1D:
case TextureType::Color2D:
case TextureType::ColorCube:
@@ -644,7 +579,6 @@ Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& i
Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
coords = AdjustCoordinatesForEmulation(ctx, info, coords);
const Id zero{ctx.f32_zero_value};
const Id sampler{Texture(ctx, info, index)};
return ctx.OpCompositeConstruct(ctx.F32[4], ctx.OpImageQueryLod(ctx.F32[2], sampler, coords),
@@ -654,7 +588,6 @@ Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, I
Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
Id derivatives, const IR::Value& offset, Id lod_clamp) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
coords = AdjustCoordinatesForEmulation(ctx, info, coords);
const auto operands = info.num_derivatives == 3
? ImageOperands(ctx, info.has_lod_clamp != 0, derivatives,
ctx.Def(offset), {}, lod_clamp)
@@ -667,7 +600,6 @@ Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, I
Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
coords = AdjustCoordinatesForEmulation(ctx, info, coords);
if (info.image_format == ImageFormat::Typeless && !ctx.profile.support_typeless_image_loads) {
LOG_WARNING(Shader_SPIRV, "Typeless image read not supported by host");
return ctx.ConstantNull(ctx.U32[4]);
@@ -684,7 +616,6 @@ Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id co
void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
coords = AdjustCoordinatesForEmulation(ctx, info, coords);
const auto [image, is_integer] = Image(ctx, index, info);
if (!is_integer) {
color = ctx.OpBitcast(ctx.F32[4], color);

View File

@@ -95,7 +95,7 @@ void EmitLoadGlobalS16(EmitContext&) {
}
Id EmitLoadGlobal32(EmitContext& ctx, Id address) {
if (ctx.SupportsNativeInt64() || ctx.UsesInt64Emulation()) {
if (ctx.profile.support_int64) {
return ctx.OpFunctionCall(ctx.U32[1], ctx.load_global_func_u32, address);
}
LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation");
@@ -103,7 +103,7 @@ Id EmitLoadGlobal32(EmitContext& ctx, Id address) {
}
Id EmitLoadGlobal64(EmitContext& ctx, Id address) {
if (ctx.SupportsNativeInt64() || ctx.UsesInt64Emulation()) {
if (ctx.profile.support_int64) {
return ctx.OpFunctionCall(ctx.U32[2], ctx.load_global_func_u32x2, address);
}
LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation");
@@ -111,7 +111,7 @@ Id EmitLoadGlobal64(EmitContext& ctx, Id address) {
}
Id EmitLoadGlobal128(EmitContext& ctx, Id address) {
if (ctx.SupportsNativeInt64() || ctx.UsesInt64Emulation()) {
if (ctx.profile.support_int64) {
return ctx.OpFunctionCall(ctx.U32[4], ctx.load_global_func_u32x4, address);
}
LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation");
@@ -135,7 +135,7 @@ void EmitWriteGlobalS16(EmitContext&) {
}
void EmitWriteGlobal32(EmitContext& ctx, Id address, Id value) {
if (ctx.SupportsNativeInt64() || ctx.UsesInt64Emulation()) {
if (ctx.profile.support_int64) {
ctx.OpFunctionCall(ctx.void_id, ctx.write_global_func_u32, address, value);
return;
}
@@ -143,7 +143,7 @@ void EmitWriteGlobal32(EmitContext& ctx, Id address, Id value) {
}
void EmitWriteGlobal64(EmitContext& ctx, Id address, Id value) {
if (ctx.SupportsNativeInt64() || ctx.UsesInt64Emulation()) {
if (ctx.profile.support_int64) {
ctx.OpFunctionCall(ctx.void_id, ctx.write_global_func_u32x2, address, value);
return;
}
@@ -151,7 +151,7 @@ void EmitWriteGlobal64(EmitContext& ctx, Id address, Id value) {
}
void EmitWriteGlobal128(EmitContext& ctx, Id address, Id value) {
if (ctx.SupportsNativeInt64() || ctx.UsesInt64Emulation()) {
if (ctx.profile.support_int64) {
ctx.OpFunctionCall(ctx.void_id, ctx.write_global_func_u32x4, address, value);
return;
}

View File

@@ -33,24 +33,11 @@ Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) {
const Id type{ctx.F32[1]};
const bool depth{desc.is_depth};
const bool ms{desc.is_multisample};
// Mobile GPUs lack Sampled1D SPIR-V capability - emulate 1D as 2D with array layer
const bool emulate_1d = ctx.profile.needs_1d_texture_emulation;
// Debug log for 1D emulation
if (desc.type == TextureType::Color1D || desc.type == TextureType::ColorArray1D) {
LOG_WARNING(Shader_SPIRV, "ImageType(texture): Creating {} texture, emulate_1d={}",
desc.type == TextureType::Color1D ? "Color1D" : "ColorArray1D",
emulate_1d);
}
switch (desc.type) {
case TextureType::Color1D:
return emulate_1d ? ctx.TypeImage(type, spv::Dim::Dim2D, depth, false, false, 1, format)
: ctx.TypeImage(type, spv::Dim::Dim1D, depth, false, false, 1, format);
return ctx.TypeImage(type, spv::Dim::Dim1D, depth, false, false, 1, format);
case TextureType::ColorArray1D:
return emulate_1d ? ctx.TypeImage(type, spv::Dim::Dim2D, depth, true, false, 1, format)
: ctx.TypeImage(type, spv::Dim::Dim1D, depth, true, false, 1, format);
return ctx.TypeImage(type, spv::Dim::Dim1D, depth, true, false, 1, format);
case TextureType::Color2D:
case TextureType::Color2DRect:
return ctx.TypeImage(type, spv::Dim::Dim2D, depth, false, ms, 1, format);
@@ -92,22 +79,11 @@ spv::ImageFormat GetImageFormat(ImageFormat format) {
Id ImageType(EmitContext& ctx, const ImageDescriptor& desc, Id sampled_type) {
const spv::ImageFormat format{GetImageFormat(desc.format)};
const bool emulate_1d = ctx.profile.needs_1d_texture_emulation;
// Debug log for 1D emulation
if (desc.type == TextureType::Color1D || desc.type == TextureType::ColorArray1D) {
LOG_WARNING(Shader_SPIRV, "ImageType: Creating {} image, emulate_1d={}",
desc.type == TextureType::Color1D ? "Color1D" : "ColorArray1D",
emulate_1d);
}
switch (desc.type) {
case TextureType::Color1D:
return emulate_1d ? ctx.TypeImage(sampled_type, spv::Dim::Dim2D, false, false, false, 2, format)
: ctx.TypeImage(sampled_type, spv::Dim::Dim1D, false, false, false, 2, format);
return ctx.TypeImage(sampled_type, spv::Dim::Dim1D, false, false, false, 2, format);
case TextureType::ColorArray1D:
return emulate_1d ? ctx.TypeImage(sampled_type, spv::Dim::Dim2D, false, true, false, 2, format)
: ctx.TypeImage(sampled_type, spv::Dim::Dim1D, false, true, false, 2, format);
return ctx.TypeImage(sampled_type, spv::Dim::Dim1D, false, true, false, 2, format);
case TextureType::Color2D:
return ctx.TypeImage(sampled_type, spv::Dim::Dim2D, false, false, false, 2, format);
case TextureType::ColorArray2D:
@@ -484,14 +460,9 @@ void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_vie
EmitContext::EmitContext(const Profile& profile_, const RuntimeInfo& runtime_info_,
IR::Program& program, Bindings& bindings)
: Sirit::Module(profile_.supported_spirv), profile{profile_}, runtime_info{runtime_info_},
stage{program.stage},
// Enable int64 emulation if host lacks int64 but we either use int64 ops
// or we need 64-bit addressing for global memory operations.
emulate_int64{!profile.support_int64 &&
(program.info.uses_int64 || program.info.uses_global_memory)},
texture_rescaling_index{bindings.texture_scaling_index},
image_rescaling_index{bindings.image_scaling_index} {
: Sirit::Module(profile_.supported_spirv), profile{profile_}, runtime_info{runtime_info_},
stage{program.stage}, texture_rescaling_index{bindings.texture_scaling_index},
image_rescaling_index{bindings.image_scaling_index} {
const bool is_unified{profile.unified_descriptor_binding};
u32& uniform_binding{is_unified ? bindings.unified : bindings.uniform_buffer};
u32& storage_binding{is_unified ? bindings.unified : bindings.storage_buffer};
@@ -961,163 +932,11 @@ void EmitContext::DefineWriteStorageCasLoopFunction(const Info& info) {
}
void EmitContext::DefineGlobalMemoryFunctions(const Info& info) {
if (!info.uses_global_memory) {
if (!info.uses_global_memory || !profile.support_int64) {
return;
}
using DefPtr = Id StorageDefinitions::*;
const Id zero{u32_zero_value};
if (SupportsNativeInt64()) {
const auto define_body{[&](DefPtr ssbo_member, Id addr, Id element_pointer, u32 shift,
auto&& callback) {
AddLabel();
const size_t num_buffers{info.storage_buffers_descriptors.size()};
for (size_t index = 0; index < num_buffers; ++index) {
if (!info.nvn_buffer_used[index]) {
continue;
}
const auto& ssbo{info.storage_buffers_descriptors[index]};
const Id ssbo_addr_cbuf_offset{Const(ssbo.cbuf_offset / 8)};
const Id ssbo_size_cbuf_offset{Const(ssbo.cbuf_offset / 4 + 2)};
const Id ssbo_addr_pointer{OpAccessChain(
uniform_types.U32x2, cbufs[ssbo.cbuf_index].U32x2, zero,
ssbo_addr_cbuf_offset)};
const Id ssbo_size_pointer{OpAccessChain(
uniform_types.U32, cbufs[ssbo.cbuf_index].U32, zero, ssbo_size_cbuf_offset)};
const u64 ssbo_align_mask{~(profile.min_ssbo_alignment - 1U)};
const Id unaligned_addr{OpBitcast(U64, OpLoad(U32[2], ssbo_addr_pointer))};
const Id ssbo_addr{OpBitwiseAnd(U64, unaligned_addr, Constant(U64, ssbo_align_mask))};
const Id ssbo_size{OpUConvert(U64, OpLoad(U32[1], ssbo_size_pointer))};
const Id ssbo_end{OpIAdd(U64, ssbo_addr, ssbo_size)};
const Id cond{OpLogicalAnd(U1, OpUGreaterThanEqual(U1, addr, ssbo_addr),
OpULessThan(U1, addr, ssbo_end))};
const Id then_label{OpLabel()};
const Id else_label{OpLabel()};
OpSelectionMerge(else_label, spv::SelectionControlMask::MaskNone);
OpBranchConditional(cond, then_label, else_label);
AddLabel(then_label);
const Id ssbo_id{ssbos[index].*ssbo_member};
const Id ssbo_offset{OpUConvert(U32[1], OpISub(U64, addr, ssbo_addr))};
const Id ssbo_index{OpShiftRightLogical(U32[1], ssbo_offset, Const(shift))};
const Id ssbo_pointer{OpAccessChain(element_pointer, ssbo_id, zero, ssbo_index)};
callback(ssbo_pointer);
AddLabel(else_label);
}
}};
const auto define_load{[&](DefPtr ssbo_member, Id element_pointer, Id type, u32 shift) {
const Id function_type{TypeFunction(type, U64)};
const Id func_id{OpFunction(type, spv::FunctionControlMask::MaskNone, function_type)};
const Id addr{OpFunctionParameter(U64)};
define_body(ssbo_member, addr, element_pointer, shift,
[&](Id ssbo_pointer) { OpReturnValue(OpLoad(type, ssbo_pointer)); });
OpReturnValue(ConstantNull(type));
OpFunctionEnd();
return func_id;
}};
const auto define_write{[&](DefPtr ssbo_member, Id element_pointer, Id type, u32 shift) {
const Id function_type{TypeFunction(void_id, U64, type)};
const Id func_id{
OpFunction(void_id, spv::FunctionControlMask::MaskNone, function_type)};
const Id addr{OpFunctionParameter(U64)};
const Id data{OpFunctionParameter(type)};
define_body(ssbo_member, addr, element_pointer, shift, [&](Id ssbo_pointer) {
OpStore(ssbo_pointer, data);
OpReturn();
});
OpReturn();
OpFunctionEnd();
return func_id;
}};
const auto define{
[&](DefPtr ssbo_member, const StorageTypeDefinition& type_def, Id type, size_t size) {
const Id element_type{type_def.element};
const u32 shift{static_cast<u32>(std::countr_zero(size))};
const Id load_func{define_load(ssbo_member, element_type, type, shift)};
const Id write_func{define_write(ssbo_member, element_type, type, shift)};
return std::make_pair(load_func, write_func);
}};
std::tie(load_global_func_u32, write_global_func_u32) =
define(&StorageDefinitions::U32, storage_types.U32, U32[1], sizeof(u32));
std::tie(load_global_func_u32x2, write_global_func_u32x2) =
define(&StorageDefinitions::U32x2, storage_types.U32x2, U32[2], sizeof(u32[2]));
std::tie(load_global_func_u32x4, write_global_func_u32x4) =
define(&StorageDefinitions::U32x4, storage_types.U32x4, U32[4], sizeof(u32[4]));
return;
}
if (!UsesInt64Emulation()) {
return;
}
const auto make_pair = [&](Id lo, Id hi) {
return OpCompositeConstruct(U32[2], lo, hi);
};
const auto split_pair = [&](Id value) {
return std::array<Id, 2>{OpCompositeExtract(U32[1], value, 0U),
OpCompositeExtract(U32[1], value, 1U)};
};
const auto bool_to_u32 = [&](Id predicate) {
return OpSelect(U32[1], predicate, Const(1u), zero);
};
const auto and_pair = [&](Id value, Id mask) {
const auto value_parts{split_pair(value)};
const auto mask_parts{split_pair(mask)};
return make_pair(OpBitwiseAnd(U32[1], value_parts[0], mask_parts[0]),
OpBitwiseAnd(U32[1], value_parts[1], mask_parts[1]));
};
const auto add_pair = [&](Id lhs, Id rhs) {
const auto lhs_parts{split_pair(lhs)};
const auto rhs_parts{split_pair(rhs)};
const Id sum_lo{OpIAdd(U32[1], lhs_parts[0], rhs_parts[0])};
const Id carry{OpULessThan(U1, sum_lo, lhs_parts[0])};
Id sum_hi{OpIAdd(U32[1], lhs_parts[1], rhs_parts[1])};
sum_hi = OpIAdd(U32[1], sum_hi, bool_to_u32(carry));
return make_pair(sum_lo, sum_hi);
};
const auto sub_pair = [&](Id lhs, Id rhs) {
const auto lhs_parts{split_pair(lhs)};
const auto rhs_parts{split_pair(rhs)};
const Id borrow{OpULessThan(U1, lhs_parts[0], rhs_parts[0])};
const Id diff_lo{OpISub(U32[1], lhs_parts[0], rhs_parts[0])};
Id diff_hi{OpISub(U32[1], lhs_parts[1], rhs_parts[1])};
diff_hi = OpISub(U32[1], diff_hi, bool_to_u32(borrow));
return make_pair(diff_lo, diff_hi);
};
const auto shift_right_pair = [&](Id value, u32 shift) {
if (shift == 0) {
return value;
}
const auto parts{split_pair(value)};
const Id shift_id{Const(shift)};
const Id high_shifted{OpShiftRightLogical(U32[1], parts[1], shift_id)};
Id low_shifted{OpShiftRightLogical(U32[1], parts[0], shift_id)};
const Id carry_bits{OpShiftLeftLogical(U32[1], parts[1], Const(32u - shift))};
low_shifted = OpBitwiseOr(U32[1], low_shifted, carry_bits);
return make_pair(low_shifted, high_shifted);
};
const auto greater_equal_pair = [&](Id lhs, Id rhs) {
const auto lhs_parts{split_pair(lhs)};
const auto rhs_parts{split_pair(rhs)};
const Id hi_gt{OpUGreaterThan(U1, lhs_parts[1], rhs_parts[1])};
const Id hi_eq{OpIEqual(U1, lhs_parts[1], rhs_parts[1])};
const Id lo_ge{OpUGreaterThanEqual(U1, lhs_parts[0], rhs_parts[0])};
return OpLogicalOr(U1, hi_gt, OpLogicalAnd(U1, hi_eq, lo_ge));
};
const auto less_than_pair = [&](Id lhs, Id rhs) {
const auto lhs_parts{split_pair(lhs)};
const auto rhs_parts{split_pair(rhs)};
const Id hi_lt{OpULessThan(U1, lhs_parts[1], rhs_parts[1])};
const Id hi_eq{OpIEqual(U1, lhs_parts[1], rhs_parts[1])};
const Id lo_lt{OpULessThan(U1, lhs_parts[0], rhs_parts[0])};
return OpLogicalOr(U1, hi_lt, OpLogicalAnd(U1, hi_eq, lo_lt));
};
const u64 ssbo_align_mask_value{~(profile.min_ssbo_alignment - 1U)};
const Id ssbo_align_mask{
Const(static_cast<u32>(ssbo_align_mask_value & 0xFFFFFFFFu),
static_cast<u32>(ssbo_align_mask_value >> 32))};
const auto define_body{[&](DefPtr ssbo_member, Id addr, Id element_pointer, u32 shift,
auto&& callback) {
AddLabel();
@@ -1134,44 +953,40 @@ void EmitContext::DefineGlobalMemoryFunctions(const Info& info) {
const Id ssbo_size_pointer{OpAccessChain(uniform_types.U32, cbufs[ssbo.cbuf_index].U32,
zero, ssbo_size_cbuf_offset)};
const Id unaligned_addr_pair{OpLoad(U32[2], ssbo_addr_pointer)};
const Id ssbo_addr_pair{and_pair(unaligned_addr_pair, ssbo_align_mask)};
const Id ssbo_size_value{OpLoad(U32[1], ssbo_size_pointer)};
const Id ssbo_size_pair{make_pair(ssbo_size_value, zero)};
const Id ssbo_end_pair{add_pair(ssbo_addr_pair, ssbo_size_pair)};
const Id cond{OpLogicalAnd(U1, greater_equal_pair(addr, ssbo_addr_pair),
less_than_pair(addr, ssbo_end_pair))};
const u64 ssbo_align_mask{~(profile.min_ssbo_alignment - 1U)};
const Id unaligned_addr{OpBitcast(U64, OpLoad(U32[2], ssbo_addr_pointer))};
const Id ssbo_addr{OpBitwiseAnd(U64, unaligned_addr, Constant(U64, ssbo_align_mask))};
const Id ssbo_size{OpUConvert(U64, OpLoad(U32[1], ssbo_size_pointer))};
const Id ssbo_end{OpIAdd(U64, ssbo_addr, ssbo_size)};
const Id cond{OpLogicalAnd(U1, OpUGreaterThanEqual(U1, addr, ssbo_addr),
OpULessThan(U1, addr, ssbo_end))};
const Id then_label{OpLabel()};
const Id else_label{OpLabel()};
OpSelectionMerge(else_label, spv::SelectionControlMask::MaskNone);
OpBranchConditional(cond, then_label, else_label);
AddLabel(then_label);
const Id ssbo_id{ssbos[index].*ssbo_member};
const Id ssbo_offset_pair{sub_pair(addr, ssbo_addr_pair)};
const Id ssbo_index_pair{shift_right_pair(ssbo_offset_pair, shift)};
const Id ssbo_index{OpCompositeExtract(U32[1], ssbo_index_pair, 0U)};
const Id ssbo_offset{OpUConvert(U32[1], OpISub(U64, addr, ssbo_addr))};
const Id ssbo_index{OpShiftRightLogical(U32[1], ssbo_offset, Const(shift))};
const Id ssbo_pointer{OpAccessChain(element_pointer, ssbo_id, zero, ssbo_index)};
callback(ssbo_pointer);
AddLabel(else_label);
}
}};
const auto define_load{[&](DefPtr ssbo_member, Id element_pointer, Id type, u32 shift) {
const Id function_type{TypeFunction(type, U32[2])};
const Id function_type{TypeFunction(type, U64)};
const Id func_id{OpFunction(type, spv::FunctionControlMask::MaskNone, function_type)};
const Id addr{OpFunctionParameter(U32[2])};
const Id addr{OpFunctionParameter(U64)};
define_body(ssbo_member, addr, element_pointer, shift,
[&](Id ssbo_pointer) { OpReturnValue(OpLoad(type, ssbo_pointer)); });
OpReturnValue(ConstantNull(type));
OpFunctionEnd();
return func_id;
}};
const auto define_write{[&](DefPtr ssbo_member, Id element_pointer, Id type, u32 shift) {
const Id function_type{TypeFunction(void_id, U32[2], type)};
const Id func_id{
OpFunction(void_id, spv::FunctionControlMask::MaskNone, function_type)};
const Id addr{OpFunctionParameter(U32[2])};
const Id function_type{TypeFunction(void_id, U64, type)};
const Id func_id{OpFunction(void_id, spv::FunctionControlMask::MaskNone, function_type)};
const Id addr{OpFunctionParameter(U64)};
const Id data{OpFunctionParameter(type)};
define_body(ssbo_member, addr, element_pointer, shift, [&](Id ssbo_pointer) {
OpStore(ssbo_pointer, data);
@@ -1181,7 +996,6 @@ void EmitContext::DefineGlobalMemoryFunctions(const Info& info) {
OpFunctionEnd();
return func_id;
}};
const auto define{
[&](DefPtr ssbo_member, const StorageTypeDefinition& type_def, Id type, size_t size) {
const Id element_type{type_def.element};
@@ -1190,7 +1004,6 @@ void EmitContext::DefineGlobalMemoryFunctions(const Info& info) {
const Id write_func{define_write(ssbo_member, element_type, type, shift)};
return std::make_pair(load_func, write_func);
}};
std::tie(load_global_func_u32, write_global_func_u32) =
define(&StorageDefinitions::U32, storage_types.U32, U32[1], sizeof(u32));
std::tie(load_global_func_u32x2, write_global_func_u32x2) =
@@ -1629,15 +1442,6 @@ void EmitContext::DefineInputs(const IR::Program& program) {
subgroup_mask_le = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLeMaskKHR);
subgroup_mask_gt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGtMaskKHR);
subgroup_mask_ge = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGeMaskKHR);
// Vulkan spec: Fragment shader Input variables with integer/float type must have Flat decoration
if (stage == Stage::Fragment) {
Decorate(subgroup_mask_eq, spv::Decoration::Flat);
Decorate(subgroup_mask_lt, spv::Decoration::Flat);
Decorate(subgroup_mask_le, spv::Decoration::Flat);
Decorate(subgroup_mask_gt, spv::Decoration::Flat);
Decorate(subgroup_mask_ge, spv::Decoration::Flat);
}
}
if (info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles ||
(profile.warp_size_potentially_larger_than_guest &&

View File

@@ -210,15 +210,6 @@ public:
const Profile& profile;
const RuntimeInfo& runtime_info;
Stage stage{};
const bool emulate_int64{};
bool SupportsNativeInt64() const {
return profile.support_int64;
}
bool UsesInt64Emulation() const {
return emulate_int64;
}
Id void_id{};
Id U1{};

View File

@@ -95,8 +95,6 @@ struct Profile {
bool ignore_nan_fp_comparisons{};
/// Some drivers have broken support for OpVectorExtractDynamic on subgroup mask inputs
bool has_broken_spirv_subgroup_mask_vector_extract_dynamic{};
/// Mobile GPUs lack Sampled1D capability - need to emulate 1D textures as 2D with height=1
bool needs_1d_texture_emulation{};
u32 gl_max_compute_smem_size{};

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -133,7 +130,7 @@ enum class TexturePixelFormat {
ASTC_2D_8X6_SRGB,
ASTC_2D_6X5_UNORM,
ASTC_2D_6X5_SRGB,
E5B9G9R9_FLOAT,
D32_FLOAT,
D16_UNORM,
X8_D24_UNORM,

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -116,6 +113,7 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> FORMAT_TAB
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR}, // ASTC_2D_8X6_SRGB
{GL_COMPRESSED_RGBA_ASTC_6x5_KHR}, // ASTC_2D_6X5_UNORM
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR}, // ASTC_2D_6X5_SRGB
{GL_RGB9_E5, GL_RGB, GL_UNSIGNED_INT_5_9_9_9_REV}, // E5B9G9R9_FLOAT
{GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT}, // D32_FLOAT
{GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, // D16_UNORM
{GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT_24_8}, // X8_D24_UNORM

View File

@@ -58,42 +58,12 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
const auto topology_ = maxwell3d.draw_manager->GetDrawState().topology;
raw1 = 0;
raw1_eds3_extended = 0;
pad_align_u64 = 0;
// EDS1
extended_dynamic_state.Assign(features.has_extended_dynamic_state);
// EDS2
extended_dynamic_state_2.Assign(features.has_extended_dynamic_state_2);
extended_dynamic_state_2_logic_op.Assign(features.has_extended_dynamic_state_2_logic_op);
extended_dynamic_state_2_patch_control_points.Assign(features.has_extended_dynamic_state_2_patch_control_points);
// EDS3 - Blending/Enables
extended_dynamic_state_3_blend.Assign(features.has_extended_dynamic_state_3_blend);
extended_dynamic_state_3_enables.Assign(features.has_extended_dynamic_state_3_enables);
// EDS3 - Granular features
extended_dynamic_state_3_depth_clamp.Assign(features.has_extended_dynamic_state_3_depth_clamp);
extended_dynamic_state_3_logic_op_enable.Assign(features.has_extended_dynamic_state_3_logic_op_enable);
extended_dynamic_state_3_tessellation_domain_origin.Assign(features.has_extended_dynamic_state_3_tessellation_domain_origin);
extended_dynamic_state_3_polygon_mode.Assign(features.has_extended_dynamic_state_3_polygon_mode);
extended_dynamic_state_3_rasterization_samples.Assign(features.has_extended_dynamic_state_3_rasterization_samples);
extended_dynamic_state_3_sample_mask.Assign(features.has_extended_dynamic_state_3_sample_mask);
extended_dynamic_state_3_alpha_to_coverage_enable.Assign(features.has_extended_dynamic_state_3_alpha_to_coverage_enable);
extended_dynamic_state_3_alpha_to_one_enable.Assign(features.has_extended_dynamic_state_3_alpha_to_one_enable);
extended_dynamic_state_3_depth_clip_enable.Assign(features.has_extended_dynamic_state_3_depth_clip_enable);
extended_dynamic_state_3_depth_clip_negative_one_to_one.Assign(features.has_extended_dynamic_state_3_depth_clip_negative_one_to_one);
extended_dynamic_state_3_line_rasterization_mode.Assign(features.has_extended_dynamic_state_3_line_rasterization_mode);
extended_dynamic_state_3_line_stipple_enable.Assign(features.has_extended_dynamic_state_3_line_stipple_enable);
extended_dynamic_state_3_provoking_vertex_mode.Assign(features.has_extended_dynamic_state_3_provoking_vertex_mode);
extended_dynamic_state_3_conservative_rasterization_mode.Assign(features.has_extended_dynamic_state_3_conservative_rasterization_mode);
extended_dynamic_state_3_sample_locations_enable.Assign(features.has_extended_dynamic_state_3_sample_locations_enable);
extended_dynamic_state_3_rasterization_stream.Assign(features.has_extended_dynamic_state_3_rasterization_stream);
// Vertex Input
dynamic_vertex_input.Assign(features.has_dynamic_vertex_input);
extended_dynamic_state.Assign(features.has_extended_dynamic_state ? 1 : 0);
extended_dynamic_state_2.Assign(features.has_extended_dynamic_state_2 ? 1 : 0);
extended_dynamic_state_2_extra.Assign(features.has_extended_dynamic_state_2_extra ? 1 : 0);
extended_dynamic_state_3_blend.Assign(features.has_extended_dynamic_state_3_blend ? 1 : 0);
extended_dynamic_state_3_enables.Assign(features.has_extended_dynamic_state_3_enables ? 1 : 0);
dynamic_vertex_input.Assign(features.has_dynamic_vertex_input ? 1 : 0);
xfb_enabled.Assign(regs.transform_feedback_enabled != 0);
ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0);
polygon_mode.Assign(PackPolygonMode(VideoCore::EffectivePolygonMode(regs)));
@@ -188,7 +158,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
return static_cast<u16>(array.stride.Value());
});
}
if (!extended_dynamic_state_2_logic_op) {
if (!extended_dynamic_state_2_extra) {
dynamic_state.Refresh2(regs, topology_, extended_dynamic_state_2);
}
if (!extended_dynamic_state_3_blend) {

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -21,45 +18,13 @@ namespace Vulkan {
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
struct DynamicFeatures {
union {
u32 raw0;
// VK_EXT_extended_dynamic_state (EDS1) - Bit 0
BitField<0, 1, u32> has_extended_dynamic_state;
// VK_EXT_extended_dynamic_state2 (EDS2) - Bits 1-3
BitField<1, 1, u32> has_extended_dynamic_state_2; // Core EDS2
BitField<2, 1, u32> has_extended_dynamic_state_2_logic_op; // LogicOp
BitField<3, 1, u32> has_extended_dynamic_state_2_patch_control_points; // Tessellation
// VK_EXT_extended_dynamic_state3 (EDS3) - Bits 4-5
BitField<4, 1, u32> has_extended_dynamic_state_3_blend; // Blending composite
BitField<5, 1, u32> has_extended_dynamic_state_3_enables; // Enables composite
// VK_EXT_vertex_input_dynamic_state - Bit 6
BitField<6, 1, u32> has_dynamic_vertex_input;
// EDS3 Granular Features - Bits 7-15
BitField<7, 1, u32> has_extended_dynamic_state_3_depth_clamp;
BitField<8, 1, u32> has_extended_dynamic_state_3_logic_op_enable;
BitField<9, 1, u32> has_extended_dynamic_state_3_tessellation_domain_origin;
BitField<10, 1, u32> has_extended_dynamic_state_3_polygon_mode;
BitField<11, 1, u32> has_extended_dynamic_state_3_rasterization_samples;
BitField<12, 1, u32> has_extended_dynamic_state_3_sample_mask;
BitField<13, 1, u32> has_extended_dynamic_state_3_alpha_to_coverage_enable;
BitField<14, 1, u32> has_extended_dynamic_state_3_alpha_to_one_enable;
BitField<15, 1, u32> has_extended_dynamic_state_3_depth_clip_enable;
// EDS3 Additional Features - Bits 16-22
BitField<16, 1, u32> has_extended_dynamic_state_3_depth_clip_negative_one_to_one;
BitField<17, 1, u32> has_extended_dynamic_state_3_line_rasterization_mode;
BitField<18, 1, u32> has_extended_dynamic_state_3_line_stipple_enable;
BitField<19, 1, u32> has_extended_dynamic_state_3_provoking_vertex_mode;
BitField<20, 1, u32> has_extended_dynamic_state_3_conservative_rasterization_mode;
BitField<21, 1, u32> has_extended_dynamic_state_3_sample_locations_enable;
BitField<22, 1, u32> has_extended_dynamic_state_3_rasterization_stream;
};
bool has_extended_dynamic_state;
bool has_extended_dynamic_state_2;
bool has_extended_dynamic_state_2_extra;
bool has_extended_dynamic_state_3_blend;
bool has_extended_dynamic_state_3_enables;
bool has_dynamic_vertex_input;
};
static_assert(std::has_unique_object_representations_v<DynamicFeatures>);
struct FixedPipelineState {
static u32 PackComparisonOp(Maxwell::ComparisonOp op) noexcept;
@@ -219,56 +184,23 @@ struct FixedPipelineState {
union {
u32 raw1;
// EDS1 - Bit 0
BitField<0, 1, u32> extended_dynamic_state;
// EDS2 - Bits 1-3
BitField<1, 1, u32> extended_dynamic_state_2;
BitField<2, 1, u32> extended_dynamic_state_2_logic_op;
BitField<3, 1, u32> extended_dynamic_state_2_patch_control_points;
// EDS3 Blending/Enables - Bits 4-5
BitField<4, 1, u32> extended_dynamic_state_3_blend;
BitField<5, 1, u32> extended_dynamic_state_3_enables;
// Vertex Input - Bit 6
BitField<6, 1, u32> dynamic_vertex_input;
// Other state - Bits 7-19
BitField<7, 1, u32> xfb_enabled;
BitField<8, 1, u32> ndc_minus_one_to_one;
BitField<9, 2, u32> polygon_mode;
BitField<11, 2, u32> tessellation_primitive;
BitField<13, 2, u32> tessellation_spacing;
BitField<15, 1, u32> tessellation_clockwise;
BitField<16, 5, u32> patch_control_points_minus_one;
BitField<2, 1, u32> extended_dynamic_state_2_extra;
BitField<3, 1, u32> extended_dynamic_state_3_blend;
BitField<4, 1, u32> extended_dynamic_state_3_enables;
BitField<5, 1, u32> dynamic_vertex_input;
BitField<6, 1, u32> xfb_enabled;
BitField<7, 1, u32> ndc_minus_one_to_one;
BitField<8, 2, u32> polygon_mode;
BitField<10, 2, u32> tessellation_primitive;
BitField<12, 2, u32> tessellation_spacing;
BitField<14, 1, u32> tessellation_clockwise;
BitField<15, 5, u32> patch_control_points_minus_one;
// Topology and MSAA - Bits 24-31
BitField<24, 4, Maxwell::PrimitiveTopology> topology;
BitField<28, 4, Tegra::Texture::MsaaMode> msaa_mode;
};
union {
u32 raw1_eds3_extended;
// EDS3 Additional Features - Bits 0-15
BitField<0, 1, u32> extended_dynamic_state_3_depth_clamp;
BitField<1, 1, u32> extended_dynamic_state_3_logic_op_enable;
BitField<2, 1, u32> extended_dynamic_state_3_tessellation_domain_origin;
BitField<3, 1, u32> extended_dynamic_state_3_polygon_mode;
BitField<4, 1, u32> extended_dynamic_state_3_rasterization_samples;
BitField<5, 1, u32> extended_dynamic_state_3_sample_mask;
BitField<6, 1, u32> extended_dynamic_state_3_alpha_to_coverage_enable;
BitField<7, 1, u32> extended_dynamic_state_3_alpha_to_one_enable;
BitField<8, 1, u32> extended_dynamic_state_3_depth_clip_enable;
BitField<9, 1, u32> extended_dynamic_state_3_depth_clip_negative_one_to_one;
BitField<10, 1, u32> extended_dynamic_state_3_line_rasterization_mode;
BitField<11, 1, u32> extended_dynamic_state_3_line_stipple_enable;
BitField<12, 1, u32> extended_dynamic_state_3_provoking_vertex_mode;
BitField<13, 1, u32> extended_dynamic_state_3_conservative_rasterization_mode;
BitField<14, 1, u32> extended_dynamic_state_3_sample_locations_enable;
BitField<15, 1, u32> extended_dynamic_state_3_rasterization_stream;
};
union {
u32 raw2;
BitField<1, 3, u32> alpha_test_func;
@@ -283,15 +215,12 @@ struct FixedPipelineState {
BitField<16, 1, u32> alpha_to_one_enabled;
BitField<17, 3, Tegra::Engines::Maxwell3D::EngineHint> app_stage;
};
std::array<u8, Maxwell::NumRenderTargets> color_formats;
u32 alpha_test_ref;
u32 point_size;
std::array<u8, Maxwell::NumRenderTargets> color_formats;
std::array<u16, Maxwell::NumViewports> viewport_swizzles;
u32 pad_align_u64;
union {
u64 attribute_types; // Used with VK_EXT_vertex_input_dynamic_state
u64 enabled_divisors;

View File

@@ -214,6 +214,7 @@ struct FormatTuple {
{VK_FORMAT_ASTC_8x6_SRGB_BLOCK}, // ASTC_2D_8X6_SRGB
{VK_FORMAT_ASTC_6x5_UNORM_BLOCK}, // ASTC_2D_6X5_UNORM
{VK_FORMAT_ASTC_6x5_SRGB_BLOCK}, // ASTC_2D_6X5_SRGB
{VK_FORMAT_E5B9G9R9_UFLOAT_PACK32}, // E5B9G9R9_FLOAT (SAMPLED_IMAGE only, no COLOR_ATTACHMENT)
// Depth formats
{VK_FORMAT_D32_SFLOAT, Attachable}, // D32_FLOAT

View File

@@ -177,6 +177,8 @@ try
RendererVulkan::~RendererVulkan() {
scheduler.RegisterOnSubmit([] {});
// Acquire submit_mutex before WaitIdle to prevent simultaneous queue access
std::scoped_lock lock{scheduler.submit_mutex};
void(device.GetLogical().WaitIdle());
}

View File

@@ -30,7 +30,8 @@ BlitScreen::~BlitScreen() = default;
void BlitScreen::WaitIdle() {
present_manager.WaitPresent();
scheduler.Finish();
device.GetLogical().WaitIdle();
// Note: scheduler.Finish() already waits for GPU and synchronizes submit_mutex
// Calling device.WaitIdle() here causes threading errors (simultaneous queue access)
}
void BlitScreen::SetWindowAdaptPass() {

View File

@@ -837,38 +837,13 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
.pAttachments = cb_attachments.data(),
.blendConstants = {}
};
// Base Vulkan Dynamic States - Always active (independent of EDS)
// Granular fallback: Each state added only if device supports it (protection against broken drivers)
static_vector<VkDynamicState, 34> dynamic_states;
if (device.SupportsDynamicViewport()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_VIEWPORT);
}
if (device.SupportsDynamicScissor()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_SCISSOR);
}
if (device.SupportsDynamicLineWidth()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_LINE_WIDTH);
}
if (device.SupportsDynamicDepthBias()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_BIAS);
}
if (device.SupportsDynamicBlendConstants()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_BLEND_CONSTANTS);
}
if (device.SupportsDynamicDepthBounds()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_BOUNDS);
}
if (device.SupportsDynamicStencilCompareMask()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK);
}
if (device.SupportsDynamicStencilWriteMask()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_WRITE_MASK);
}
if (device.SupportsDynamicStencilReference()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_REFERENCE);
}
// EDS1 - Extended Dynamic State (12 states)
static_vector<VkDynamicState, 34> dynamic_states{
VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR,
VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_BLEND_CONSTANTS,
VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK,
VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE,
VK_DYNAMIC_STATE_LINE_WIDTH,
};
if (key.state.extended_dynamic_state) {
static constexpr std::array extended{
VK_DYNAMIC_STATE_CULL_MODE_EXT,
@@ -881,59 +856,49 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT,
VK_DYNAMIC_STATE_STENCIL_OP_EXT,
};
if (key.state.dynamic_vertex_input) {
dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT);
}
dynamic_states.insert(dynamic_states.end(), extended.begin(), extended.end());
}
if (key.state.extended_dynamic_state_2) {
static constexpr std::array extended2{
VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE_EXT,
VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE_EXT,
VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE_EXT,
};
dynamic_states.insert(dynamic_states.end(), extended2.begin(), extended2.end());
}
if (key.state.extended_dynamic_state_2_extra) {
dynamic_states.push_back(VK_DYNAMIC_STATE_LOGIC_OP_EXT);
}
if (key.state.extended_dynamic_state_3_blend) {
static constexpr std::array extended3{
VK_DYNAMIC_STATE_COLOR_BLEND_ENABLE_EXT,
VK_DYNAMIC_STATE_COLOR_BLEND_EQUATION_EXT,
VK_DYNAMIC_STATE_COLOR_WRITE_MASK_EXT,
// Vertex Input Dynamic State (independent toggle)
if (key.state.dynamic_vertex_input) {
dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT);
}
// VK_DYNAMIC_STATE_COLOR_BLEND_ADVANCED_EXT,
};
dynamic_states.insert(dynamic_states.end(), extended3.begin(), extended3.end());
}
if (key.state.extended_dynamic_state_3_enables) {
static constexpr std::array extended3{
VK_DYNAMIC_STATE_DEPTH_CLAMP_ENABLE_EXT,
VK_DYNAMIC_STATE_LOGIC_OP_ENABLE_EXT,
// EDS2 - Extended Dynamic State 2 Core (3 states)
if (key.state.extended_dynamic_state_2) {
static constexpr std::array extended2{
VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE_EXT,
VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE_EXT,
VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE_EXT,
};
dynamic_states.insert(dynamic_states.end(), extended2.begin(), extended2.end());
}
// additional state3 extensions
VK_DYNAMIC_STATE_LINE_RASTERIZATION_MODE_EXT,
// EDS2 - Logic Op (granular feature)
if (key.state.extended_dynamic_state_2_logic_op) {
dynamic_states.push_back(VK_DYNAMIC_STATE_LOGIC_OP_EXT);
}
VK_DYNAMIC_STATE_CONSERVATIVE_RASTERIZATION_MODE_EXT,
// EDS3 - Blending (composite: ColorBlendEnable + Equation + WriteMask)
if (key.state.extended_dynamic_state_3_blend) {
static constexpr std::array extended3{
VK_DYNAMIC_STATE_COLOR_BLEND_ENABLE_EXT,
VK_DYNAMIC_STATE_COLOR_BLEND_EQUATION_EXT,
VK_DYNAMIC_STATE_COLOR_WRITE_MASK_EXT,
// VK_DYNAMIC_STATE_COLOR_BLEND_ADVANCED_EXT,
};
dynamic_states.insert(dynamic_states.end(), extended3.begin(), extended3.end());
}
// EDS3 - Enables (granular: DepthClamp + LogicOpEnable + ...)
if (key.state.extended_dynamic_state_3_enables) {
static constexpr std::array extended3{
VK_DYNAMIC_STATE_DEPTH_CLAMP_ENABLE_EXT,
VK_DYNAMIC_STATE_LOGIC_OP_ENABLE_EXT,
// additional state3 extensions
VK_DYNAMIC_STATE_LINE_RASTERIZATION_MODE_EXT,
VK_DYNAMIC_STATE_CONSERVATIVE_RASTERIZATION_MODE_EXT,
VK_DYNAMIC_STATE_LINE_STIPPLE_ENABLE_EXT,
VK_DYNAMIC_STATE_ALPHA_TO_COVERAGE_ENABLE_EXT,
VK_DYNAMIC_STATE_ALPHA_TO_ONE_ENABLE_EXT,
VK_DYNAMIC_STATE_DEPTH_CLIP_ENABLE_EXT,
VK_DYNAMIC_STATE_PROVOKING_VERTEX_MODE_EXT,
};
dynamic_states.insert(dynamic_states.end(), extended3.begin(), extended3.end());
VK_DYNAMIC_STATE_LINE_STIPPLE_ENABLE_EXT,
VK_DYNAMIC_STATE_ALPHA_TO_COVERAGE_ENABLE_EXT,
VK_DYNAMIC_STATE_ALPHA_TO_ONE_ENABLE_EXT,
VK_DYNAMIC_STATE_DEPTH_CLIP_ENABLE_EXT,
VK_DYNAMIC_STATE_PROVOKING_VERTEX_MODE_EXT,
};
dynamic_states.insert(dynamic_states.end(), extended3.begin(), extended3.end());
}
}
const VkPipelineDynamicStateCreateInfo dynamic_state_ci{

View File

@@ -342,15 +342,18 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
.support_fp64_signed_zero_nan_preserve =
float_control.shaderSignedZeroInfNanPreserveFloat64 != VK_FALSE,
// Switch/Maxwell native float behavior - ONLY for Turnip Mesa (Stock Qualcomm broken)
// Stock Adreno drivers have broken float controls disabled in vulkan_device.cpp
.force_fp32_denorm_flush = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY &&
device.IsKhrShaderFloatControlsSupported(), // false on Stock, true on Turnip
.force_fp32_denorm_preserve = false, // FTZ dominates
.force_fp32_rte_rounding = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY &&
device.IsKhrShaderFloatControlsSupported(), // false on Stock, true on Turnip
.force_fp32_signed_zero_inf_nan = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY &&
device.IsKhrShaderFloatControlsSupported(), // false on Stock, true on Turnip
#ifdef ANDROID
// User-forced float behavior overrides (Eden Veil/Extensions)
.force_fp32_denorm_flush = Settings::values.shader_float_ftz.GetValue(),
.force_fp32_denorm_preserve = Settings::values.shader_float_denorm_preserve.GetValue(),
.force_fp32_rte_rounding = Settings::values.shader_float_rte.GetValue(),
.force_fp32_signed_zero_inf_nan = Settings::values.shader_float_signed_zero_inf_nan.GetValue(),
#else
.force_fp32_denorm_flush = false,
.force_fp32_denorm_preserve = false,
.force_fp32_rte_rounding = false,
.force_fp32_signed_zero_inf_nan = false,
#endif
.support_explicit_workgroup_layout = device.IsKhrWorkgroupMemoryExplicitLayoutSupported(),
.support_vote = device.IsSubgroupFeatureSupported(VK_SUBGROUP_FEATURE_VOTE_BIT),
@@ -382,17 +385,10 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
.has_broken_spirv_position_input = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY,
.has_broken_unsigned_image_offsets = false,
.has_broken_signed_operations = false,
.has_broken_fp16_float_controls = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY ||
driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY,
.has_broken_fp16_float_controls = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY,
.ignore_nan_fp_comparisons = false,
.has_broken_spirv_subgroup_mask_vector_extract_dynamic =
driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY,
.needs_1d_texture_emulation =
driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
driver_id == VK_DRIVER_ID_MESA_TURNIP ||
driver_id == VK_DRIVER_ID_ARM_PROPRIETARY ||
driver_id == VK_DRIVER_ID_BROADCOM_PROPRIETARY ||
driver_id == VK_DRIVER_ID_IMAGINATION_PROPRIETARY,
.has_broken_robust =
device.IsNvidia() && device.GetNvidiaArch() <= NvidiaArchitecture::Arch_Pascal,
.min_ssbo_alignment = device.GetStorageBufferAlignment(),
@@ -426,39 +422,14 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
LOG_INFO(Render_Vulkan, "DynamicState value is set to {}", (u32) dynamic_state);
dynamic_features.raw0 = 0;
// EDS1 - All-or-nothing (enabled if driver supports AND setting > 0)
dynamic_features.has_extended_dynamic_state.Assign(device.IsExtExtendedDynamicStateSupported() && dynamic_state > 0);
// EDS2 - Core features (enabled if driver supports AND setting > 1)
dynamic_features.has_extended_dynamic_state_2.Assign(device.IsExtExtendedDynamicState2Supported() && dynamic_state > 1);
dynamic_features.has_extended_dynamic_state_2_logic_op.Assign(device.IsExtExtendedDynamicState2LogicOpSupported() && dynamic_state > 1);
dynamic_features.has_extended_dynamic_state_2_patch_control_points.Assign(device.IsExtExtendedDynamicState2PatchControlPointsSupported() && dynamic_state > 1);
// EDS3 - Granular features (enabled if driver supports AND setting > 2)
dynamic_features.has_extended_dynamic_state_3_blend.Assign(device.IsExtExtendedDynamicState3BlendingSupported() && dynamic_state > 2);
dynamic_features.has_extended_dynamic_state_3_enables.Assign(device.IsExtExtendedDynamicState3EnablesSupported() && dynamic_state > 2);
dynamic_features.has_extended_dynamic_state_3_depth_clamp.Assign(device.IsExtExtendedDynamicState3DepthClampEnableSupported() && dynamic_state > 2);
dynamic_features.has_extended_dynamic_state_3_logic_op_enable.Assign(device.IsExtExtendedDynamicState3LogicOpEnableSupported() && dynamic_state > 2);
dynamic_features.has_extended_dynamic_state_3_tessellation_domain_origin.Assign(device.IsExtExtendedDynamicState3TessellationDomainOriginSupported() && dynamic_state > 2);
dynamic_features.has_extended_dynamic_state_3_polygon_mode.Assign(device.IsExtExtendedDynamicState3PolygonModeSupported() && dynamic_state > 2);
dynamic_features.has_extended_dynamic_state_3_rasterization_samples.Assign(device.IsExtExtendedDynamicState3RasterizationSamplesSupported() && dynamic_state > 2);
dynamic_features.has_extended_dynamic_state_3_sample_mask.Assign(device.IsExtExtendedDynamicState3SampleMaskSupported() && dynamic_state > 2);
dynamic_features.has_extended_dynamic_state_3_alpha_to_coverage_enable.Assign(device.IsExtExtendedDynamicState3AlphaToCoverageEnableSupported() && dynamic_state > 2);
dynamic_features.has_extended_dynamic_state_3_alpha_to_one_enable.Assign(device.IsExtExtendedDynamicState3AlphaToOneEnableSupported() && dynamic_state > 2);
dynamic_features.has_extended_dynamic_state_3_depth_clip_enable.Assign(device.IsExtExtendedDynamicState3DepthClipEnableSupported() && dynamic_state > 2);
dynamic_features.has_extended_dynamic_state_3_depth_clip_negative_one_to_one.Assign(device.IsExtExtendedDynamicState3DepthClipNegativeOneToOneSupported() && dynamic_state > 2);
dynamic_features.has_extended_dynamic_state_3_line_rasterization_mode.Assign(device.IsExtExtendedDynamicState3LineRasterizationModeSupported() && dynamic_state > 2);
dynamic_features.has_extended_dynamic_state_3_line_stipple_enable.Assign(device.IsExtExtendedDynamicState3LineStippleEnableSupported() && dynamic_state > 2);
dynamic_features.has_extended_dynamic_state_3_provoking_vertex_mode.Assign(device.IsExtExtendedDynamicState3ProvokingVertexModeSupported() && dynamic_state > 2);
dynamic_features.has_extended_dynamic_state_3_conservative_rasterization_mode.Assign(device.IsExtExtendedDynamicState3ConservativeRasterizationModeSupported() && dynamic_state > 2);
dynamic_features.has_extended_dynamic_state_3_sample_locations_enable.Assign(device.IsExtExtendedDynamicState3SampleLocationsEnableSupported() && dynamic_state > 2);
dynamic_features.has_extended_dynamic_state_3_rasterization_stream.Assign(device.IsExtExtendedDynamicState3RasterizationStreamSupported() && dynamic_state > 2);
// Vertex input dynamic state (independent toggle)
dynamic_features.has_dynamic_vertex_input.Assign(device.IsExtVertexInputDynamicStateSupported() &&
Settings::values.vertex_input_dynamic_state.GetValue());
dynamic_features = DynamicFeatures{
.has_extended_dynamic_state = device.IsExtExtendedDynamicStateSupported() && dynamic_state > 0,
.has_extended_dynamic_state_2 = device.IsExtExtendedDynamicState2Supported() && dynamic_state > 1,
.has_extended_dynamic_state_2_extra = device.IsExtExtendedDynamicState2ExtrasSupported() && dynamic_state > 1,
.has_extended_dynamic_state_3_blend = device.IsExtExtendedDynamicState3BlendingSupported() && dynamic_state > 2,
.has_extended_dynamic_state_3_enables = device.IsExtExtendedDynamicState3EnablesSupported() && dynamic_state > 2,
.has_dynamic_vertex_input = device.IsExtVertexInputDynamicStateSupported() && dynamic_state > 2,
};
}
PipelineCache::~PipelineCache() {
@@ -559,29 +530,16 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
GraphicsPipelineCacheKey key;
file.read(reinterpret_cast<char*>(&key), sizeof(key));
// Validate dynamic features compatibility - granular per-feature check
if ((key.state.extended_dynamic_state != 0) != dynamic_features.has_extended_dynamic_state ||
(key.state.extended_dynamic_state_2 != 0) != dynamic_features.has_extended_dynamic_state_2 ||
(key.state.extended_dynamic_state_2_logic_op != 0) != dynamic_features.has_extended_dynamic_state_2_logic_op ||
(key.state.extended_dynamic_state_2_patch_control_points != 0) != dynamic_features.has_extended_dynamic_state_2_patch_control_points ||
(key.state.extended_dynamic_state_3_blend != 0) != dynamic_features.has_extended_dynamic_state_3_blend ||
(key.state.extended_dynamic_state_3_enables != 0) != dynamic_features.has_extended_dynamic_state_3_enables ||
(key.state.extended_dynamic_state_3_depth_clamp != 0) != dynamic_features.has_extended_dynamic_state_3_depth_clamp ||
(key.state.extended_dynamic_state_3_logic_op_enable != 0) != dynamic_features.has_extended_dynamic_state_3_logic_op_enable ||
(key.state.extended_dynamic_state_3_tessellation_domain_origin != 0) != dynamic_features.has_extended_dynamic_state_3_tessellation_domain_origin ||
(key.state.extended_dynamic_state_3_polygon_mode != 0) != dynamic_features.has_extended_dynamic_state_3_polygon_mode ||
(key.state.extended_dynamic_state_3_rasterization_samples != 0) != dynamic_features.has_extended_dynamic_state_3_rasterization_samples ||
(key.state.extended_dynamic_state_3_sample_mask != 0) != dynamic_features.has_extended_dynamic_state_3_sample_mask ||
(key.state.extended_dynamic_state_3_alpha_to_coverage_enable != 0) != dynamic_features.has_extended_dynamic_state_3_alpha_to_coverage_enable ||
(key.state.extended_dynamic_state_3_alpha_to_one_enable != 0) != dynamic_features.has_extended_dynamic_state_3_alpha_to_one_enable ||
(key.state.extended_dynamic_state_3_depth_clip_enable != 0) != dynamic_features.has_extended_dynamic_state_3_depth_clip_enable ||
(key.state.extended_dynamic_state_3_depth_clip_negative_one_to_one != 0) != dynamic_features.has_extended_dynamic_state_3_depth_clip_negative_one_to_one ||
(key.state.extended_dynamic_state_3_line_rasterization_mode != 0) != dynamic_features.has_extended_dynamic_state_3_line_rasterization_mode ||
(key.state.extended_dynamic_state_3_line_stipple_enable != 0) != dynamic_features.has_extended_dynamic_state_3_line_stipple_enable ||
(key.state.extended_dynamic_state_3_provoking_vertex_mode != 0) != dynamic_features.has_extended_dynamic_state_3_provoking_vertex_mode ||
(key.state.extended_dynamic_state_3_conservative_rasterization_mode != 0) != dynamic_features.has_extended_dynamic_state_3_conservative_rasterization_mode ||
(key.state.extended_dynamic_state_3_sample_locations_enable != 0) != dynamic_features.has_extended_dynamic_state_3_sample_locations_enable ||
(key.state.extended_dynamic_state_3_rasterization_stream != 0) != dynamic_features.has_extended_dynamic_state_3_rasterization_stream ||
if ((key.state.extended_dynamic_state != 0) !=
dynamic_features.has_extended_dynamic_state ||
(key.state.extended_dynamic_state_2 != 0) !=
dynamic_features.has_extended_dynamic_state_2 ||
(key.state.extended_dynamic_state_2_extra != 0) !=
dynamic_features.has_extended_dynamic_state_2_extra ||
(key.state.extended_dynamic_state_3_blend != 0) !=
dynamic_features.has_extended_dynamic_state_3_blend ||
(key.state.extended_dynamic_state_3_enables != 0) !=
dynamic_features.has_extended_dynamic_state_3_enables ||
(key.state.dynamic_vertex_input != 0) != dynamic_features.has_dynamic_vertex_input) {
return;
}
@@ -838,15 +796,14 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
// Adreno and mobile GPUs have lower shared memory limits (32KB vs Switch's 48KB)
// Skip shader compilation if it exceeds device limits to prevent GPU crashes
// Adreno have lower shared memory limits (32KB)
// Clamp shared memory usage to device maximum to avoid validation errors
const u32 max_shared_memory = device.GetMaxComputeSharedMemorySize();
if (program.shared_memory_size > max_shared_memory) {
LOG_ERROR(Render_Vulkan,
"Compute shader 0x{:016x} requests {}KB shared memory but device max is {}KB - "
"SKIPPING compilation to prevent GPU crash. Visual effect will be missing.",
key.unique_hash, program.shared_memory_size / 1024, max_shared_memory / 1024);
return nullptr;
LOG_WARNING(Render_Vulkan,
"Compute shader 0x{:016x} requests {}KB shared memory but device max is {}KB - clamping",
key.unique_hash, program.shared_memory_size / 1024, max_shared_memory / 1024);
program.shared_memory_size = max_shared_memory;
}
const std::vector<u32> code{EmitSPIRV(profile, program, this->optimize_spirv_output)};

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -116,10 +113,6 @@ public:
void LoadDiskResources(u64 title_id, std::stop_token stop_loading,
const VideoCore::DiskResourceLoadCallback& callback);
[[nodiscard]] const DynamicFeatures& GetDynamicFeatures() const noexcept {
return dynamic_features;
}
private:
[[nodiscard]] GraphicsPipeline* CurrentGraphicsPipelineSlowPath();

View File

@@ -954,10 +954,9 @@ void RasterizerVulkan::UpdateDynamicStates() {
UpdateStencilFaces(regs);
UpdateLineWidth(regs);
const auto& dynamic_features = pipeline_cache.GetDynamicFeatures();
const u8 dynamic_state = Settings::values.dyna_state.GetValue();
// EDS1 - Extended Dynamic State 1
if (dynamic_features.has_extended_dynamic_state) {
if (device.IsExtExtendedDynamicStateSupported() && dynamic_state > 0) {
UpdateCullMode(regs);
UpdateDepthCompareOp(regs);
UpdateFrontFace(regs);
@@ -967,78 +966,40 @@ void RasterizerVulkan::UpdateDynamicStates() {
UpdateDepthTestEnable(regs);
UpdateDepthWriteEnable(regs);
UpdateStencilTestEnable(regs);
}
}
// EDS2 - Extended Dynamic State 2 Core
if (dynamic_features.has_extended_dynamic_state_2) {
if (state_tracker.TouchStateEnable()) {
UpdatePrimitiveRestartEnable(regs);
UpdateRasterizerDiscardEnable(regs);
UpdateDepthBiasEnable(regs);
}
}
// EDS2 - LogicOp (granular feature)
if (dynamic_features.has_extended_dynamic_state_2_logic_op) {
UpdateLogicOp(regs);
}
// EDS3 - Depth Clamp Enable (granular)
if (dynamic_features.has_extended_dynamic_state_3_depth_clamp ||
dynamic_features.has_extended_dynamic_state_3_enables) {
if (state_tracker.TouchStateEnable()) {
UpdateDepthClampEnable(regs);
}
}
// EDS3 - Logic Op Enable (granular)
if (dynamic_features.has_extended_dynamic_state_3_logic_op_enable ||
dynamic_features.has_extended_dynamic_state_3_enables) {
if (state_tracker.TouchStateEnable()) {
using namespace Tegra::Engines;
// AMD workaround for logic op with float vertex attributes
if (device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_OPEN_SOURCE ||
device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_PROPRIETARY) {
struct In {
const Maxwell3D::Regs::VertexAttribute::Type d;
In(Maxwell3D::Regs::VertexAttribute::Type n) : d(n) {}
bool operator()(Maxwell3D::Regs::VertexAttribute n) const {
return n.type == d;
}
};
auto has_float = std::any_of(regs.vertex_attrib_format.begin(),
regs.vertex_attrib_format.end(),
In(Maxwell3D::Regs::VertexAttribute::Type::Float));
if (regs.logic_op.enable) {
regs.logic_op.enable = static_cast<u32>(!has_float);
}
if (device.IsExtExtendedDynamicState2Supported() && dynamic_state > 1) {
UpdatePrimitiveRestartEnable(regs);
UpdateRasterizerDiscardEnable(regs);
UpdateDepthBiasEnable(regs);
}
UpdateLogicOpEnable(regs);
if (device.IsExtExtendedDynamicState3EnablesSupported() && dynamic_state > 2) {
using namespace Tegra::Engines;
if (device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_OPEN_SOURCE || device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_PROPRIETARY) {
struct In {
const Maxwell3D::Regs::VertexAttribute::Type d;
In(Maxwell3D::Regs::VertexAttribute::Type n) : d(n) {}
bool operator()(Maxwell3D::Regs::VertexAttribute n) const {
return n.type == d;
}
};
auto has_float = std::any_of(regs.vertex_attrib_format.begin(), regs.vertex_attrib_format.end(), In(Maxwell3D::Regs::VertexAttribute::Type::Float));
if (regs.logic_op.enable) {
regs.logic_op.enable = static_cast<u32>(!has_float);
}
}
UpdateLogicOpEnable(regs);
UpdateDepthClampEnable(regs);
UpdateLineStippleEnable(regs);
UpdateConservativeRasterizationMode(regs);
}
}
if (device.IsExtExtendedDynamicState2ExtrasSupported() && dynamic_state > 1) {
UpdateLogicOp(regs);
}
if (device.IsExtExtendedDynamicState3BlendingSupported() && dynamic_state > 2) {
UpdateBlending(regs);
}
}
// EDS3 - Line Stipple Enable (granular)
if (dynamic_features.has_extended_dynamic_state_3_line_stipple_enable) {
if (state_tracker.TouchStateEnable()) {
UpdateLineStippleEnable(regs);
}
}
// EDS3 - Conservative Rasterization Mode (granular)
if (dynamic_features.has_extended_dynamic_state_3_conservative_rasterization_mode) {
if (state_tracker.TouchStateEnable()) {
UpdateConservativeRasterizationMode(regs);
}
}
// EDS3 - Blending (composite feature: ColorBlendEnable + ColorBlendEquation + ColorWriteMask)
if (dynamic_features.has_extended_dynamic_state_3_blend) {
UpdateBlending(regs);
}
// Vertex Input Dynamic State
if (dynamic_features.has_dynamic_vertex_input) {
if (device.IsExtVertexInputDynamicStateSupported() && dynamic_state > 2) {
if (auto* gp = pipeline_cache.CurrentGraphicsPipeline(); gp && gp->HasDynamicVertexInput()) {
UpdateVertexInput(regs);
}

View File

@@ -2120,13 +2120,20 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
}
}
const auto format_info = MaxwellToVK::SurfaceFormat(*device, FormatType::Optimal, true, format);
VkFormat view_format = format_info.format;
// TODO: Format reinterpretation toggles (per-game settings)
// Some games incorrectly use integer formats with float samplers:
// - R32_UINT with texture() instead of texelFetch() causes flickering
// - R8_UINT with LINEAR filter causes validation errors
// Cannot auto-detect: need user toggles to force format reinterpretation
// This causes validation errors and undefined behavior (flickering, missing geometry) on certain games
// Reinterpret R32_UINT as R32_SFLOAT for sampled images to match shader expectations
VkFormat view_format = format_info.format;
if (view_format == VK_FORMAT_R32_UINT &&
!info.IsRenderTarget() &&
(ImageUsageFlags(format_info, format) & VK_IMAGE_USAGE_SAMPLED_BIT)) {
// Only reinterpret if NOT used as storage image (storage requires matching types)
const bool is_storage = (ImageUsageFlags(format_info, format) & VK_IMAGE_USAGE_STORAGE_BIT) != 0;
if (!is_storage) {
view_format = VK_FORMAT_R32_SFLOAT;
LOG_DEBUG(Render_Vulkan, "Reinterpreting R32_UINT as R32_SFLOAT for sampled image compatibility");
}
}
if (ImageUsageFlags(format_info, format) != image.UsageFlags()) {
LOG_WARNING(Render_Vulkan,
@@ -2138,7 +2145,6 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
.pNext = nullptr,
.usage = ImageUsageFlags(format_info, format),
};
const VkImageViewCreateInfo create_info{
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.pNext = &image_view_usage,

View File

@@ -277,19 +277,7 @@ std::optional<u64> GenericEnvironment::TryFindSize() {
Tegra::Texture::TICEntry GenericEnvironment::ReadTextureInfo(GPUVAddr tic_addr, u32 tic_limit,
bool via_header_index, u32 raw) {
const auto handle{Tegra::Texture::TexturePair(raw, via_header_index)};
// Some games (especially on updates) use invalid texture handles beyond tic_limit
// Clamp to limit instead of asserting to prevent crashes
if (handle.first > tic_limit) {
LOG_WARNING(HW_GPU, "Texture handle {} exceeds TIC limit {}, clamping to limit",
handle.first, tic_limit);
const u32 clamped_handle = std::min(handle.first, tic_limit);
const GPUVAddr descriptor_addr{tic_addr + clamped_handle * sizeof(Tegra::Texture::TICEntry)};
Tegra::Texture::TICEntry entry;
gpu_memory->ReadBlock(descriptor_addr, &entry, sizeof(entry));
return entry;
}
ASSERT(handle.first <= tic_limit);
const GPUVAddr descriptor_addr{tic_addr + handle.first * sizeof(Tegra::Texture::TICEntry)};
Tegra::Texture::TICEntry entry;
gpu_memory->ReadBlock(descriptor_addr, &entry, sizeof(entry));

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -111,6 +108,7 @@ enum class PixelFormat {
ASTC_2D_8X6_SRGB,
ASTC_2D_6X5_UNORM,
ASTC_2D_6X5_SRGB,
E5B9G9R9_FLOAT,
MaxColorFormat,
@@ -251,6 +249,7 @@ constexpr std::array<u8, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{
8, // ASTC_2D_8X6_SRGB
6, // ASTC_2D_6X5_UNORM
6, // ASTC_2D_6X5_SRGB
1, // E5B9G9R9_FLOAT
1, // D32_FLOAT
1, // D16_UNORM
1, // X8_D24_UNORM
@@ -360,6 +359,7 @@ constexpr std::array<u8, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{
6, // ASTC_2D_8X6_SRGB
5, // ASTC_2D_6X5_UNORM
5, // ASTC_2D_6X5_SRGB
1, // E5B9G9R9_FLOAT
1, // D32_FLOAT
1, // D16_UNORM
1, // X8_D24_UNORM
@@ -469,6 +469,7 @@ constexpr std::array<u8, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{
128, // ASTC_2D_8X6_SRGB
128, // ASTC_2D_6X5_UNORM
128, // ASTC_2D_6X5_SRGB
32, // E5B9G9R9_FLOAT
32, // D32_FLOAT
16, // D16_UNORM
32, // X8_D24_UNORM

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -138,7 +135,7 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red,
case Hash(TextureFormat::R32, SINT):
return PixelFormat::R32_SINT;
case Hash(TextureFormat::E5B9G9R9, FLOAT):
return PixelFormat::B10G11R11_FLOAT;
return PixelFormat::E5B9G9R9_FLOAT;
case Hash(TextureFormat::Z32, FLOAT):
return PixelFormat::D32_FLOAT;
case Hash(TextureFormat::Z32, FLOAT, UINT, UINT, UINT, LINEAR):

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -208,7 +205,8 @@ struct fmt::formatter<VideoCore::Surface::PixelFormat> : fmt::formatter<fmt::str
return "ASTC_2D_6X5_UNORM";
case PixelFormat::ASTC_2D_6X5_SRGB:
return "ASTC_2D_6X5_SRGB";
case PixelFormat::E5B9G9R9_FLOAT:
return "E5B9G9R9_FLOAT";
case PixelFormat::D32_FLOAT:
return "D32_FLOAT";
case PixelFormat::D16_UNORM:

View File

@@ -1,6 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -100,13 +99,24 @@ constexpr std::array VK_FORMAT_A4B4G4R4_UNORM_PACK16{
// - RGBA16F: Last resort only if RGB8 variants fail (should never happen)
constexpr std::array B10G11R11_UFLOAT_PACK32{
#ifdef ANDROID
VK_FORMAT_A8B8G8R8_SRGB_PACK32, // sRGB variant (for gamma-correct fallback)
VK_FORMAT_A8B8G8R8_SRGB_PACK32, // sRGB variant (for gamma-correct fallback)
#else
VK_FORMAT_A8B8G8R8_UNORM_PACK32, // Primary fallback: RGBA8 LDR (32-bit, universal)
VK_FORMAT_A2B10G10R10_UNORM_PACK32, // Better precision: RGB10A2 (32-bit, common)
VK_FORMAT_A8B8G8R8_UNORM_PACK32, // Primary fallback: RGBA8 LDR (32-bit, universal)
VK_FORMAT_A2B10G10R10_UNORM_PACK32, // Better precision: RGB10A2 (32-bit, common)
#endif
VK_FORMAT_R16G16B16A16_SFLOAT, // Emergency fallback: RGBA16F (64-bit, should never reach)
VK_FORMAT_UNDEFINED,
VK_FORMAT_R16G16B16A16_SFLOAT, // Emergency fallback: RGBA16F (64-bit, should never reach)
VK_FORMAT_UNDEFINED,
};
// E5B9G9R9_UFLOAT (RGB9E5) - INVALID for COLOR_ATTACHMENT on Nintendo Switch
// Nintendo Switch hardware validation: NO COLOR_ATTACHMENT_BIT (only SAMPLED_IMAGE)
// Reference: vp_gpuinfo_nintendo_switch_v2_495_0_0_0 - Missing required attachment bits
// This format should NEVER be used as render target, only for texture sampling
constexpr std::array E5B9G9R9_UFLOAT_PACK32{
VK_FORMAT_B10G11R11_UFLOAT_PACK32, // Upgrade to proper HDR format with attachment support
VK_FORMAT_A8B8G8R8_UNORM_PACK32, // Fallback: RGBA8 LDR
VK_FORMAT_R16G16B16A16_SFLOAT, // Last resort: RGBA16F
VK_FORMAT_UNDEFINED,
};
} // namespace Alternatives
@@ -143,7 +153,8 @@ constexpr const VkFormat* GetFormatAlternatives(VkFormat format) {
return Alternatives::VK_FORMAT_A4B4G4R4_UNORM_PACK16.data();
case VK_FORMAT_B10G11R11_UFLOAT_PACK32:
return Alternatives::B10G11R11_UFLOAT_PACK32.data();
case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32:
return Alternatives::E5B9G9R9_UFLOAT_PACK32.data();
default:
return nullptr;
}
@@ -518,20 +529,19 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
// Driver-specific handling for VK_EXT_custom_border_color
// On some Qualcomm/Turnip/ARM drivers the extension may be partially implemented.
// Disable completely if no feature bits are reported to avoid crashes/undefined behavior.
// Enable it if ANY useful feature bit is reported; otherwise, let the removal pass drop it.
if (is_qualcomm || is_turnip || is_arm) {
const bool has_any_custom_border_color =
features.custom_border_color.customBorderColors ||
features.custom_border_color.customBorderColorWithoutFormat;
if (!has_any_custom_border_color) {
LOG_WARNING(Render_Vulkan,
"Disabling VK_EXT_custom_border_color on '{}' — no usable features reported",
"Disabling VK_EXT_custom_border_color on '{}' — no usable custom border color features reported",
properties.driver.driverName);
RemoveExtensionFeature(extensions.custom_border_color, features.custom_border_color,
VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
// Do not clear here; final removal happens in RemoveUnsuitableExtensions based on bits.
} else {
LOG_INFO(Render_Vulkan,
"VK_EXT_custom_border_color enabled on '{}' (partial support detected)",
"Partial VK_EXT_custom_border_color support detected on '{}' — enabling available features",
properties.driver.driverName);
}
}
@@ -543,32 +553,81 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
// Log Qualcomm-specific optimizations
if (extensions.render_pass_store_ops) {
LOG_INFO(Render_Vulkan, "VK_QCOM_render_pass_store_ops: Enabled");
LOG_INFO(Render_Vulkan, "VK_QCOM_render_pass_store_ops: ENABLED (TBDR store optimization)");
}
if (extensions.tile_properties) {
LOG_INFO(Render_Vulkan, "VK_QCOM_tile_properties: Enabled");
LOG_INFO(Render_Vulkan, "VK_QCOM_tile_properties: ENABLED (tile size queries available)");
}
if (extensions.render_pass_shader_resolve) {
LOG_INFO(Render_Vulkan, "VK_QCOM_render_pass_shader_resolve: Enabled");
}
if (extensions.render_pass_transform) {
LOG_INFO(Render_Vulkan, "VK_QCOM_render_pass_transform: Enabled");
}
if (extensions.rotated_copy_commands) {
LOG_INFO(Render_Vulkan, "VK_QCOM_rotated_copy_commands: Enabled");
}
if (extensions.image_processing) {
LOG_INFO(Render_Vulkan, "VK_QCOM_image_processing: Enabled");
LOG_INFO(Render_Vulkan, "VK_QCOM_render_pass_shader_resolve: ENABLED (HDR+MSAA shader resolve)");
}
// Shader Float Controls: Completely broken on Stock Qualcomm
// The extension causes rendering issues regardless of FP16/FP32 mode
// Turnip Mesa: Works correctly, keep enabled
if (!is_turnip) {
LOG_WARNING(Render_Vulkan, "Disabling Shader Float Controls for Stock Qualcomm (broken implementation)");
RemoveExtensionFeature(extensions.shader_float_controls, features.shader_float_controls,
VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME);
#ifdef ANDROID
// Shader Float Controls handling for Qualcomm Adreno
// Default: DISABLED due to historical issues with binning precision causing visual glitches
const bool force_enable = Settings::values.shader_float_controls_force_enable.GetValue();
if (force_enable) {
// User explicitly enabled float controls - log detected capabilities and user config
LOG_INFO(Render_Vulkan, "Shader Float Controls FORCE ENABLED by user (Eden Veil/Extensions)");
// Log driver capabilities
const auto& fc = properties.float_controls;
LOG_INFO(Render_Vulkan, "Driver Float Controls Capabilities:");
LOG_INFO(Render_Vulkan, " - Denorm Flush FP32: {}", fc.shaderDenormFlushToZeroFloat32 ? "YES" : "NO");
LOG_INFO(Render_Vulkan, " - Denorm Preserve FP32: {}", fc.shaderDenormPreserveFloat32 ? "YES" : "NO");
LOG_INFO(Render_Vulkan, " - RTE Rounding FP32: {}", fc.shaderRoundingModeRTEFloat32 ? "YES" : "NO");
LOG_INFO(Render_Vulkan, " - Signed Zero/Inf/Nan FP32: {}", fc.shaderSignedZeroInfNanPreserveFloat32 ? "YES" : "NO");
LOG_INFO(Render_Vulkan, " - Independence: {}",
fc.denormBehaviorIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL ? "ALL" : "LIMITED");
// Log user selections
bool ftz = Settings::values.shader_float_ftz.GetValue();
bool preserve = Settings::values.shader_float_denorm_preserve.GetValue();
const bool rte = Settings::values.shader_float_rte.GetValue();
const bool signed_zero = Settings::values.shader_float_signed_zero_inf_nan.GetValue();
// Validate mutually exclusive options
if (ftz && preserve) {
LOG_WARNING(Render_Vulkan,
"CONFLICT: FTZ and DenormPreserve are mutually exclusive!");
LOG_WARNING(Render_Vulkan,
" -> DenormPreserve will take precedence (accuracy over speed)");
ftz = false; // Preserve takes priority for correctness
}
LOG_INFO(Render_Vulkan, "User Float Behavior Selection:");
LOG_INFO(Render_Vulkan, " - Flush To Zero (FTZ): {}", ftz ? "ENABLED" : "disabled");
LOG_INFO(Render_Vulkan, " - Denorm Preserve: {}", preserve ? "ENABLED" : "disabled");
LOG_INFO(Render_Vulkan, " - Round To Even (RTE): {}", rte ? "ENABLED" : "disabled");
LOG_INFO(Render_Vulkan, " - Signed Zero/Inf/Nan: {}", signed_zero ? "ENABLED" : "disabled");
// Analyze configuration vs Switch native behavior
const bool matches_switch = ftz && !preserve && rte && signed_zero;
if (matches_switch) {
LOG_INFO(Render_Vulkan, "Configuration MATCHES Switch/Maxwell native behavior (FTZ+RTE+SignedZero)");
} else if (!ftz && !preserve && !rte && !signed_zero) {
LOG_WARNING(Render_Vulkan, "No float behaviors selected - using driver default (may cause glitches)");
} else {
LOG_INFO(Render_Vulkan, "Configuration is CUSTOM - testing mode active");
}
// Extension stays enabled
LOG_INFO(Render_Vulkan, "VK_KHR_shader_float_controls: ENABLED");
} else {
// Default behavior - disable float controls
LOG_WARNING(Render_Vulkan,
"Disabling shader float controls on Qualcomm (historical binning precision issues)");
LOG_INFO(Render_Vulkan,
"To enable: Eden Veil -> Extensions -> Shader Float Controls (Force Enable)");
RemoveExtension(extensions.shader_float_controls, VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME);
}
#else
// Non-Android: keep original behavior
LOG_WARNING(Render_Vulkan,
"Disabling shader float controls and 64-bit integer features on Qualcomm proprietary drivers");
RemoveExtension(extensions.shader_float_controls, VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME);
#endif
// Int64 atomics - genuinely broken, always disable
RemoveExtensionFeature(extensions.shader_atomic_int64, features.shader_atomic_int64,
@@ -617,7 +676,83 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
cant_blit_msaa = true;
}
}
// Dynamic state blacklists moved to GetSuitability() for proper ordering
if (extensions.extended_dynamic_state && is_radv) {
// Mask driver version variant
const u32 version = (properties.properties.driverVersion << 3) >> 3;
if (version < VK_MAKE_API_VERSION(0, 21, 2, 0)) {
LOG_WARNING(Render_Vulkan,
"RADV versions older than 21.2 have broken VK_EXT_extended_dynamic_state");
//RemoveExtensionFeature(extensions.extended_dynamic_state,
//features.extended_dynamic_state,
//VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
}
}
if (extensions.extended_dynamic_state2 && is_radv) {
const u32 version = (properties.properties.driverVersion << 3) >> 3;
if (version < VK_MAKE_API_VERSION(0, 22, 3, 1)) {
LOG_WARNING(
Render_Vulkan,
"RADV versions older than 22.3.1 have broken VK_EXT_extended_dynamic_state2");
// RemoveExtensionFeature(extensions.extended_dynamic_state2,
// features.extended_dynamic_state2,
// VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
}
}
if (extensions.extended_dynamic_state2 && is_qualcomm) {
const u32 version = (properties.properties.driverVersion << 3) >> 3;
if (version >= VK_MAKE_API_VERSION(0, 0, 676, 0) &&
version < VK_MAKE_API_VERSION(0, 0, 680, 0)) {
// Qualcomm Adreno 7xx drivers do not properly support extended_dynamic_state2.
LOG_WARNING(Render_Vulkan,
"Qualcomm Adreno 7xx drivers have broken VK_EXT_extended_dynamic_state2");
//RemoveExtensionFeature(extensions.extended_dynamic_state2,
//features.extended_dynamic_state2,
//VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
}
}
if (extensions.extended_dynamic_state3 && is_radv) {
LOG_WARNING(Render_Vulkan, "RADV has broken extendedDynamicState3ColorBlendEquation");
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = true;
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation = true;
dynamic_state3_blending = true;
const u32 version = (properties.properties.driverVersion << 3) >> 3;
if (version < VK_MAKE_API_VERSION(0, 23, 1, 0)) {
LOG_WARNING(Render_Vulkan,
"RADV versions older than 23.1.0 have broken depth clamp dynamic state");
features.extended_dynamic_state3.extendedDynamicState3DepthClampEnable = true;
dynamic_state3_enables = true;
}
}
if (extensions.extended_dynamic_state3 && (is_amd_driver || driver_id == VK_DRIVER_ID_SAMSUNG_PROPRIETARY)) {
// AMD and Samsung drivers have broken extendedDynamicState3ColorBlendEquation
LOG_WARNING(Render_Vulkan,
"AMD and Samsung drivers have broken extendedDynamicState3ColorBlendEquation");
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = true;
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation = true;
dynamic_state3_blending = true;
}
if (extensions.vertex_input_dynamic_state && is_radv) {
// TODO(ameerj): Blacklist only offending driver versions
// TODO(ameerj): Confirm if RDNA1 is affected
const bool is_rdna2 =
supported_extensions.contains(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME);
if (is_rdna2) {
LOG_WARNING(Render_Vulkan,
"RADV has broken VK_EXT_vertex_input_dynamic_state on RDNA2 hardware");
// RemoveExtensionFeature(extensions.vertex_input_dynamic_state,
// features.vertex_input_dynamic_state,
// VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
}
}
if (extensions.vertex_input_dynamic_state && is_qualcomm) {
// Qualcomm drivers do not properly support vertex_input_dynamic_state.
LOG_WARNING(Render_Vulkan,
"Qualcomm drivers have broken VK_EXT_vertex_input_dynamic_state");
//RemoveExtensionFeature(extensions.vertex_input_dynamic_state,
// features.vertex_input_dynamic_state,
// VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
}
sets_per_pool = 64;
if (is_amd_driver) {
@@ -646,7 +781,15 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
}
}
// VertexInputDynamicState blacklist moved to GetSuitability() for proper ordering
if (extensions.vertex_input_dynamic_state && is_intel_windows) {
const u32 version = (properties.properties.driverVersion << 3) >> 3;
if (version < VK_MAKE_API_VERSION(27, 20, 100, 0)) {
LOG_WARNING(Render_Vulkan, "Intel has broken VK_EXT_vertex_input_dynamic_state");
//RemoveExtensionFeature(extensions.vertex_input_dynamic_state,
//features.vertex_input_dynamic_state,
//VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
}
}
if (features.shader_float16_int8.shaderFloat16 && is_intel_windows) {
// Intel's compiler crashes when using fp16 on Astral Chain, disable it for the time being.
LOG_WARNING(Render_Vulkan, "Intel has broken float16 math");
@@ -722,11 +865,8 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
(std::min)(properties.properties.limits.maxVertexInputBindings, 16U);
}
if (is_turnip || is_qualcomm) {
// Ensure proper vertex input bindings limit for Qualcomm hardware
LOG_WARNING(Render_Vulkan,
"{}: Ensuring maxVertexInputBindings = 32",
is_turnip ? "Turnip" : "Qualcomm");
if (is_turnip) {
LOG_WARNING(Render_Vulkan, "Turnip requires higher-than-reported binding limits");
properties.properties.limits.maxVertexInputBindings = 32;
}
@@ -737,21 +877,41 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
}
// Intel iGPU/MoltenVK blacklist moved to GetSuitability() for proper ordering
if (!extensions.extended_dynamic_state2 && extensions.extended_dynamic_state3) {
LOG_INFO(Render_Vulkan,
"Removing extendedDynamicState3 due to missing extendedDynamicState2");
RemoveExtensionFeature(extensions.extended_dynamic_state3, features.extended_dynamic_state3,
VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME);
dynamic_state3_blending = true;
dynamic_state3_enables = true;
}
#ifdef ANDROID
// Stock Qualcomm and ARM Mali drivers don't report VK_FORMAT_*_SSCALED/USCALED formats
// Turnip implements them in software, so only force emulation for stock drivers
if ((is_qualcomm && !is_turnip) || is_arm) {
// Mesa Intel drivers on UHD 620 have broken EDS causing extreme flickering - unknown if it affects other iGPUs
// ALSO affects ALL versions of UHD drivers on Windows 10+, seems to cause even worse issues like straight up crashing
// So... Yeah, UHD drivers fucking suck -- maybe one day we can work past this, maybe; some driver hacking?
// And then we can rest in peace by doing `< VK_MAKE_API_VERSION(26, 0, 0)` for our beloved mesa drivers... one day
if ((is_mvk || (is_integrated && is_intel_anv) || (is_integrated && is_intel_windows)) && Settings::values.dyna_state.GetValue() != 0) {
LOG_WARNING(Render_Vulkan, "Driver has broken dynamic state, forcing to 0 to prevent graphical issues");
Settings::values.dyna_state.SetValue(0);
}
if (Settings::values.dyna_state.GetValue() == 0) {
must_emulate_scaled_formats = true;
LOG_INFO(Render_Vulkan, "Mobile GPU detected: forcing scaled format emulation (hardware limitation)");
LOG_INFO(Render_Vulkan, "Extended dynamic state is fully disabled, scaled format emulation is ON");
RemoveExtensionFeature(extensions.custom_border_color, features.custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2, VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
RemoveExtensionFeature(extensions.extended_dynamic_state3, features.extended_dynamic_state3, VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME);
RemoveExtensionFeature(extensions.vertex_input_dynamic_state, features.vertex_input_dynamic_state, VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
dynamic_state3_blending = false;
dynamic_state3_enables = false;
LOG_INFO(Render_Vulkan, "All dynamic state extensions and features have been disabled");
} else {
must_emulate_scaled_formats = false;
LOG_INFO(Render_Vulkan, "Extended dynamic state is enabled, scaled format emulation is OFF");
}
#else
// Desktop GPUs support scaled formats natively
must_emulate_scaled_formats = false;
#endif
logical = vk::Device::Create(physical, queue_cis, ExtensionListForVulkan(loaded_extensions), first_next, dld);
@@ -792,7 +952,7 @@ Device::~Device() {
VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags wanted_usage,
FormatType format_type) const {
if (IsFormatSupported(wanted_format, wanted_usage, format_type)) {
// Critical: Even if format is "supported", check for STORAGE + HDR + no MSAA support
// CRITICAL FIX: Even if format is "supported", check for STORAGE + HDR + no MSAA support
// Driver may report STORAGE_IMAGE_BIT but shaderStorageImageMultisample=false means
// it will fail at runtime when used with MSAA (CopyImageMSAA silently fails)
const bool requests_storage = (wanted_usage & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) != 0;
@@ -1219,148 +1379,6 @@ bool Device::GetSuitability(bool requires_swapchain) {
}
}
// CRITICAL: Apply driver-specific feature workarounds BEFORE validation
// These blacklists disable broken features on specific drivers to prevent
// rendering issues and crashes.
// MUST execute before RemoveUnsuitableExtensions() calculates feature flags.
const VkDriverId driver_id = properties.driver.driverID;
const bool is_radv = driver_id == VK_DRIVER_ID_MESA_RADV;
const bool is_amd_driver =
driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE;
const bool is_intel_windows = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS;
const bool is_intel_anv = driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA;
const bool is_qualcomm = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY;
const bool is_mvk = driver_id == VK_DRIVER_ID_MOLTENVK;
const bool is_integrated = properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
// VK_DYNAMIC_STATE
// Mesa Intel drivers on UHD 620 have broken EDS causing extreme flickering - unknown if it affects other iGPUs
// ALSO affects ALL versions of UHD drivers on Windows 10+, seems to cause even worse issues like straight up crashing
// So... Yeah, UHD drivers fucking suck -- maybe one day we can work past this, maybe; some driver hacking?
// And then we can rest in peace by doing `< VK_MAKE_API_VERSION(26, 0, 0)` for our beloved mesa drivers... one day
// Disable dynamic state on affected drivers
if ((is_mvk || (is_integrated && is_intel_anv) || (is_integrated && is_intel_windows)) &&
Settings::values.dyna_state.GetValue() != 0) {
LOG_WARNING(Render_Vulkan,
"Intel iGPU/MoltenVK: Forcing dyna_state=0 due to broken dynamic state implementation");
Settings::values.dyna_state.SetValue(0);
}
// VK_EXT_extended_dynamic_state
// RADV < 21.2.0: Broken ExtendedDynamicState implementation
// Disable entire extension on old drivers
if (extensions.extended_dynamic_state && is_radv) {
const u32 version = (properties.properties.driverVersion << 3) >> 3;
if (version < VK_MAKE_API_VERSION(0, 21, 2, 0)) {
LOG_WARNING(Render_Vulkan,
"RADV < 21.2.0: Disabling broken VK_EXT_extended_dynamic_state");
features.extended_dynamic_state.extendedDynamicState = false;
}
}
// VK_EXT_extended_dynamic_state2
// RADV < 22.3.1: Broken ExtendedDynamicState2 implementation
// Disable entire extension on old drivers
if (extensions.extended_dynamic_state2 && is_radv) {
const u32 version = (properties.properties.driverVersion << 3) >> 3;
if (version < VK_MAKE_API_VERSION(0, 22, 3, 1)) {
LOG_WARNING(Render_Vulkan,
"RADV < 22.3.1: Disabling broken VK_EXT_extended_dynamic_state2");
features.extended_dynamic_state2.extendedDynamicState2 = false;
}
}
// Qualcomm Adreno 7xx (drivers 676.0 - 679.x): Broken ExtendedDynamicState2
// Disable ExtendedDynamicState2 on affected driver versions
if (extensions.extended_dynamic_state2 && is_qualcomm) {
const u32 version = (properties.properties.driverVersion << 3) >> 3;
if (version >= VK_MAKE_API_VERSION(0, 0, 676, 0) &&
version < VK_MAKE_API_VERSION(0, 0, 680, 0)) {
LOG_WARNING(Render_Vulkan,
"Qualcomm Adreno 7xx (676-679): Disabling broken VK_EXT_extended_dynamic_state2");
features.extended_dynamic_state2.extendedDynamicState2 = false;
}
}
// VK_EXT_extended_dynamic_state3
// AMD/Samsung: Broken extendedDynamicState3ColorBlendEquation
// Disable blend equation dynamic state, force static pipeline state
if (extensions.extended_dynamic_state3 &&
(is_amd_driver || driver_id == VK_DRIVER_ID_SAMSUNG_PROPRIETARY)) {
LOG_WARNING(Render_Vulkan,
"AMD/Samsung: Disabling broken extendedDynamicState3ColorBlendEquation");
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = false;
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation = false;
}
// RADV: Broken extendedDynamicState3ColorBlendEquation
// Disable problematic features based on driver version < 23.1.0
if (extensions.extended_dynamic_state3 && is_radv) {
LOG_WARNING(Render_Vulkan, "RADV: Disabling broken extendedDynamicState3ColorBlendEquation");
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = false;
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation = false;
const u32 version = (properties.properties.driverVersion << 3) >> 3;
if (version < VK_MAKE_API_VERSION(0, 23, 1, 0)) {
LOG_WARNING(Render_Vulkan,
"RADV < 23.1.0: Disabling broken depth clamp dynamic state");
features.extended_dynamic_state3.extendedDynamicState3DepthClampEnable = false;
}
}
// VK_EXT_vertex_input_dynamic_state
// RADV + RDNA2: Broken VertexInputDynamicState on RDNA2 hardware
// Disable VertexInputDynamicState on RDNA2
if (extensions.vertex_input_dynamic_state && is_radv) {
const bool is_rdna2 =
supported_extensions.contains(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME);
if (is_rdna2) {
LOG_WARNING(Render_Vulkan,
"RADV + RDNA2: Disabling broken VK_EXT_vertex_input_dynamic_state");
features.vertex_input_dynamic_state.vertexInputDynamicState = false;
}
}
// Qualcomm: Broken VertexInputDynamicState implementation
// Disable VertexInputDynamicState on all Qualcomm drivers
if (extensions.vertex_input_dynamic_state && is_qualcomm) {
LOG_WARNING(Render_Vulkan,
"Qualcomm: Disabling broken VK_EXT_vertex_input_dynamic_state");
features.vertex_input_dynamic_state.vertexInputDynamicState = false;
}
// Intel Windows < 27.20.100.0: Broken VertexInputDynamicState
// Disable VertexInputDynamicState on old Intel Windows drivers
if (extensions.vertex_input_dynamic_state && is_intel_windows) {
const u32 version = (properties.properties.driverVersion << 3) >> 3;
if (version < VK_MAKE_API_VERSION(27, 20, 100, 0)) {
LOG_WARNING(Render_Vulkan,
"Intel Windows < 27.20.100.0: Disabling broken VK_EXT_vertex_input_dynamic_state");
features.vertex_input_dynamic_state.vertexInputDynamicState = false;
}
}
// If user setting is dyna_state=0, disable all dynamic state features
if (Settings::values.dyna_state.GetValue() == 0) {
LOG_INFO(Render_Vulkan, "Dynamic state disabled by user setting, clearing all EDS features");
features.custom_border_color.customBorderColors = false;
features.custom_border_color.customBorderColorWithoutFormat = false;
features.extended_dynamic_state.extendedDynamicState = false;
features.extended_dynamic_state2.extendedDynamicState2 = false;
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = false;
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation = false;
features.extended_dynamic_state3.extendedDynamicState3ColorWriteMask = false;
features.extended_dynamic_state3.extendedDynamicState3DepthClampEnable = false;
features.extended_dynamic_state3.extendedDynamicState3LogicOpEnable = false;
// Note: vertex_input_dynamic_state has independent toggle, NOT affected by dyna_state=0
}
// Return whether we were suitable.
return suitable;
}
@@ -1512,18 +1530,11 @@ void Device::RemoveUnsuitableExtensions() {
RemoveExtensionIfUnsuitable(extensions.swapchain_maintenance1, VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME);
// VK_EXT_vertex_input_dynamic_state
if (Settings::values.vertex_input_dynamic_state.GetValue()) {
extensions.vertex_input_dynamic_state =
features.vertex_input_dynamic_state.vertexInputDynamicState;
RemoveExtensionFeatureIfUnsuitable(extensions.vertex_input_dynamic_state,
features.vertex_input_dynamic_state,
VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
} else {
RemoveExtensionFeature(extensions.vertex_input_dynamic_state,
features.vertex_input_dynamic_state,
VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
LOG_INFO(Render_Vulkan, "Vertex Input Dynamic State disabled by user setting");
}
extensions.vertex_input_dynamic_state =
features.vertex_input_dynamic_state.vertexInputDynamicState;
RemoveExtensionFeatureIfUnsuitable(extensions.vertex_input_dynamic_state,
features.vertex_input_dynamic_state,
VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
// VK_KHR_pipeline_executable_properties
if (Settings::values.renderer_shader_feedback.GetValue()) {

View File

@@ -64,7 +64,6 @@ VK_DEFINE_HANDLE(VmaAllocator)
pipeline_executable_properties) \
FEATURE(KHR, WorkgroupMemoryExplicitLayout, WORKGROUP_MEMORY_EXPLICIT_LAYOUT, \
workgroup_memory_explicit_layout) \
FEATURE(QCOM, ImageProcessing, IMAGE_PROCESSING, image_processing_qcom) \
FEATURE(QCOM, TileProperties, TILE_PROPERTIES, tile_properties_qcom)
// Define miscellaneous extensions which may be used by the implementation here.
@@ -99,9 +98,6 @@ VK_DEFINE_HANDLE(VmaAllocator)
EXTENSION(QCOM, FILTER_CUBIC_WEIGHTS, filter_cubic_weights) \
EXTENSION(QCOM, RENDER_PASS_SHADER_RESOLVE, render_pass_shader_resolve) \
EXTENSION(QCOM, RENDER_PASS_STORE_OPS, render_pass_store_ops) \
EXTENSION(QCOM, RENDER_PASS_TRANSFORM, render_pass_transform) \
EXTENSION(QCOM, ROTATED_COPY_COMMANDS, rotated_copy_commands) \
EXTENSION(QCOM, IMAGE_PROCESSING, image_processing) \
EXTENSION(QCOM, TILE_PROPERTIES, tile_properties) \
EXTENSION(KHR, MAINTENANCE_1, maintenance1) \
EXTENSION(KHR, MAINTENANCE_2, maintenance2) \
@@ -552,21 +548,6 @@ public:
return extensions.custom_border_color;
}
/// Base Vulkan Dynamic State support checks.
/// These provide granular control over each base dynamic state, allowing individual states
/// to be disabled if broken driver implementations are detected at device initialization.
/// By default all states are enabled. If a specific driver has issues with certain states,
/// they can be disabled in vulkan_device.cpp constructor (see has_broken_compute pattern).
bool SupportsDynamicViewport() const { return supports_dynamic_viewport; }
bool SupportsDynamicScissor() const { return supports_dynamic_scissor; }
bool SupportsDynamicLineWidth() const { return supports_dynamic_line_width; }
bool SupportsDynamicDepthBias() const { return supports_dynamic_depth_bias; }
bool SupportsDynamicBlendConstants() const { return supports_dynamic_blend_constants; }
bool SupportsDynamicDepthBounds() const { return supports_dynamic_depth_bounds; }
bool SupportsDynamicStencilCompareMask() const { return supports_dynamic_stencil_compare; }
bool SupportsDynamicStencilWriteMask() const { return supports_dynamic_stencil_write; }
bool SupportsDynamicStencilReference() const { return supports_dynamic_stencil_reference; }
/// Returns true if the device supports VK_EXT_extended_dynamic_state.
bool IsExtExtendedDynamicStateSupported() const {
return extensions.extended_dynamic_state;
@@ -601,98 +582,6 @@ public:
return dynamic_state3_enables;
}
// EDS2 granular feature checks
bool IsExtExtendedDynamicState2LogicOpSupported() const {
return extensions.extended_dynamic_state2 &&
features.extended_dynamic_state2.extendedDynamicState2LogicOp;
}
bool IsExtExtendedDynamicState2PatchControlPointsSupported() const {
return extensions.extended_dynamic_state2 &&
features.extended_dynamic_state2.extendedDynamicState2PatchControlPoints;
}
// EDS3 granular feature checks
bool IsExtExtendedDynamicState3DepthClampEnableSupported() const {
return extensions.extended_dynamic_state3 &&
features.extended_dynamic_state3.extendedDynamicState3DepthClampEnable;
}
bool IsExtExtendedDynamicState3LogicOpEnableSupported() const {
return extensions.extended_dynamic_state3 &&
features.extended_dynamic_state3.extendedDynamicState3LogicOpEnable;
}
bool IsExtExtendedDynamicState3TessellationDomainOriginSupported() const {
return extensions.extended_dynamic_state3 &&
features.extended_dynamic_state3.extendedDynamicState3TessellationDomainOrigin;
}
bool IsExtExtendedDynamicState3PolygonModeSupported() const {
return extensions.extended_dynamic_state3 &&
features.extended_dynamic_state3.extendedDynamicState3PolygonMode;
}
bool IsExtExtendedDynamicState3RasterizationSamplesSupported() const {
return extensions.extended_dynamic_state3 &&
features.extended_dynamic_state3.extendedDynamicState3RasterizationSamples;
}
bool IsExtExtendedDynamicState3SampleMaskSupported() const {
return extensions.extended_dynamic_state3 &&
features.extended_dynamic_state3.extendedDynamicState3SampleMask;
}
bool IsExtExtendedDynamicState3AlphaToCoverageEnableSupported() const {
return extensions.extended_dynamic_state3 &&
features.extended_dynamic_state3.extendedDynamicState3AlphaToCoverageEnable;
}
bool IsExtExtendedDynamicState3AlphaToOneEnableSupported() const {
return extensions.extended_dynamic_state3 &&
features.extended_dynamic_state3.extendedDynamicState3AlphaToOneEnable;
}
bool IsExtExtendedDynamicState3DepthClipEnableSupported() const {
return extensions.extended_dynamic_state3 &&
features.extended_dynamic_state3.extendedDynamicState3DepthClipEnable;
}
bool IsExtExtendedDynamicState3DepthClipNegativeOneToOneSupported() const {
return extensions.extended_dynamic_state3 &&
features.extended_dynamic_state3.extendedDynamicState3DepthClipNegativeOneToOne;
}
bool IsExtExtendedDynamicState3LineRasterizationModeSupported() const {
return extensions.extended_dynamic_state3 &&
features.extended_dynamic_state3.extendedDynamicState3LineRasterizationMode;
}
bool IsExtExtendedDynamicState3LineStippleEnableSupported() const {
return extensions.extended_dynamic_state3 &&
features.extended_dynamic_state3.extendedDynamicState3LineStippleEnable;
}
bool IsExtExtendedDynamicState3ProvokingVertexModeSupported() const {
return extensions.extended_dynamic_state3 &&
features.extended_dynamic_state3.extendedDynamicState3ProvokingVertexMode;
}
bool IsExtExtendedDynamicState3ConservativeRasterizationModeSupported() const {
return extensions.extended_dynamic_state3 &&
features.extended_dynamic_state3.extendedDynamicState3ConservativeRasterizationMode;
}
bool IsExtExtendedDynamicState3SampleLocationsEnableSupported() const {
return extensions.extended_dynamic_state3 &&
features.extended_dynamic_state3.extendedDynamicState3SampleLocationsEnable;
}
bool IsExtExtendedDynamicState3RasterizationStreamSupported() const {
return extensions.extended_dynamic_state3 &&
features.extended_dynamic_state3.extendedDynamicState3RasterizationStream;
}
/// Returns true if the device supports VK_EXT_filter_cubic
bool IsExtFilterCubicSupported() const {
return extensions.filter_cubic;
@@ -718,21 +607,6 @@ public:
return extensions.tile_properties;
}
/// Returns true if the device supports VK_QCOM_render_pass_transform
bool IsQcomRenderPassTransformSupported() const {
return extensions.render_pass_transform;
}
/// Returns true if the device supports VK_QCOM_rotated_copy_commands
bool IsQcomRotatedCopyCommandsSupported() const {
return extensions.rotated_copy_commands;
}
/// Returns true if the device supports VK_QCOM_image_processing
bool IsQcomImageProcessingSupported() const {
return extensions.image_processing;
}
/// Returns Qualcomm tile size (width, height, depth). Returns {0,0,0} if not queried or unsupported
VkExtent3D GetQcomTileSize() const {
return properties.qcom_tile_size;
@@ -1044,22 +918,6 @@ private:
bool dynamic_state3_blending{}; ///< Has all blending features of dynamic_state3.
bool dynamic_state3_enables{}; ///< Has all enables features of dynamic_state3.
bool supports_conditional_barriers{}; ///< Allows barriers in conditional control flow.
/// Base Vulkan Dynamic State support flags (granular fallback for broken drivers).
/// All default to true. These can be individually disabled in vulkan_device.cpp
/// if specific broken driver implementations are detected during initialization.
/// This provides emergency protection against drivers that report support but crash/misbehave.
/// Pattern: Check driver/device and set to false in vulkan_device.cpp constructor.
bool supports_dynamic_viewport{true}; ///< VK_DYNAMIC_STATE_VIEWPORT
bool supports_dynamic_scissor{true}; ///< VK_DYNAMIC_STATE_SCISSOR
bool supports_dynamic_line_width{true}; ///< VK_DYNAMIC_STATE_LINE_WIDTH
bool supports_dynamic_depth_bias{true}; ///< VK_DYNAMIC_STATE_DEPTH_BIAS
bool supports_dynamic_blend_constants{true}; ///< VK_DYNAMIC_STATE_BLEND_CONSTANTS
bool supports_dynamic_depth_bounds{true}; ///< VK_DYNAMIC_STATE_DEPTH_BOUNDS
bool supports_dynamic_stencil_compare{true}; ///< VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK
bool supports_dynamic_stencil_write{true}; ///< VK_DYNAMIC_STATE_STENCIL_WRITE_MASK
bool supports_dynamic_stencil_reference{true};///< VK_DYNAMIC_STATE_STENCIL_REFERENCE
u64 device_access_memory{}; ///< Total size of device local memory in bytes.
u32 sets_per_pool{}; ///< Sets per Description Pool
NvidiaArchitecture nvidia_arch{NvidiaArchitecture::Arch_AmpereOrNewer};