Browse Source

Revert "Controlled SPV features on QCOM"

This reverts commit 907b041ec6.
eds-true-adreno-fixes
Caio Oliveira 4 weeks ago
parent
commit
4fb7aea7b4
No known key found for this signature in database GPG Key ID: AAAE6C7FD4186B0C
  1. 264
      src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
  2. 208
      src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
  3. 4
      src/shader_recompiler/backend/spirv/spirv_emit_context.h
  4. 7
      src/shader_recompiler/profile.h
  5. 12
      src/video_core/renderer_opengl/gl_shader_cache.cpp
  6. 11
      src/video_core/renderer_vulkan/vk_pipeline_cache.cpp

264
src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp

@ -41,203 +41,19 @@ struct OutAttr {
Id pointer{};
Id type{};
const GenericElementInfo* generic_info{};
u32 generic_element{};
bool is_position{};
u32 position_component{};
};
bool NeedsVectorAccessWorkaround(const EmitContext& ctx) {
return ctx.profile.has_broken_spirv_vector_access_chain;
}
bool StageHasPerVertexInputs(const EmitContext& ctx) {
switch (ctx.stage) {
case Stage::TessellationControl:
case Stage::TessellationEval:
case Stage::Geometry:
return true;
default:
return false;
}
}
bool StageHasPerInvocationOutputs(const EmitContext& ctx) {
return ctx.stage == Stage::TessellationControl;
}
Id ApplyGenericConversion(EmitContext& ctx, const InputGenericInfo& generic, Id value) {
switch (generic.load_op) {
case InputGenericLoadOp::Bitcast:
return ctx.OpBitcast(ctx.F32[1], value);
case InputGenericLoadOp::SToF:
return ctx.OpConvertSToF(ctx.F32[1], value);
case InputGenericLoadOp::UToF:
return ctx.OpConvertUToF(ctx.F32[1], value);
default:
return value;
}
}
Id InputGenericVectorPointer(EmitContext& ctx, const InputGenericInfo& generic, Id vertex,
bool use_vertex) {
if (use_vertex) {
return ctx.OpAccessChain(generic.composite_pointer_type, generic.id, vertex);
}
return generic.id;
}
Id LoadGenericComponentConst(EmitContext& ctx, const InputGenericInfo& generic, Id vertex,
u32 component, bool use_vertex) {
if (!NeedsVectorAccessWorkaround(ctx)) {
const Id pointer{
AttrPointer(ctx, generic.pointer_type, vertex, generic.id, ctx.Const(component))};
const Id value{ctx.OpLoad(generic.component_type, pointer)};
return ApplyGenericConversion(ctx, generic, value);
}
const Id vector_pointer{InputGenericVectorPointer(ctx, generic, vertex, use_vertex)};
const Id vector_value{ctx.OpLoad(generic.composite_type, vector_pointer)};
const Id component_value{
ctx.OpCompositeExtract(generic.component_type, vector_value, component)};
return ApplyGenericConversion(ctx, generic, component_value);
}
Id OutputGenericVectorPointer(EmitContext& ctx, const GenericElementInfo& info,
bool use_invocation) {
if (use_invocation) {
const Id invocation_id{ctx.OpLoad(ctx.U32[1], ctx.invocation_id)};
return ctx.OpAccessChain(info.composite_pointer_type, info.id, invocation_id);
}
return info.id;
}
void StoreGenericComponentConst(EmitContext& ctx, const GenericElementInfo& info,
u32 attribute_element, Id value, bool use_invocation) {
const u32 local_component{attribute_element - info.first_element};
if (!NeedsVectorAccessWorkaround(ctx) || info.num_components <= 1) {
const Id pointer{
OutputAccessChain(ctx, ctx.output_f32, info.id, ctx.Const(local_component))};
ctx.OpStore(pointer, value);
return;
}
const Id vector_pointer{OutputGenericVectorPointer(ctx, info, use_invocation)};
const Id vector_value{ctx.OpLoad(info.composite_type, vector_pointer)};
const Id updated{
ctx.OpCompositeInsert(info.composite_type, value, vector_value, local_component)};
ctx.OpStore(vector_pointer, updated);
}
Id InputPositionPointer(EmitContext& ctx, Id vertex, bool use_vertex) {
const Id vector_pointer_type{ctx.TypePointer(spv::StorageClass::Input, ctx.F32[4])};
if (ctx.need_input_position_indirect) {
if (use_vertex) {
return ctx.OpAccessChain(vector_pointer_type, ctx.input_position, vertex,
ctx.u32_zero_value);
}
return ctx.OpAccessChain(vector_pointer_type, ctx.input_position, ctx.u32_zero_value);
}
if (use_vertex) {
return ctx.OpAccessChain(vector_pointer_type, ctx.input_position, vertex);
}
return ctx.input_position;
}
Id LoadPositionComponent(EmitContext& ctx, Id vertex, u32 component, bool use_vertex) {
if (!NeedsVectorAccessWorkaround(ctx)) {
return ctx.OpLoad(
ctx.F32[1],
ctx.need_input_position_indirect
? AttrPointer(ctx, ctx.input_f32, vertex, ctx.input_position, ctx.u32_zero_value,
ctx.Const(component))
: AttrPointer(ctx, ctx.input_f32, vertex, ctx.input_position,
ctx.Const(component)));
}
const Id pointer{InputPositionPointer(ctx, vertex, use_vertex)};
const Id vector_value{ctx.OpLoad(ctx.F32[4], pointer)};
return ctx.OpCompositeExtract(ctx.F32[1], vector_value, component);
}
void StorePositionComponent(EmitContext& ctx, u32 component, Id value) {
if (!NeedsVectorAccessWorkaround(ctx)) {
const Id pointer{OutputAccessChain(ctx, ctx.output_f32, ctx.output_position,
ctx.Const(component))};
ctx.OpStore(pointer, value);
return;
}
const bool use_invocation{StageHasPerInvocationOutputs(ctx)};
const Id pointer_type{ctx.TypePointer(spv::StorageClass::Output, ctx.F32[4])};
const Id target_pointer{use_invocation
? ctx.OpAccessChain(pointer_type, ctx.output_position,
ctx.OpLoad(ctx.U32[1], ctx.invocation_id))
: ctx.output_position};
const Id vector_value{ctx.OpLoad(ctx.F32[4], target_pointer)};
const Id updated{ctx.OpCompositeInsert(ctx.F32[4], value, vector_value, component)};
ctx.OpStore(target_pointer, updated);
}
Id LoadBuiltinVectorComponent(EmitContext& ctx, Id pointer, Id pointer_type, Id vector_type,
u32 component) {
if (!NeedsVectorAccessWorkaround(ctx)) {
const Id comp_id{ctx.Const(component)};
const Id elem_ptr{ctx.OpAccessChain(pointer_type, pointer, comp_id)};
return ctx.OpLoad(ctx.F32[1], elem_ptr);
}
const Id vector_value{ctx.OpLoad(vector_type, pointer)};
return ctx.OpCompositeExtract(ctx.F32[1], vector_value, component);
}
Id LoadPatchComponent(EmitContext& ctx, Id patch, u32 component) {
if (!NeedsVectorAccessWorkaround(ctx)) {
const Id pointer{ctx.OpAccessChain(ctx.stage == Stage::TessellationControl ? ctx.output_f32
: ctx.input_f32,
patch, ctx.Const(component))};
return ctx.OpLoad(ctx.F32[1], pointer);
}
const Id vector_value{ctx.OpLoad(ctx.F32[4], patch)};
return ctx.OpCompositeExtract(ctx.F32[1], vector_value, component);
}
void StorePatchComponent(EmitContext& ctx, Id patch, u32 component, Id value) {
if (!NeedsVectorAccessWorkaround(ctx)) {
const Id pointer{ctx.OpAccessChain(ctx.output_f32, patch, ctx.Const(component))};
ctx.OpStore(pointer, value);
return;
}
const Id vector_value{ctx.OpLoad(ctx.F32[4], patch)};
const Id updated{ctx.OpCompositeInsert(ctx.F32[4], value, vector_value, component)};
ctx.OpStore(patch, updated);
}
void StoreFragColorComponent(EmitContext& ctx, u32 index, u32 component, Id value) {
if (!NeedsVectorAccessWorkaround(ctx)) {
const Id component_id{ctx.Const(component)};
const Id pointer{
ctx.OpAccessChain(ctx.output_f32, ctx.frag_color.at(index), component_id)};
ctx.OpStore(pointer, value);
return;
}
const Id vector_value{ctx.OpLoad(ctx.F32[4], ctx.frag_color.at(index))};
const Id updated{ctx.OpCompositeInsert(ctx.F32[4], value, vector_value, component)};
ctx.OpStore(ctx.frag_color.at(index), updated);
}
std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
if (IR::IsGeneric(attr)) {
const u32 index{IR::GenericAttributeIndex(attr)};
const u32 element{IR::GenericAttributeElement(attr)};
const GenericElementInfo& info{ctx.output_generics.at(index).at(element)};
if (info.num_components == 1) {
OutAttr out{info.id};
out.generic_info = &info;
out.generic_element = element;
return out;
return info.id;
} else {
const u32 index_element{element - info.first_element};
const Id index_id{ctx.Const(index_element)};
OutAttr out{OutputAccessChain(ctx, ctx.output_f32, info.id, index_id)};
out.generic_info = &info;
out.generic_element = element;
return out;
return OutputAccessChain(ctx, ctx.output_f32, info.id, index_id);
}
}
@ -250,10 +66,7 @@ std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
case IR::Attribute::PositionW: {
const u32 element{static_cast<u32>(attr) % 4};
const Id element_id{ctx.Const(element)};
OutAttr out{OutputAccessChain(ctx, ctx.output_f32, ctx.output_position, element_id)};
out.is_position = true;
out.position_component = element;
return out;
return OutputAccessChain(ctx, ctx.output_f32, ctx.output_position, element_id);
}
case IR::Attribute::ClipDistance0:
case IR::Attribute::ClipDistance1:
@ -503,8 +316,21 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
// Attribute is disabled or varying component is not written
return ctx.Const(element == 3 ? 1.0f : 0.0f);
}
const bool use_vertex{StageHasPerVertexInputs(ctx)};
return LoadGenericComponentConst(ctx, generic, vertex, element, use_vertex);
const Id pointer{
AttrPointer(ctx, generic.pointer_type, vertex, generic.id, ctx.Const(element))};
const Id value{ctx.OpLoad(generic.component_type, pointer)};
return [&ctx, generic, value]() {
switch (generic.load_op) {
case InputGenericLoadOp::Bitcast:
return ctx.OpBitcast(ctx.F32[1], value);
case InputGenericLoadOp::SToF:
return ctx.OpConvertSToF(ctx.F32[1], value);
case InputGenericLoadOp::UToF:
return ctx.OpConvertUToF(ctx.F32[1], value);
default:
return value;
};
}();
}
switch (attr) {
case IR::Attribute::PrimitiveId:
@ -515,7 +341,12 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
case IR::Attribute::PositionY:
case IR::Attribute::PositionZ:
case IR::Attribute::PositionW:
return LoadPositionComponent(ctx, vertex, element, StageHasPerVertexInputs(ctx));
return ctx.OpLoad(
ctx.F32[1],
ctx.need_input_position_indirect
? AttrPointer(ctx, ctx.input_f32, vertex, ctx.input_position, ctx.u32_zero_value,
ctx.Const(element))
: AttrPointer(ctx, ctx.input_f32, vertex, ctx.input_position, ctx.Const(element)));
case IR::Attribute::InstanceId:
if (ctx.profile.support_vertex_instance_id) {
return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.instance_id));
@ -541,13 +372,17 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
ctx.OpBitcast(ctx.F32[1], ctx.Const((std::numeric_limits<u32>::max)())),
ctx.f32_zero_value);
case IR::Attribute::PointSpriteS:
return LoadBuiltinVectorComponent(ctx, ctx.point_coord, ctx.input_f32, ctx.F32[2], 0);
return ctx.OpLoad(ctx.F32[1],
ctx.OpAccessChain(ctx.input_f32, ctx.point_coord, ctx.u32_zero_value));
case IR::Attribute::PointSpriteT:
return LoadBuiltinVectorComponent(ctx, ctx.point_coord, ctx.input_f32, ctx.F32[2], 1);
return ctx.OpLoad(ctx.F32[1],
ctx.OpAccessChain(ctx.input_f32, ctx.point_coord, ctx.Const(1U)));
case IR::Attribute::TessellationEvaluationPointU:
return LoadBuiltinVectorComponent(ctx, ctx.tess_coord, ctx.input_f32, ctx.F32[3], 0);
return ctx.OpLoad(ctx.F32[1],
ctx.OpAccessChain(ctx.input_f32, ctx.tess_coord, ctx.u32_zero_value));
case IR::Attribute::TessellationEvaluationPointV:
return LoadBuiltinVectorComponent(ctx, ctx.tess_coord, ctx.input_f32, ctx.F32[3], 1);
return ctx.OpLoad(ctx.F32[1],
ctx.OpAccessChain(ctx.input_f32, ctx.tess_coord, ctx.Const(1U)));
default:
throw NotImplementedException("Read attribute {}", attr);
}
@ -598,20 +433,6 @@ void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, [[maybe_un
const u32 idx = (u32) attr - (u32) cd0;
clip_distance_written.set(idx);
}
if (NeedsVectorAccessWorkaround(ctx)) {
const bool use_invocation{StageHasPerInvocationOutputs(ctx)};
if (output->generic_info) {
StoreGenericComponentConst(ctx, *output->generic_info, output->generic_element, value,
use_invocation);
return;
}
if (output->is_position) {
StorePositionComponent(ctx, output->position_component, value);
return;
}
}
ctx.OpStore(output->pointer, value);
}
@ -635,20 +456,18 @@ Id EmitGetPatch(EmitContext& ctx, IR::Patch patch) {
throw NotImplementedException("Non-generic patch load");
}
const u32 index{IR::GenericPatchIndex(patch)};
const u32 element{IR::GenericPatchElement(patch)};
return LoadPatchComponent(ctx, ctx.patches.at(index), element);
const Id element{ctx.Const(IR::GenericPatchElement(patch))};
const Id type{ctx.stage == Stage::TessellationControl ? ctx.output_f32 : ctx.input_f32};
const Id pointer{ctx.OpAccessChain(type, ctx.patches.at(index), element)};
return ctx.OpLoad(ctx.F32[1], pointer);
}
void EmitSetPatch(EmitContext& ctx, IR::Patch patch, Id value) {
const Id pointer{[&] {
if (IR::IsGeneric(patch)) {
const u32 index{IR::GenericPatchIndex(patch)};
const u32 element{IR::GenericPatchElement(patch)};
if (NeedsVectorAccessWorkaround(ctx)) {
StorePatchComponent(ctx, ctx.patches.at(index), element, value);
return Id{};
}
return ctx.OpAccessChain(ctx.output_f32, ctx.patches.at(index), ctx.Const(element));
const Id element{ctx.Const(IR::GenericPatchElement(patch))};
return ctx.OpAccessChain(ctx.output_f32, ctx.patches.at(index), element);
}
switch (patch) {
case IR::Patch::TessellationLodLeft:
@ -668,18 +487,11 @@ void EmitSetPatch(EmitContext& ctx, IR::Patch patch, Id value) {
throw NotImplementedException("Patch {}", patch);
}
}()};
if (!Sirit::ValidId(pointer)) {
return;
}
ctx.OpStore(pointer, value);
}
void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, Id value) {
const Id component_id{ctx.Const(component)};
if (NeedsVectorAccessWorkaround(ctx)) {
StoreFragColorComponent(ctx, index, component, value);
return;
}
const Id pointer{ctx.OpAccessChain(ctx.output_f32, ctx.frag_color.at(index), component_id)};
ctx.OpStore(pointer, value);
}

208
src/shader_recompiler/backend/spirv/spirv_emit_context.cpp

@ -13,26 +13,23 @@
#include <fmt/ranges.h>
const Id vec_type{ctx.F32[4]};
#include "common/common_types.h"
#include "common/div_ceil.h"
#include "shader_recompiler/backend/spirv/emit_spirv.h"
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
vec_type,
ctx.TypePointer(spv::StorageClass::Input, vec_type),
namespace Shader::Backend::SPIRV {
namespace {
enum class Operation {
Increment,
const Id vec_type{ctx.U32[4]};
Decrement,
FPAdd,
FPMin,
vec_type,
ctx.TypePointer(spv::StorageClass::Input, vec_type),
FPMax,
};
Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) {
const spv::ImageFormat format{spv::ImageFormat::Unknown};
const Id scalar_type{ctx.TypeInt(32, true)};
const Id vec_type{ctx.TypeVector(scalar_type, 4)};
const Id type{ctx.F32[1]};
const bool depth{desc.is_depth};
const bool ms{desc.is_multisample};
@ -42,39 +39,35 @@ Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) {
case TextureType::ColorArray1D:
return ctx.TypeImage(type, spv::Dim::Dim1D, depth, true, false, 1, format);
case TextureType::Color2D:
const Id vec_type{ctx.F32[4]};
case TextureType::Color2DRect:
return ctx.TypeImage(type, spv::Dim::Dim2D, depth, false, ms, 1, format);
case TextureType::ColorArray2D:
vec_type,
ctx.TypePointer(spv::StorageClass::Input, vec_type),
return ctx.TypeImage(type, spv::Dim::Dim2D, depth, true, ms, 1, format);
case TextureType::Color3D:
return ctx.TypeImage(type, spv::Dim::Dim3D, depth, false, false, 1, format);
case TextureType::ColorCube:
const Id scalar_type{ctx.TypeInt(32, true)};
const Id vec_type{ctx.TypeVector(scalar_type, 4)};
return InputGenericInfo{id,
ctx.input_s32,
scalar_type,
vec_type,
ctx.TypePointer(spv::StorageClass::Input, vec_type),
InputGenericLoadOp::SToF};
return ctx.TypeImage(type, spv::Dim::Cube, depth, false, false, 1, format);
case TextureType::ColorArrayCube:
return ctx.TypeImage(type, spv::Dim::Cube, depth, true, false, 1, format);
case TextureType::Buffer:
break;
}
throw InvalidArgument("Invalid texture type {}", desc.type);
}
const Id vec_type{ctx.F32[4]};
spv::ImageFormat GetImageFormat(ImageFormat format) {
switch (format) {
case ImageFormat::Typeless:
vec_type,
ctx.TypePointer(spv::StorageClass::Input, vec_type),
return spv::ImageFormat::Unknown;
case ImageFormat::R8_UINT:
return spv::ImageFormat::R8ui;
case ImageFormat::R8_SINT:
const Id vec_type{ctx.U32[4]};
return InputGenericInfo{id,
ctx.input_u32,
ctx.U32[1],
vec_type,
ctx.TypePointer(spv::StorageClass::Input, vec_type),
InputGenericLoadOp::UToF};
return spv::ImageFormat::R8i;
case ImageFormat::R16_UINT:
return spv::ImageFormat::R16ui;
case ImageFormat::R16_SINT:
return spv::ImageFormat::R16i;
case ImageFormat::R32_UINT:
return spv::ImageFormat::R32ui;
case ImageFormat::R32G32_UINT:
return spv::ImageFormat::Rg32ui;
@ -192,14 +185,10 @@ void DefineGenericOutput(EmitContext& ctx, size_t index, std::optional<u32> invo
} else {
ctx.Name(id, fmt::format("out_attr{}", index));
}
const Id vector_type{ctx.F32[num_components]};
const GenericElementInfo info{
.id = id,
.first_element = element,
.num_components = num_components,
.composite_type = vector_type,
.composite_pointer_type =
ctx.TypePointer(spv::StorageClass::Output, vector_type),
};
std::fill_n(ctx.output_generics[index].begin() + element, num_components, info);
element += num_components;
@ -228,58 +217,21 @@ Id GetAttributeType(EmitContext& ctx, AttributeType type) {
InputGenericInfo GetAttributeInfo(EmitContext& ctx, AttributeType type, Id id) {
switch (type) {
case AttributeType::Float:
return InputGenericInfo{id,
ctx.input_f32,
ctx.F32[1],
ctx.F32[4],
ctx.TypePointer(spv::StorageClass::Input, ctx.F32[4]),
InputGenericLoadOp::None};
return InputGenericInfo{id, ctx.input_f32, ctx.F32[1], InputGenericLoadOp::None};
case AttributeType::UnsignedInt:
return InputGenericInfo{id,
ctx.input_u32,
ctx.U32[1],
ctx.U32[4],
ctx.TypePointer(spv::StorageClass::Input, ctx.U32[4]),
InputGenericLoadOp::Bitcast};
return InputGenericInfo{id, ctx.input_u32, ctx.U32[1], InputGenericLoadOp::Bitcast};
case AttributeType::SignedInt:
return InputGenericInfo{id,
ctx.input_s32,
ctx.TypeInt(32, true),
ctx.TypeVector(ctx.TypeInt(32, true), 4),
ctx.TypePointer(spv::StorageClass::Input,
ctx.TypeVector(ctx.TypeInt(32, true), 4)),
return InputGenericInfo{id, ctx.input_s32, ctx.TypeInt(32, true),
InputGenericLoadOp::Bitcast};
case AttributeType::SignedScaled:
if (ctx.profile.support_scaled_attributes) {
return InputGenericInfo{id,
ctx.input_f32,
ctx.F32[1],
ctx.F32[4],
ctx.TypePointer(spv::StorageClass::Input, ctx.F32[4]),
InputGenericLoadOp::None};
}
return InputGenericInfo{id,
ctx.input_s32,
ctx.TypeInt(32, true),
ctx.TypeVector(ctx.TypeInt(32, true), 4),
ctx.TypePointer(spv::StorageClass::Input,
ctx.TypeVector(ctx.TypeInt(32, true), 4)),
InputGenericLoadOp::SToF};
return ctx.profile.support_scaled_attributes
? InputGenericInfo{id, ctx.input_f32, ctx.F32[1], InputGenericLoadOp::None}
: InputGenericInfo{id, ctx.input_s32, ctx.TypeInt(32, true),
InputGenericLoadOp::SToF};
case AttributeType::UnsignedScaled:
if (ctx.profile.support_scaled_attributes) {
return InputGenericInfo{id,
ctx.input_f32,
ctx.F32[1],
ctx.F32[4],
ctx.TypePointer(spv::StorageClass::Input, ctx.F32[4]),
InputGenericLoadOp::None};
}
return InputGenericInfo{id,
ctx.input_u32,
ctx.U32[1],
ctx.U32[4],
ctx.TypePointer(spv::StorageClass::Input, ctx.U32[4]),
InputGenericLoadOp::UToF};
return ctx.profile.support_scaled_attributes
? InputGenericInfo{id, ctx.input_f32, ctx.F32[1], InputGenericLoadOp::None}
: InputGenericInfo{id, ctx.input_u32, ctx.U32[1], InputGenericLoadOp::UToF};
case AttributeType::Disabled:
return InputGenericInfo{};
}
@ -750,8 +702,7 @@ void EmitContext::DefineSharedMemoryFunctions(const IR::Program& program) {
void EmitContext::DefineAttributeMemAccess(const Info& info) {
const auto make_load{[&] {
const bool is_array{stage == Stage::Geometry || stage == Stage::TessellationControl ||
stage == Stage::TessellationEval};
const bool is_array{stage == Stage::Geometry};
const Id end_block{OpLabel()};
const Id default_label{OpLabel()};
@ -786,28 +737,6 @@ void EmitContext::DefineAttributeMemAccess(const Info& info) {
size_t label_index{0};
if (info.loads.AnyComponent(IR::Attribute::PositionX)) {
AddLabel(labels[label_index]);
if (ctx.profile.has_broken_spirv_vector_access_chain) {
const Id pointer_type{TypePointer(spv::StorageClass::Input, F32[4])};
const Id vector_pointer{[&]() {
if (need_input_position_indirect) {
if (is_array) {
return OpAccessChain(pointer_type, input_position, vertex,
u32_zero_value);
}
return OpAccessChain(pointer_type, input_position, u32_zero_value);
} else {
if (is_array) {
return OpAccessChain(pointer_type, input_position, vertex);
}
return input_position;
}
}()};
const Id vector_value{OpLoad(F32[4], vector_pointer)};
const Id result{OpVectorExtractDynamic(F32[1], vector_value, masked_index)};
OpReturnValue(result);
++label_index;
continue;
}
const Id pointer{[&]() {
if (need_input_position_indirect) {
if (is_array)
@ -839,36 +768,22 @@ void EmitContext::DefineAttributeMemAccess(const Info& info) {
++label_index;
continue;
}
const auto convert_value{[&](Id component) {
const Id pointer{
is_array ? OpAccessChain(generic.pointer_type, generic_id, vertex, masked_index)
: OpAccessChain(generic.pointer_type, generic_id, masked_index)};
const Id value{OpLoad(generic.component_type, pointer)};
const Id result{[this, generic, value]() {
switch (generic.load_op) {
case InputGenericLoadOp::Bitcast:
return OpBitcast(F32[1], component);
return OpBitcast(F32[1], value);
case InputGenericLoadOp::SToF:
return OpConvertSToF(F32[1], component);
return OpConvertSToF(F32[1], value);
case InputGenericLoadOp::UToF:
return OpConvertUToF(F32[1], component);
return OpConvertUToF(F32[1], value);
default:
return component;
}
}};
Id result{};
if (ctx.profile.has_broken_spirv_vector_access_chain) {
const Id vector_pointer{is_array
? OpAccessChain(generic.composite_pointer_type,
generic_id, vertex)
: generic_id};
const Id vector_value{OpLoad(generic.composite_type, vector_pointer)};
const Id component_value{
OpVectorExtractDynamic(generic.component_type, vector_value, masked_index)};
result = convert_value(component_value);
} else {
const Id pointer{
is_array ? OpAccessChain(generic.pointer_type, generic_id, vertex,
masked_index)
: OpAccessChain(generic.pointer_type, generic_id, masked_index)};
const Id value{OpLoad(generic.component_type, pointer)};
result = convert_value(value);
}
return value;
};
}()};
OpReturnValue(result);
++label_index;
}
@ -920,19 +835,6 @@ void EmitContext::DefineAttributeMemAccess(const Info& info) {
size_t label_index{0};
if (info.stores.AnyComponent(IR::Attribute::PositionX)) {
AddLabel(labels[label_index]);
if (ctx.profile.has_broken_spirv_vector_access_chain) {
const bool use_invocation{ctx.stage == Stage::TessellationControl};
const Id pointer_type{TypePointer(spv::StorageClass::Output, F32[4])};
const Id vector_pointer{use_invocation
? OpAccessChain(pointer_type, output_position,
OpLoad(U32[1], invocation_id))
: output_position};
const Id vector_value{OpLoad(F32[4], vector_pointer)};
const Id updated{
OpVectorInsertDynamic(F32[4], vector_value, store_value, masked_index)};
OpStore(vector_pointer, updated);
OpReturn();
}
const Id pointer{OpAccessChain(output_f32, output_position, masked_index)};
OpStore(pointer, store_value);
OpReturn();
@ -946,27 +848,7 @@ void EmitContext::DefineAttributeMemAccess(const Info& info) {
throw NotImplementedException("Physical stores and transform feedbacks");
}
AddLabel(labels[label_index]);
const GenericElementInfo& element_info{output_generics[index][0]};
if (ctx.profile.has_broken_spirv_vector_access_chain) {
Id local_index{masked_index};
if (element_info.first_element != 0) {
local_index = OpISub(U32[1], masked_index, Const(element_info.first_element));
}
const bool use_invocation{ctx.stage == Stage::TessellationControl};
const Id pointer_type{
TypePointer(spv::StorageClass::Output, element_info.composite_type)};
const Id invocation{use_invocation ? OpLoad(U32[1], invocation_id) : Id{}};
const Id vector_pointer{use_invocation
? OpAccessChain(pointer_type, element_info.id,
invocation)
: element_info.id};
const Id vector_value{OpLoad(element_info.composite_type, vector_pointer)};
const Id updated{OpVectorInsertDynamic(element_info.composite_type, vector_value,
store_value, local_index)};
OpStore(vector_pointer, updated);
OpReturn();
}
const Id generic_id{element_info.id};
const Id generic_id{output_generics[index][0].id};
const Id pointer{OpAccessChain(output_f32, generic_id, masked_index)};
OpStore(pointer, store_value);
OpReturn();

4
src/shader_recompiler/backend/spirv/spirv_emit_context.h

@ -146,8 +146,6 @@ struct InputGenericInfo {
Id id;
Id pointer_type;
Id component_type;
Id composite_type;
Id composite_pointer_type;
InputGenericLoadOp load_op;
};
@ -155,8 +153,6 @@ struct GenericElementInfo {
Id id{};
u32 first_element{};
u32 num_components{};
Id composite_type{};
Id composite_pointer_type{};
};
class EmitContext final : public Sirit::Module {

7
src/shader_recompiler/profile.h

@ -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
@ -64,10 +61,6 @@ struct Profile {
/// OpFClamp is broken and OpFMax + OpFMin should be used instead
bool has_broken_spirv_clamp{};
/// Driver mishandles vector OpAccessChain operations
bool has_broken_spirv_vector_access_chain{};
/// Driver crashes when spirv-opt folds certain OpAccessChain chains
bool has_broken_spirv_access_chain_opt{};
/// The Position builtin needs to be wrapped in a struct when used as an input
bool has_broken_spirv_position_input{};
/// Offset image operands with an unsigned type do not work

12
src/video_core/renderer_opengl/gl_shader_cache.cpp

@ -228,9 +228,7 @@ ShaderCache::ShaderCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
.need_fastmath_off = device.NeedsFastmathOff(),
.need_gather_subpixel_offset = device.IsAmd() || device.IsIntel(),
.has_broken_spirv_clamp = true,
.has_broken_spirv_vector_access_chain = false,
.has_broken_spirv_access_chain_opt = false,
.has_broken_spirv_clamp = true,
.has_broken_unsigned_image_offsets = true,
.has_broken_signed_operations = true,
.has_broken_fp16_float_controls = false,
@ -543,9 +541,7 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
case Settings::ShaderBackend::SpirV:
ConvertLegacyToGeneric(program, runtime_info);
sources_spirv[stage_index] =
const bool optimize_shader{this->optimize_spirv_output &&
!profile.has_broken_spirv_access_chain_opt};
EmitSPIRV(profile, runtime_info, program, binding, optimize_shader);
EmitSPIRV(profile, runtime_info, program, binding, this->optimize_spirv_output);
break;
}
previous_program = &program;
@ -604,9 +600,7 @@ std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
code = EmitGLASM(profile, info, program);
break;
case Settings::ShaderBackend::SpirV:
const bool optimize_shader{this->optimize_spirv_output &&
!profile.has_broken_spirv_access_chain_opt};
code_spirv = EmitSPIRV(profile, program, optimize_shader);
code_spirv = EmitSPIRV(profile, program, this->optimize_spirv_output);
break;
}

11
src/video_core/renderer_vulkan/vk_pipeline_cache.cpp

@ -368,8 +368,6 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA,
.has_broken_spirv_clamp = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS,
.has_broken_spirv_vector_access_chain = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY,
.has_broken_spirv_access_chain_opt = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY,
.has_broken_spirv_position_input = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY,
.has_broken_unsigned_image_offsets = false,
.has_broken_signed_operations = false,
@ -696,10 +694,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
const auto runtime_info{MakeRuntimeInfo(programs, key, program, previous_stage)};
ConvertLegacyToGeneric(program, runtime_info);
const bool optimize_shader{this->optimize_spirv_output &&
!profile.has_broken_spirv_access_chain_opt};
const std::vector<u32> code{EmitSPIRV(profile, runtime_info, program, binding,
optimize_shader)};
const std::vector<u32> code{EmitSPIRV(profile, runtime_info, program, binding, this->optimize_spirv_output)};
device.SaveShader(code);
modules[stage_index] = BuildShader(device, code);
if (device.HasDebuggingToolAttached()) {
@ -806,9 +801,7 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
max_shared_memory / 1024);
program.shared_memory_size = max_shared_memory;
}
const bool optimize_shader{this->optimize_spirv_output &&
!profile.has_broken_spirv_access_chain_opt};
const std::vector<u32> code{EmitSPIRV(profile, program, optimize_shader)};
const std::vector<u32> code{EmitSPIRV(profile, program, this->optimize_spirv_output)};
device.SaveShader(code);
vk::ShaderModule spv_module{BuildShader(device, code)};
if (device.HasDebuggingToolAttached()) {

Loading…
Cancel
Save