Compare commits
2 Commits
refactored
...
showcase
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c99f0c166 | ||
|
|
520e07e756 |
@@ -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};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 &&
|
||||
|
||||
@@ -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{};
|
||||
|
||||
@@ -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{};
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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)};
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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};
|
||||
|
||||
Reference in New Issue
Block a user