[vk, SPIR-V] Various changes in an attempt to fix MH on Android
This commit is contained in:
parent
eb98fcd8ab
commit
d6551b049c
10 changed files with 287 additions and 44 deletions
|
@ -409,7 +409,7 @@ void SetupTransformFeedbackCapabilities(EmitContext& ctx, Id main_func) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ctx) {
|
void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ctx) {
|
||||||
if (info.uses_sampled_1d) {
|
if (info.uses_sampled_1d || info.uses_image_1d) {
|
||||||
ctx.AddCapability(spv::Capability::Sampled1D);
|
ctx.AddCapability(spv::Capability::Sampled1D);
|
||||||
}
|
}
|
||||||
if (info.uses_sparse_residency) {
|
if (info.uses_sparse_residency) {
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <boost/container/static_vector.hpp>
|
#include <boost/container/static_vector.hpp>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
#include "shader_recompiler/backend/spirv/emit_spirv.h"
|
||||||
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
|
||||||
|
@ -185,6 +186,84 @@ private:
|
||||||
spv::ImageOperandsMask mask{};
|
spv::ImageOperandsMask mask{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Id SampledVectorType(EmitContext& ctx, TextureComponentType component_type) {
|
||||||
|
switch (component_type) {
|
||||||
|
case TextureComponentType::Float:
|
||||||
|
return ctx.F32[4];
|
||||||
|
case TextureComponentType::Sint:
|
||||||
|
return ctx.S32[4];
|
||||||
|
case TextureComponentType::Uint:
|
||||||
|
return ctx.U32[4];
|
||||||
|
}
|
||||||
|
throw LogicError("Unhandled texture component type {}", static_cast<u32>(component_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExpectsFloatResult(const IR::Inst* inst) {
|
||||||
|
switch (inst->Type()) {
|
||||||
|
case IR::Type::F32:
|
||||||
|
case IR::Type::F32x2:
|
||||||
|
case IR::Type::F32x3:
|
||||||
|
case IR::Type::F32x4:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Id MakeFloatVector(EmitContext& ctx, float value) {
|
||||||
|
const Id scalar{ctx.Const(value)};
|
||||||
|
return ctx.ConstantComposite(ctx.F32[4], scalar, scalar, scalar, scalar);
|
||||||
|
}
|
||||||
|
|
||||||
|
Id NormalizeUnsignedSample(EmitContext& ctx, u32 component_bits, Id value) {
|
||||||
|
if (component_bits == 0) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
const double max_value = std::exp2(static_cast<double>(component_bits)) - 1.0;
|
||||||
|
if (!(max_value > 0.0)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
const float inv_max = static_cast<float>(1.0 / max_value);
|
||||||
|
return ctx.OpFMul(ctx.F32[4], value, MakeFloatVector(ctx, inv_max));
|
||||||
|
}
|
||||||
|
|
||||||
|
Id NormalizeSignedSample(EmitContext& ctx, u32 component_bits, Id value) {
|
||||||
|
if (component_bits == 0) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
const double positive_max = component_bits > 0 ? std::exp2(static_cast<double>(component_bits - 1)) - 1.0 : 0.0;
|
||||||
|
if (!(positive_max > 0.0)) {
|
||||||
|
return ctx.OpFClamp(ctx.F32[4], value, MakeFloatVector(ctx, -1.0f), MakeFloatVector(ctx, 1.0f));
|
||||||
|
}
|
||||||
|
const float inv_pos = static_cast<float>(1.0 / positive_max);
|
||||||
|
const Id scaled{ctx.OpFMul(ctx.F32[4], value, MakeFloatVector(ctx, inv_pos))};
|
||||||
|
return ctx.OpFClamp(ctx.F32[4], scaled, MakeFloatVector(ctx, -1.0f), MakeFloatVector(ctx, 1.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
Id ConvertSampleToExpectedType(EmitContext& ctx, const IR::Inst* inst,
|
||||||
|
const TextureDefinition* texture_def, Id value) {
|
||||||
|
if (!texture_def || texture_def->component_type == TextureComponentType::Float) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
if (!ExpectsFloatResult(inst)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
switch (texture_def->component_type) {
|
||||||
|
case TextureComponentType::Sint: {
|
||||||
|
const Id as_float{ctx.OpConvertSToF(ctx.F32[4], value)};
|
||||||
|
return NormalizeSignedSample(ctx, texture_def->component_bit_size, as_float);
|
||||||
|
}
|
||||||
|
case TextureComponentType::Uint: {
|
||||||
|
const Id as_float{ctx.OpConvertUToF(ctx.F32[4], value)};
|
||||||
|
return NormalizeUnsignedSample(ctx, texture_def->component_bit_size, as_float);
|
||||||
|
}
|
||||||
|
case TextureComponentType::Float:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
Id Texture(EmitContext& ctx, IR::TextureInstInfo info, [[maybe_unused]] const IR::Value& index) {
|
Id Texture(EmitContext& ctx, IR::TextureInstInfo info, [[maybe_unused]] const IR::Value& index) {
|
||||||
const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
|
const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
|
||||||
if (def.count > 1) {
|
if (def.count > 1) {
|
||||||
|
@ -449,31 +528,39 @@ Id EmitBoundImageWrite(EmitContext&) {
|
||||||
Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||||
Id bias_lc, const IR::Value& offset) {
|
Id bias_lc, const IR::Value& offset) {
|
||||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||||
|
const TextureDefinition* texture_def =
|
||||||
|
info.type == TextureType::Buffer ? nullptr : &ctx.textures.at(info.descriptor_index);
|
||||||
|
const Id result_type =
|
||||||
|
texture_def ? SampledVectorType(ctx, texture_def->component_type) : ctx.F32[4];
|
||||||
|
Id sample{};
|
||||||
if (ctx.stage == Stage::Fragment) {
|
if (ctx.stage == Stage::Fragment) {
|
||||||
const ImageOperands operands(ctx, info.has_bias != 0, false, info.has_lod_clamp != 0,
|
const ImageOperands operands(ctx, info.has_bias != 0, false, info.has_lod_clamp != 0,
|
||||||
bias_lc, offset);
|
bias_lc, offset);
|
||||||
return Emit(&EmitContext::OpImageSparseSampleImplicitLod,
|
sample = Emit(&EmitContext::OpImageSparseSampleImplicitLod,
|
||||||
&EmitContext::OpImageSampleImplicitLod, ctx, inst, ctx.F32[4],
|
&EmitContext::OpImageSampleImplicitLod, ctx, inst, result_type,
|
||||||
Texture(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
|
Texture(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
|
||||||
} else {
|
} else {
|
||||||
// We can't use implicit lods on non-fragment stages on SPIR-V. Maxwell hardware behaves as
|
|
||||||
// if the lod was explicitly zero. This may change on Turing with implicit compute
|
|
||||||
// derivatives
|
|
||||||
const Id lod{ctx.Const(0.0f)};
|
const Id lod{ctx.Const(0.0f)};
|
||||||
const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod, offset);
|
const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod, offset);
|
||||||
return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
|
sample = Emit(&EmitContext::OpImageSparseSampleExplicitLod,
|
||||||
&EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
|
&EmitContext::OpImageSampleExplicitLod, ctx, inst, result_type,
|
||||||
Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
|
Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
|
||||||
}
|
}
|
||||||
|
return ConvertSampleToExpectedType(ctx, inst, texture_def, sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||||
Id lod, const IR::Value& offset) {
|
Id lod, const IR::Value& offset) {
|
||||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||||
|
const TextureDefinition* texture_def =
|
||||||
|
info.type == TextureType::Buffer ? nullptr : &ctx.textures.at(info.descriptor_index);
|
||||||
|
const Id result_type =
|
||||||
|
texture_def ? SampledVectorType(ctx, texture_def->component_type) : ctx.F32[4];
|
||||||
const ImageOperands operands(ctx, false, true, false, lod, offset);
|
const ImageOperands operands(ctx, false, true, false, lod, offset);
|
||||||
return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
|
const Id sample = Emit(&EmitContext::OpImageSparseSampleExplicitLod,
|
||||||
&EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
|
&EmitContext::OpImageSampleExplicitLod, ctx, inst, result_type,
|
||||||
Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
|
Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
|
||||||
|
return ConvertSampleToExpectedType(ctx, inst, texture_def, sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
|
Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
|
||||||
|
@ -509,13 +596,19 @@ Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Va
|
||||||
Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||||
const IR::Value& offset, const IR::Value& offset2) {
|
const IR::Value& offset, const IR::Value& offset2) {
|
||||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||||
|
const TextureDefinition* texture_def =
|
||||||
|
info.type == TextureType::Buffer ? nullptr : &ctx.textures.at(info.descriptor_index);
|
||||||
|
const Id result_type =
|
||||||
|
texture_def ? SampledVectorType(ctx, texture_def->component_type) : ctx.F32[4];
|
||||||
const ImageOperands operands(ctx, offset, offset2);
|
const ImageOperands operands(ctx, offset, offset2);
|
||||||
if (ctx.profile.need_gather_subpixel_offset) {
|
if (ctx.profile.need_gather_subpixel_offset) {
|
||||||
coords = ImageGatherSubpixelOffset(ctx, info, TextureImage(ctx, info, index), coords);
|
coords = ImageGatherSubpixelOffset(ctx, info, TextureImage(ctx, info, index), coords);
|
||||||
}
|
}
|
||||||
return Emit(&EmitContext::OpImageSparseGather, &EmitContext::OpImageGather, ctx, inst,
|
const Id sample = Emit(&EmitContext::OpImageSparseGather, &EmitContext::OpImageGather, ctx, inst,
|
||||||
ctx.F32[4], Texture(ctx, info, index), coords, ctx.Const(info.gather_component),
|
result_type, Texture(ctx, info, index), coords,
|
||||||
operands.MaskOptional(), operands.Span());
|
ctx.Const(info.gather_component), operands.MaskOptional(),
|
||||||
|
operands.Span());
|
||||||
|
return ConvertSampleToExpectedType(ctx, inst, texture_def, sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||||
|
@ -538,12 +631,17 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c
|
||||||
lod = Id{};
|
lod = Id{};
|
||||||
}
|
}
|
||||||
if (Sirit::ValidId(ms)) {
|
if (Sirit::ValidId(ms)) {
|
||||||
// This image is multisampled, lod must be implicit
|
|
||||||
lod = Id{};
|
lod = Id{};
|
||||||
}
|
}
|
||||||
|
const TextureDefinition* texture_def =
|
||||||
|
info.type == TextureType::Buffer ? nullptr : &ctx.textures.at(info.descriptor_index);
|
||||||
|
const Id result_type =
|
||||||
|
texture_def ? SampledVectorType(ctx, texture_def->component_type) : ctx.F32[4];
|
||||||
const ImageOperands operands(lod, ms);
|
const ImageOperands operands(lod, ms);
|
||||||
return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4],
|
const Id sample = Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst,
|
||||||
TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
|
result_type, TextureImage(ctx, info, index), coords,
|
||||||
|
operands.MaskOptional(), operands.Span());
|
||||||
|
return ConvertSampleToExpectedType(ctx, inst, texture_def, sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod,
|
Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod,
|
||||||
|
@ -588,14 +686,19 @@ 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 EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
|
||||||
Id derivatives, const IR::Value& offset, Id lod_clamp) {
|
Id derivatives, const IR::Value& offset, Id lod_clamp) {
|
||||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||||
|
const TextureDefinition* texture_def =
|
||||||
|
info.type == TextureType::Buffer ? nullptr : &ctx.textures.at(info.descriptor_index);
|
||||||
|
const Id result_type =
|
||||||
|
texture_def ? SampledVectorType(ctx, texture_def->component_type) : ctx.F32[4];
|
||||||
const auto operands = info.num_derivatives == 3
|
const auto operands = info.num_derivatives == 3
|
||||||
? ImageOperands(ctx, info.has_lod_clamp != 0, derivatives,
|
? ImageOperands(ctx, info.has_lod_clamp != 0, derivatives,
|
||||||
ctx.Def(offset), {}, lod_clamp)
|
ctx.Def(offset), {}, lod_clamp)
|
||||||
: ImageOperands(ctx, info.has_lod_clamp != 0, derivatives,
|
: ImageOperands(ctx, info.has_lod_clamp != 0, derivatives,
|
||||||
info.num_derivatives, offset, lod_clamp);
|
info.num_derivatives, offset, lod_clamp);
|
||||||
return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
|
const Id sample = Emit(&EmitContext::OpImageSparseSampleExplicitLod,
|
||||||
&EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
|
&EmitContext::OpImageSampleExplicitLod, ctx, inst, result_type,
|
||||||
Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
|
Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
|
||||||
|
return ConvertSampleToExpectedType(ctx, inst, texture_def, sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords) {
|
Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords) {
|
||||||
|
|
|
@ -28,9 +28,23 @@ enum class Operation {
|
||||||
FPMax,
|
FPMax,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Id ComponentTypeId(EmitContext& ctx, TextureComponentType component_type) {
|
||||||
|
switch (component_type) {
|
||||||
|
case TextureComponentType::Float:
|
||||||
|
return ctx.F32[1];
|
||||||
|
case TextureComponentType::Sint:
|
||||||
|
return ctx.S32[1];
|
||||||
|
case TextureComponentType::Uint:
|
||||||
|
return ctx.U32[1];
|
||||||
|
}
|
||||||
|
throw LogicError("Unhandled texture component type {}", static_cast<u32>(component_type));
|
||||||
|
}
|
||||||
|
|
||||||
Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) {
|
Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) {
|
||||||
const spv::ImageFormat format{spv::ImageFormat::Unknown};
|
const spv::ImageFormat format{spv::ImageFormat::Unknown};
|
||||||
const Id type{ctx.F32[1]};
|
const TextureComponentType component_type = desc.is_depth ? TextureComponentType::Float
|
||||||
|
: desc.component_type;
|
||||||
|
const Id type{ComponentTypeId(ctx, component_type)};
|
||||||
const bool depth{desc.is_depth};
|
const bool depth{desc.is_depth};
|
||||||
const bool ms{desc.is_multisample};
|
const bool ms{desc.is_multisample};
|
||||||
switch (desc.type) {
|
switch (desc.type) {
|
||||||
|
@ -1374,6 +1388,8 @@ void EmitContext::DefineTextures(const Info& info, u32& binding, u32& scaling_in
|
||||||
.image_type = image_type,
|
.image_type = image_type,
|
||||||
.count = desc.count,
|
.count = desc.count,
|
||||||
.is_multisample = desc.is_multisample,
|
.is_multisample = desc.is_multisample,
|
||||||
|
.component_type = desc.component_type,
|
||||||
|
.component_bit_size = desc.component_bit_size,
|
||||||
});
|
});
|
||||||
if (profile.supported_spirv >= 0x00010400) {
|
if (profile.supported_spirv >= 0x00010400) {
|
||||||
interfaces.push_back(id);
|
interfaces.push_back(id);
|
||||||
|
@ -1417,6 +1433,12 @@ void EmitContext::DefineInputs(const IR::Program& program) {
|
||||||
const Info& info{program.info};
|
const Info& info{program.info};
|
||||||
const VaryingState loads{info.loads.mask | info.passthrough.mask};
|
const VaryingState loads{info.loads.mask | info.passthrough.mask};
|
||||||
|
|
||||||
|
const auto decorate_flat_if_fragment = [this](Id id) {
|
||||||
|
if (stage == Stage::Fragment) {
|
||||||
|
Decorate(id, spv::Decoration::Flat);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (info.uses_workgroup_id) {
|
if (info.uses_workgroup_id) {
|
||||||
workgroup_id = DefineInput(*this, U32[3], false, spv::BuiltIn::WorkgroupId);
|
workgroup_id = DefineInput(*this, U32[3], false, spv::BuiltIn::WorkgroupId);
|
||||||
}
|
}
|
||||||
|
@ -1432,16 +1454,22 @@ void EmitContext::DefineInputs(const IR::Program& program) {
|
||||||
}
|
}
|
||||||
if (info.uses_sample_id) {
|
if (info.uses_sample_id) {
|
||||||
sample_id = DefineInput(*this, U32[1], false, spv::BuiltIn::SampleId);
|
sample_id = DefineInput(*this, U32[1], false, spv::BuiltIn::SampleId);
|
||||||
|
decorate_flat_if_fragment(sample_id);
|
||||||
}
|
}
|
||||||
if (info.uses_is_helper_invocation) {
|
if (info.uses_is_helper_invocation) {
|
||||||
is_helper_invocation = DefineInput(*this, U1, false, spv::BuiltIn::HelperInvocation);
|
is_helper_invocation = DefineInput(*this, U1, false, spv::BuiltIn::HelperInvocation);
|
||||||
}
|
}
|
||||||
if (info.uses_subgroup_mask) {
|
if (info.uses_subgroup_mask) {
|
||||||
subgroup_mask_eq = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupEqMaskKHR);
|
subgroup_mask_eq = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupEqMaskKHR);
|
||||||
|
decorate_flat_if_fragment(subgroup_mask_eq);
|
||||||
subgroup_mask_lt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLtMaskKHR);
|
subgroup_mask_lt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLtMaskKHR);
|
||||||
|
decorate_flat_if_fragment(subgroup_mask_lt);
|
||||||
subgroup_mask_le = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLeMaskKHR);
|
subgroup_mask_le = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLeMaskKHR);
|
||||||
|
decorate_flat_if_fragment(subgroup_mask_le);
|
||||||
subgroup_mask_gt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGtMaskKHR);
|
subgroup_mask_gt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGtMaskKHR);
|
||||||
|
decorate_flat_if_fragment(subgroup_mask_gt);
|
||||||
subgroup_mask_ge = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGeMaskKHR);
|
subgroup_mask_ge = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGeMaskKHR);
|
||||||
|
decorate_flat_if_fragment(subgroup_mask_ge);
|
||||||
}
|
}
|
||||||
if (info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles ||
|
if (info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles ||
|
||||||
(profile.warp_size_potentially_larger_than_guest &&
|
(profile.warp_size_potentially_larger_than_guest &&
|
||||||
|
@ -1461,6 +1489,7 @@ void EmitContext::DefineInputs(const IR::Program& program) {
|
||||||
}
|
}
|
||||||
if (loads[IR::Attribute::PrimitiveId]) {
|
if (loads[IR::Attribute::PrimitiveId]) {
|
||||||
primitive_id = DefineInput(*this, U32[1], false, spv::BuiltIn::PrimitiveId);
|
primitive_id = DefineInput(*this, U32[1], false, spv::BuiltIn::PrimitiveId);
|
||||||
|
decorate_flat_if_fragment(primitive_id);
|
||||||
}
|
}
|
||||||
if (loads[IR::Attribute::Layer]) {
|
if (loads[IR::Attribute::Layer]) {
|
||||||
AddCapability(spv::Capability::Geometry);
|
AddCapability(spv::Capability::Geometry);
|
||||||
|
|
|
@ -38,6 +38,8 @@ struct TextureDefinition {
|
||||||
Id image_type;
|
Id image_type;
|
||||||
u32 count;
|
u32 count;
|
||||||
bool is_multisample;
|
bool is_multisample;
|
||||||
|
TextureComponentType component_type{TextureComponentType::Float};
|
||||||
|
u32 component_bit_size{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TextureBufferDefinition {
|
struct TextureBufferDefinition {
|
||||||
|
|
|
@ -14,6 +14,10 @@
|
||||||
|
|
||||||
namespace Shader::Optimization {
|
namespace Shader::Optimization {
|
||||||
namespace {
|
namespace {
|
||||||
|
constexpr bool IsOneDimensional(TextureType type) {
|
||||||
|
return type == TextureType::Color1D || type == TextureType::ColorArray1D;
|
||||||
|
}
|
||||||
|
|
||||||
void AddConstantBufferDescriptor(Info& info, u32 index, u32 count) {
|
void AddConstantBufferDescriptor(Info& info, u32 index, u32 count) {
|
||||||
if (count != 1) {
|
if (count != 1) {
|
||||||
throw NotImplementedException("Constant buffer descriptor indexing");
|
throw NotImplementedException("Constant buffer descriptor indexing");
|
||||||
|
@ -548,7 +552,7 @@ void VisitUsages(Info& info, IR::Inst& inst) {
|
||||||
case IR::Opcode::ImageQueryDimensions:
|
case IR::Opcode::ImageQueryDimensions:
|
||||||
case IR::Opcode::ImageGradient: {
|
case IR::Opcode::ImageGradient: {
|
||||||
const TextureType type{inst.Flags<IR::TextureInstInfo>().type};
|
const TextureType type{inst.Flags<IR::TextureInstInfo>().type};
|
||||||
info.uses_sampled_1d |= type == TextureType::Color1D || type == TextureType::ColorArray1D;
|
info.uses_sampled_1d |= IsOneDimensional(type);
|
||||||
info.uses_sparse_residency |=
|
info.uses_sparse_residency |=
|
||||||
inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp) != nullptr;
|
inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp) != nullptr;
|
||||||
break;
|
break;
|
||||||
|
@ -560,7 +564,7 @@ void VisitUsages(Info& info, IR::Inst& inst) {
|
||||||
case IR::Opcode::ImageQueryLod: {
|
case IR::Opcode::ImageQueryLod: {
|
||||||
const auto flags{inst.Flags<IR::TextureInstInfo>()};
|
const auto flags{inst.Flags<IR::TextureInstInfo>()};
|
||||||
const TextureType type{flags.type};
|
const TextureType type{flags.type};
|
||||||
info.uses_sampled_1d |= type == TextureType::Color1D || type == TextureType::ColorArray1D;
|
info.uses_sampled_1d |= IsOneDimensional(type);
|
||||||
info.uses_shadow_lod |= flags.is_depth != 0;
|
info.uses_shadow_lod |= flags.is_depth != 0;
|
||||||
info.uses_sparse_residency |=
|
info.uses_sparse_residency |=
|
||||||
inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp) != nullptr;
|
inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp) != nullptr;
|
||||||
|
@ -569,6 +573,7 @@ void VisitUsages(Info& info, IR::Inst& inst) {
|
||||||
case IR::Opcode::ImageRead: {
|
case IR::Opcode::ImageRead: {
|
||||||
const auto flags{inst.Flags<IR::TextureInstInfo>()};
|
const auto flags{inst.Flags<IR::TextureInstInfo>()};
|
||||||
info.uses_typeless_image_reads |= flags.image_format == ImageFormat::Typeless;
|
info.uses_typeless_image_reads |= flags.image_format == ImageFormat::Typeless;
|
||||||
|
info.uses_image_1d |= IsOneDimensional(flags.type);
|
||||||
info.uses_sparse_residency |=
|
info.uses_sparse_residency |=
|
||||||
inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp) != nullptr;
|
inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp) != nullptr;
|
||||||
break;
|
break;
|
||||||
|
@ -576,6 +581,7 @@ void VisitUsages(Info& info, IR::Inst& inst) {
|
||||||
case IR::Opcode::ImageWrite: {
|
case IR::Opcode::ImageWrite: {
|
||||||
const auto flags{inst.Flags<IR::TextureInstInfo>()};
|
const auto flags{inst.Flags<IR::TextureInstInfo>()};
|
||||||
info.uses_typeless_image_writes |= flags.image_format == ImageFormat::Typeless;
|
info.uses_typeless_image_writes |= flags.image_format == ImageFormat::Typeless;
|
||||||
|
info.uses_image_1d |= IsOneDimensional(flags.type);
|
||||||
info.uses_image_buffers |= flags.type == TextureType::Buffer;
|
info.uses_image_buffers |= flags.type == TextureType::Buffer;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -761,9 +767,12 @@ void VisitUsages(Info& info, IR::Inst& inst) {
|
||||||
case IR::Opcode::ImageAtomicAnd32:
|
case IR::Opcode::ImageAtomicAnd32:
|
||||||
case IR::Opcode::ImageAtomicOr32:
|
case IR::Opcode::ImageAtomicOr32:
|
||||||
case IR::Opcode::ImageAtomicXor32:
|
case IR::Opcode::ImageAtomicXor32:
|
||||||
case IR::Opcode::ImageAtomicExchange32:
|
case IR::Opcode::ImageAtomicExchange32: {
|
||||||
|
const auto flags{inst.Flags<IR::TextureInstInfo>()};
|
||||||
info.uses_atomic_image_u32 = true;
|
info.uses_atomic_image_u32 = true;
|
||||||
|
info.uses_image_1d |= IsOneDimensional(flags.type);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "shader_recompiler/host_translate_info.h"
|
#include "shader_recompiler/host_translate_info.h"
|
||||||
#include "shader_recompiler/ir_opt/passes.h"
|
#include "shader_recompiler/ir_opt/passes.h"
|
||||||
#include "shader_recompiler/shader_info.h"
|
#include "shader_recompiler/shader_info.h"
|
||||||
|
#include "video_core/surface.h"
|
||||||
|
|
||||||
namespace Shader::Optimization {
|
namespace Shader::Optimization {
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -248,11 +249,19 @@ bool IsTextureInstruction(const IR::Inst& inst) {
|
||||||
}
|
}
|
||||||
static inline TexturePixelFormat ReadTexturePixelFormatCached(Environment& env,
|
static inline TexturePixelFormat ReadTexturePixelFormatCached(Environment& env,
|
||||||
const ConstBufferAddr& cbuf) {
|
const ConstBufferAddr& cbuf) {
|
||||||
return env.ReadTexturePixelFormat(GetTextureHandleCached(env, cbuf));
|
const u32 handle = GetTextureHandleCached(env, cbuf);
|
||||||
|
if (handle == 0) {
|
||||||
|
return TexturePixelFormat::A8B8G8R8_UNORM;
|
||||||
|
}
|
||||||
|
return env.ReadTexturePixelFormat(handle);
|
||||||
}
|
}
|
||||||
static inline bool IsTexturePixelFormatIntegerCached(Environment& env,
|
static inline bool IsTexturePixelFormatIntegerCached(Environment& env,
|
||||||
const ConstBufferAddr& cbuf) {
|
const ConstBufferAddr& cbuf) {
|
||||||
return env.IsTexturePixelFormatInteger(GetTextureHandleCached(env, cbuf));
|
const u32 handle = GetTextureHandleCached(env, cbuf);
|
||||||
|
if (handle == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return env.IsTexturePixelFormatInteger(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -524,6 +533,8 @@ public:
|
||||||
const u32 index{Add(texture_descriptors, desc, [&desc](const auto& existing) {
|
const u32 index{Add(texture_descriptors, desc, [&desc](const auto& existing) {
|
||||||
return desc.type == existing.type && desc.is_depth == existing.is_depth &&
|
return desc.type == existing.type && desc.is_depth == existing.is_depth &&
|
||||||
desc.has_secondary == existing.has_secondary &&
|
desc.has_secondary == existing.has_secondary &&
|
||||||
|
desc.component_type == existing.component_type &&
|
||||||
|
desc.component_bit_size == existing.component_bit_size &&
|
||||||
desc.cbuf_index == existing.cbuf_index &&
|
desc.cbuf_index == existing.cbuf_index &&
|
||||||
desc.cbuf_offset == existing.cbuf_offset &&
|
desc.cbuf_offset == existing.cbuf_offset &&
|
||||||
desc.shift_left == existing.shift_left &&
|
desc.shift_left == existing.shift_left &&
|
||||||
|
@ -598,6 +609,35 @@ bool IsPixelFormatSNorm(TexturePixelFormat pixel_format) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextureComponentType PixelFormatComponentType(TexturePixelFormat pixel_format, bool is_integer) {
|
||||||
|
if (!is_integer) {
|
||||||
|
return TextureComponentType::Float;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (pixel_format) {
|
||||||
|
case TexturePixelFormat::A8B8G8R8_SINT:
|
||||||
|
case TexturePixelFormat::R8_SINT:
|
||||||
|
case TexturePixelFormat::R16G16B16A16_SINT:
|
||||||
|
case TexturePixelFormat::R32G32B32A32_SINT:
|
||||||
|
case TexturePixelFormat::R32G32_SINT:
|
||||||
|
case TexturePixelFormat::R16_SINT:
|
||||||
|
case TexturePixelFormat::R16G16_SINT:
|
||||||
|
case TexturePixelFormat::R8G8_SINT:
|
||||||
|
case TexturePixelFormat::R32_SINT:
|
||||||
|
return TextureComponentType::Sint;
|
||||||
|
default:
|
||||||
|
return TextureComponentType::Uint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 PixelFormatIntegerComponentBits(TexturePixelFormat pixel_format, bool is_integer) {
|
||||||
|
if (!is_integer) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return static_cast<u8>(VideoCore::Surface::PixelComponentSizeBitsInteger(
|
||||||
|
static_cast<VideoCore::Surface::PixelFormat>(pixel_format)));
|
||||||
|
}
|
||||||
|
|
||||||
void PatchTexelFetch(IR::Block& block, IR::Inst& inst, TexturePixelFormat pixel_format) {
|
void PatchTexelFetch(IR::Block& block, IR::Inst& inst, TexturePixelFormat pixel_format) {
|
||||||
const auto it{IR::Block::InstructionList::s_iterator_to(inst)};
|
const auto it{IR::Block::InstructionList::s_iterator_to(inst)};
|
||||||
IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
|
IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
|
||||||
|
@ -698,6 +738,8 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
const TexturePixelFormat pixel_format{ReadTexturePixelFormatCached(env, cbuf)};
|
||||||
|
const bool is_integer{IsTexturePixelFormatIntegerCached(env, cbuf)};
|
||||||
u32 index;
|
u32 index;
|
||||||
switch (inst->GetOpcode()) {
|
switch (inst->GetOpcode()) {
|
||||||
case IR::Opcode::ImageRead:
|
case IR::Opcode::ImageRead:
|
||||||
|
@ -718,7 +760,6 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
|
||||||
}
|
}
|
||||||
const bool is_written{inst->GetOpcode() != IR::Opcode::ImageRead};
|
const bool is_written{inst->GetOpcode() != IR::Opcode::ImageRead};
|
||||||
const bool is_read{inst->GetOpcode() != IR::Opcode::ImageWrite};
|
const bool is_read{inst->GetOpcode() != IR::Opcode::ImageWrite};
|
||||||
const bool is_integer{IsTexturePixelFormatIntegerCached(env, cbuf)};
|
|
||||||
if (flags.type == TextureType::Buffer) {
|
if (flags.type == TextureType::Buffer) {
|
||||||
index = descriptors.Add(ImageBufferDescriptor{
|
index = descriptors.Add(ImageBufferDescriptor{
|
||||||
.format = flags.image_format,
|
.format = flags.image_format,
|
||||||
|
@ -764,6 +805,8 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
|
||||||
.is_depth = flags.is_depth != 0,
|
.is_depth = flags.is_depth != 0,
|
||||||
.is_multisample = is_multisample,
|
.is_multisample = is_multisample,
|
||||||
.has_secondary = cbuf.has_secondary,
|
.has_secondary = cbuf.has_secondary,
|
||||||
|
.component_type = PixelFormatComponentType(pixel_format, is_integer),
|
||||||
|
.component_bit_size = PixelFormatIntegerComponentBits(pixel_format, is_integer),
|
||||||
.cbuf_index = cbuf.index,
|
.cbuf_index = cbuf.index,
|
||||||
.cbuf_offset = cbuf.offset,
|
.cbuf_offset = cbuf.offset,
|
||||||
.shift_left = cbuf.shift_left,
|
.shift_left = cbuf.shift_left,
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -35,6 +38,12 @@ enum class TextureType : u32 {
|
||||||
};
|
};
|
||||||
constexpr u32 NUM_TEXTURE_TYPES = 9;
|
constexpr u32 NUM_TEXTURE_TYPES = 9;
|
||||||
|
|
||||||
|
enum class TextureComponentType : u32 {
|
||||||
|
Float,
|
||||||
|
Sint,
|
||||||
|
Uint,
|
||||||
|
};
|
||||||
|
|
||||||
enum class TexturePixelFormat {
|
enum class TexturePixelFormat {
|
||||||
A8B8G8R8_UNORM,
|
A8B8G8R8_UNORM,
|
||||||
A8B8G8R8_SNORM,
|
A8B8G8R8_SNORM,
|
||||||
|
@ -174,7 +183,9 @@ struct StorageBufferDescriptor {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TextureBufferDescriptor {
|
struct TextureBufferDescriptor {
|
||||||
bool has_secondary;
|
TextureComponentType component_type{TextureComponentType::Float};
|
||||||
|
u8 component_bit_size{};
|
||||||
|
bool has_secondary{};
|
||||||
u32 cbuf_index;
|
u32 cbuf_index;
|
||||||
u32 cbuf_offset;
|
u32 cbuf_offset;
|
||||||
u32 shift_left;
|
u32 shift_left;
|
||||||
|
@ -207,6 +218,8 @@ struct TextureDescriptor {
|
||||||
bool is_depth;
|
bool is_depth;
|
||||||
bool is_multisample;
|
bool is_multisample;
|
||||||
bool has_secondary;
|
bool has_secondary;
|
||||||
|
TextureComponentType component_type{TextureComponentType::Float};
|
||||||
|
u8 component_bit_size{};
|
||||||
u32 cbuf_index;
|
u32 cbuf_index;
|
||||||
u32 cbuf_offset;
|
u32 cbuf_offset;
|
||||||
u32 shift_left;
|
u32 shift_left;
|
||||||
|
|
|
@ -191,10 +191,19 @@ inline void PushImageDescriptors(TextureCache& texture_cache,
|
||||||
ImageView& image_view{texture_cache.GetImageView(image_view_id)};
|
ImageView& image_view{texture_cache.GetImageView(image_view_id)};
|
||||||
const VkImageView vk_image_view{image_view.Handle(desc.type)};
|
const VkImageView vk_image_view{image_view.Handle(desc.type)};
|
||||||
const Sampler& sampler{texture_cache.GetSampler(sampler_id)};
|
const Sampler& sampler{texture_cache.GetSampler(sampler_id)};
|
||||||
const bool use_fallback_sampler{sampler.HasAddedAnisotropy() &&
|
const bool needs_linear_fallback = sampler.RequiresLinearFiltering() &&
|
||||||
!image_view.SupportsAnisotropy()};
|
!image_view.SupportsLinearFiltering();
|
||||||
const VkSampler vk_sampler{use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy()
|
const bool needs_aniso_fallback = sampler.HasAddedAnisotropy() &&
|
||||||
: sampler.Handle()};
|
!image_view.SupportsAnisotropy();
|
||||||
|
if (!image_view.SupportsLinearFiltering()) {
|
||||||
|
ASSERT_MSG(!sampler.RequiresLinearFiltering() || needs_linear_fallback,
|
||||||
|
"Linear filtering sampler bound to unsupported image view");
|
||||||
|
}
|
||||||
|
// Prefer degrading to nearest sampling when the view lacks linear support.
|
||||||
|
const VkSampler vk_sampler = needs_linear_fallback
|
||||||
|
? sampler.HandleWithoutLinearFiltering()
|
||||||
|
: (needs_aniso_fallback ? sampler.HandleWithDefaultAnisotropy()
|
||||||
|
: sampler.Handle());
|
||||||
guest_descriptor_queue.AddSampledImage(vk_image_view, vk_sampler);
|
guest_descriptor_queue.AddSampledImage(vk_image_view, vk_sampler);
|
||||||
rescaling.PushTexture(texture_cache.IsRescaling(image_view));
|
rescaling.PushTexture(texture_cache.IsRescaling(image_view));
|
||||||
}
|
}
|
||||||
|
|
|
@ -2061,6 +2061,8 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
|
||||||
},
|
},
|
||||||
.subresourceRange = MakeSubresourceRange(aspect_mask, info.range),
|
.subresourceRange = MakeSubresourceRange(aspect_mask, info.range),
|
||||||
};
|
};
|
||||||
|
supports_linear_filtering = device->IsFormatSupported(
|
||||||
|
create_info.format, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT, FormatType::Optimal);
|
||||||
const auto create = [&](TextureType tex_type, std::optional<u32> num_layers) {
|
const auto create = [&](TextureType tex_type, std::optional<u32> num_layers) {
|
||||||
VkImageViewCreateInfo ci{create_info};
|
VkImageViewCreateInfo ci{create_info};
|
||||||
ci.viewType = ImageViewType(tex_type);
|
ci.viewType = ImageViewType(tex_type);
|
||||||
|
@ -2111,10 +2113,13 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
|
||||||
ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageInfo& info,
|
ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageInfo& info,
|
||||||
const VideoCommon::ImageViewInfo& view_info, GPUVAddr gpu_addr_)
|
const VideoCommon::ImageViewInfo& view_info, GPUVAddr gpu_addr_)
|
||||||
: VideoCommon::ImageViewBase{info, view_info, gpu_addr_},
|
: VideoCommon::ImageViewBase{info, view_info, gpu_addr_},
|
||||||
buffer_size{VideoCommon::CalculateGuestSizeInBytes(info)} {}
|
buffer_size{VideoCommon::CalculateGuestSizeInBytes(info)} {
|
||||||
|
supports_linear_filtering = true;
|
||||||
|
}
|
||||||
|
|
||||||
ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::NullImageViewParams& params)
|
ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::NullImageViewParams& params)
|
||||||
: VideoCommon::ImageViewBase{params}, device{&runtime.device} {
|
: VideoCommon::ImageViewBase{params}, device{&runtime.device} {
|
||||||
|
supports_linear_filtering = true;
|
||||||
if (device->HasNullDescriptor()) {
|
if (device->HasNullDescriptor()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -2175,6 +2180,7 @@ VkImageView ImageView::StorageView(Shader::TextureType texture_type,
|
||||||
if (image_format == Shader::ImageFormat::Typeless) {
|
if (image_format == Shader::ImageFormat::Typeless) {
|
||||||
return Handle(texture_type);
|
return Handle(texture_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool is_signed{image_format == Shader::ImageFormat::R8_SINT ||
|
const bool is_signed{image_format == Shader::ImageFormat::R8_SINT ||
|
||||||
image_format == Shader::ImageFormat::R16_SINT};
|
image_format == Shader::ImageFormat::R16_SINT};
|
||||||
if (!storage_views) {
|
if (!storage_views) {
|
||||||
|
@ -2244,15 +2250,23 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t
|
||||||
}
|
}
|
||||||
// Some games have samplers with garbage. Sanitize them here.
|
// Some games have samplers with garbage. Sanitize them here.
|
||||||
const f32 max_anisotropy = std::clamp(tsc.MaxAnisotropy(), 1.0f, 16.0f);
|
const f32 max_anisotropy = std::clamp(tsc.MaxAnisotropy(), 1.0f, 16.0f);
|
||||||
|
const VkFilter mag_filter = MaxwellToVK::Sampler::Filter(tsc.mag_filter);
|
||||||
|
const VkFilter min_filter = MaxwellToVK::Sampler::Filter(tsc.min_filter);
|
||||||
|
const VkSamplerMipmapMode mipmap_mode = MaxwellToVK::Sampler::MipmapMode(tsc.mipmap_filter);
|
||||||
|
const f32 min_lod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.0f : tsc.MinLod();
|
||||||
|
const f32 max_lod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.25f : tsc.MaxLod();
|
||||||
|
requires_linear_filtering = mag_filter == VK_FILTER_LINEAR || min_filter == VK_FILTER_LINEAR ||
|
||||||
|
mipmap_mode == VK_SAMPLER_MIPMAP_MODE_LINEAR || max_anisotropy > 1.0f;
|
||||||
|
|
||||||
const auto create_sampler = [&](const f32 anisotropy) {
|
const auto create_sampler = [&](VkFilter mag, VkFilter min, VkSamplerMipmapMode mip,
|
||||||
|
f32 anisotropy) {
|
||||||
return device.GetLogical().CreateSampler(VkSamplerCreateInfo{
|
return device.GetLogical().CreateSampler(VkSamplerCreateInfo{
|
||||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||||
.pNext = pnext,
|
.pNext = pnext,
|
||||||
.flags = 0,
|
.flags = 0,
|
||||||
.magFilter = MaxwellToVK::Sampler::Filter(tsc.mag_filter),
|
.magFilter = mag,
|
||||||
.minFilter = MaxwellToVK::Sampler::Filter(tsc.min_filter),
|
.minFilter = min,
|
||||||
.mipmapMode = MaxwellToVK::Sampler::MipmapMode(tsc.mipmap_filter),
|
.mipmapMode = mip,
|
||||||
.addressModeU = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_u, tsc.mag_filter),
|
.addressModeU = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_u, tsc.mag_filter),
|
||||||
.addressModeV = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_v, tsc.mag_filter),
|
.addressModeV = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_v, tsc.mag_filter),
|
||||||
.addressModeW = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_p, tsc.mag_filter),
|
.addressModeW = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_p, tsc.mag_filter),
|
||||||
|
@ -2261,19 +2275,25 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t
|
||||||
.maxAnisotropy = anisotropy,
|
.maxAnisotropy = anisotropy,
|
||||||
.compareEnable = tsc.depth_compare_enabled,
|
.compareEnable = tsc.depth_compare_enabled,
|
||||||
.compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func),
|
.compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func),
|
||||||
.minLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.0f : tsc.MinLod(),
|
.minLod = min_lod,
|
||||||
.maxLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.25f : tsc.MaxLod(),
|
.maxLod = max_lod,
|
||||||
.borderColor =
|
.borderColor =
|
||||||
arbitrary_borders ? VK_BORDER_COLOR_FLOAT_CUSTOM_EXT : ConvertBorderColor(color),
|
arbitrary_borders ? VK_BORDER_COLOR_FLOAT_CUSTOM_EXT : ConvertBorderColor(color),
|
||||||
.unnormalizedCoordinates = VK_FALSE,
|
.unnormalizedCoordinates = VK_FALSE,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
sampler = create_sampler(max_anisotropy);
|
sampler = create_sampler(mag_filter, min_filter, mipmap_mode, max_anisotropy);
|
||||||
|
|
||||||
const f32 max_anisotropy_default = static_cast<f32>(1U << tsc.max_anisotropy);
|
const f32 max_anisotropy_default = static_cast<f32>(1U << tsc.max_anisotropy);
|
||||||
if (max_anisotropy > max_anisotropy_default) {
|
if (max_anisotropy > max_anisotropy_default) {
|
||||||
sampler_default_anisotropy = create_sampler(max_anisotropy_default);
|
sampler_default_anisotropy = create_sampler(mag_filter, min_filter, mipmap_mode,
|
||||||
|
max_anisotropy_default);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requires_linear_filtering) {
|
||||||
|
sampler_no_linear = create_sampler(VK_FILTER_NEAREST, VK_FILTER_NEAREST,
|
||||||
|
VK_SAMPLER_MIPMAP_MODE_NEAREST, 1.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -238,6 +238,10 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] bool IsRescaled() const noexcept;
|
[[nodiscard]] bool IsRescaled() const noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]] bool SupportsLinearFiltering() const noexcept {
|
||||||
|
return supports_linear_filtering;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] VkImageView Handle(Shader::TextureType texture_type) const noexcept {
|
[[nodiscard]] VkImageView Handle(Shader::TextureType texture_type) const noexcept {
|
||||||
return *image_views[static_cast<size_t>(texture_type)];
|
return *image_views[static_cast<size_t>(texture_type)];
|
||||||
}
|
}
|
||||||
|
@ -278,6 +282,7 @@ private:
|
||||||
vk::ImageView depth_view;
|
vk::ImageView depth_view;
|
||||||
vk::ImageView stencil_view;
|
vk::ImageView stencil_view;
|
||||||
vk::ImageView color_view;
|
vk::ImageView color_view;
|
||||||
|
bool supports_linear_filtering{};
|
||||||
vk::Image null_image;
|
vk::Image null_image;
|
||||||
VkImage image_handle = VK_NULL_HANDLE;
|
VkImage image_handle = VK_NULL_HANDLE;
|
||||||
VkImageView render_target = VK_NULL_HANDLE;
|
VkImageView render_target = VK_NULL_HANDLE;
|
||||||
|
@ -296,16 +301,26 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] VkSampler HandleWithDefaultAnisotropy() const noexcept {
|
[[nodiscard]] VkSampler HandleWithDefaultAnisotropy() const noexcept {
|
||||||
return *sampler_default_anisotropy;
|
return sampler_default_anisotropy ? *sampler_default_anisotropy : *sampler;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] VkSampler HandleWithoutLinearFiltering() const noexcept {
|
||||||
|
return sampler_no_linear ? *sampler_no_linear : *sampler;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool HasAddedAnisotropy() const noexcept {
|
[[nodiscard]] bool HasAddedAnisotropy() const noexcept {
|
||||||
return static_cast<bool>(sampler_default_anisotropy);
|
return static_cast<bool>(sampler_default_anisotropy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool RequiresLinearFiltering() const noexcept {
|
||||||
|
return requires_linear_filtering;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
vk::Sampler sampler;
|
vk::Sampler sampler;
|
||||||
vk::Sampler sampler_default_anisotropy;
|
vk::Sampler sampler_default_anisotropy;
|
||||||
|
vk::Sampler sampler_no_linear;
|
||||||
|
bool requires_linear_filtering{};
|
||||||
};
|
};
|
||||||
|
|
||||||
class Framebuffer {
|
class Framebuffer {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue