shader: Better interpolation and disabled attributes support
This commit is contained in:
parent
675a82416d
commit
dbd882ddeb
9 changed files with 101 additions and 25 deletions
|
@ -76,6 +76,8 @@ Id GetAttributeType(EmitContext& ctx, AttributeType type) {
|
|||
return ctx.TypeVector(ctx.TypeInt(32, true), 4);
|
||||
case AttributeType::UnsignedInt:
|
||||
return ctx.U32[4];
|
||||
case AttributeType::Disabled:
|
||||
break;
|
||||
}
|
||||
throw InvalidArgument("Invalid attribute type {}", type);
|
||||
}
|
||||
|
@ -305,15 +307,36 @@ void EmitContext::DefineInputs(const Info& info) {
|
|||
if (info.loads_front_face) {
|
||||
front_face = DefineInput(*this, U1, spv::BuiltIn::FrontFacing);
|
||||
}
|
||||
for (size_t index = 0; index < info.loads_generics.size(); ++index) {
|
||||
if (!info.loads_generics[index]) {
|
||||
for (size_t index = 0; index < info.input_generics.size(); ++index) {
|
||||
const InputVarying generic{info.input_generics[index]};
|
||||
if (!generic.used) {
|
||||
continue;
|
||||
}
|
||||
const Id type{GetAttributeType(*this, profile.generic_input_types[index])};
|
||||
const AttributeType input_type{profile.generic_input_types[index]};
|
||||
if (input_type == AttributeType::Disabled) {
|
||||
continue;
|
||||
}
|
||||
const Id type{GetAttributeType(*this, input_type)};
|
||||
const Id id{DefineInput(*this, type)};
|
||||
Decorate(id, spv::Decoration::Location, static_cast<u32>(index));
|
||||
Name(id, fmt::format("in_attr{}", index));
|
||||
input_generics[index] = id;
|
||||
|
||||
if (stage != Stage::Fragment) {
|
||||
continue;
|
||||
}
|
||||
switch (generic.interpolation) {
|
||||
case Interpolation::Smooth:
|
||||
// Default
|
||||
// Decorate(id, spv::Decoration::Smooth);
|
||||
break;
|
||||
case Interpolation::NoPerspective:
|
||||
Decorate(id, spv::Decoration::NoPerspective);
|
||||
break;
|
||||
case Interpolation::Flat:
|
||||
Decorate(id, spv::Decoration::Flat);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,16 +10,23 @@
|
|||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
std::tuple<Id, Id, bool> AttrTypes(EmitContext& ctx, u32 index) {
|
||||
const bool is_first_reader{ctx.stage == Stage::VertexB};
|
||||
struct AttrInfo {
|
||||
Id pointer;
|
||||
Id id;
|
||||
bool needs_cast;
|
||||
};
|
||||
|
||||
std::optional<AttrInfo> AttrTypes(EmitContext& ctx, u32 index) {
|
||||
const AttributeType type{ctx.profile.generic_input_types.at(index)};
|
||||
switch (type) {
|
||||
case AttributeType::Float:
|
||||
return {ctx.input_f32, ctx.F32[1], false};
|
||||
return AttrInfo{ctx.input_f32, ctx.F32[1], false};
|
||||
case AttributeType::UnsignedInt:
|
||||
return {ctx.input_u32, ctx.U32[1], true};
|
||||
return AttrInfo{ctx.input_u32, ctx.U32[1], true};
|
||||
case AttributeType::SignedInt:
|
||||
return {ctx.input_s32, ctx.TypeInt(32, true), true};
|
||||
return AttrInfo{ctx.input_s32, ctx.TypeInt(32, true), true};
|
||||
case AttributeType::Disabled:
|
||||
return std::nullopt;
|
||||
}
|
||||
throw InvalidArgument("Invalid attribute type {}", type);
|
||||
}
|
||||
|
@ -129,11 +136,15 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr) {
|
|||
const auto element_id{[&] { return ctx.Constant(ctx.U32[1], element); }};
|
||||
if (IR::IsGeneric(attr)) {
|
||||
const u32 index{IR::GenericAttributeIndex(attr)};
|
||||
const auto [pointer_type, type, needs_cast]{AttrTypes(ctx, index)};
|
||||
const std::optional<AttrInfo> type{AttrTypes(ctx, index)};
|
||||
if (!type) {
|
||||
// Attribute is disabled
|
||||
return ctx.Constant(ctx.F32[1], 0.0f);
|
||||
}
|
||||
const Id generic_id{ctx.input_generics.at(index)};
|
||||
const Id pointer{ctx.OpAccessChain(pointer_type, generic_id, element_id())};
|
||||
const Id value{ctx.OpLoad(type, pointer)};
|
||||
return needs_cast ? ctx.OpBitcast(ctx.F32[1], value) : value;
|
||||
const Id pointer{ctx.OpAccessChain(type->pointer, generic_id, element_id())};
|
||||
const Id value{ctx.OpLoad(type->id, pointer)};
|
||||
return type->needs_cast ? ctx.OpBitcast(ctx.F32[1], value) : value;
|
||||
}
|
||||
switch (attr) {
|
||||
case IR::Attribute::PositionX:
|
||||
|
|
|
@ -27,6 +27,40 @@ static void RemoveUnreachableBlocks(IR::Program& program) {
|
|||
});
|
||||
}
|
||||
|
||||
static void CollectInterpolationInfo(Environment& env, IR::Program& program) {
|
||||
if (program.stage != Stage::Fragment) {
|
||||
return;
|
||||
}
|
||||
const ProgramHeader& sph{env.SPH()};
|
||||
for (size_t index = 0; index < program.info.input_generics.size(); ++index) {
|
||||
std::optional<PixelImap> imap;
|
||||
for (const PixelImap value : sph.ps.GenericInputMap(static_cast<u32>(index))) {
|
||||
if (value == PixelImap::Unused) {
|
||||
continue;
|
||||
}
|
||||
if (imap && imap != value) {
|
||||
throw NotImplementedException("Per component interpolation");
|
||||
}
|
||||
imap = value;
|
||||
}
|
||||
if (!imap) {
|
||||
continue;
|
||||
}
|
||||
program.info.input_generics[index].interpolation = [&] {
|
||||
switch (*imap) {
|
||||
case PixelImap::Unused:
|
||||
case PixelImap::Perspective:
|
||||
return Interpolation::Smooth;
|
||||
case PixelImap::Constant:
|
||||
return Interpolation::Flat;
|
||||
case PixelImap::ScreenLinear:
|
||||
return Interpolation::NoPerspective;
|
||||
}
|
||||
throw NotImplementedException("Unknown interpolation {}", *imap);
|
||||
}();
|
||||
}
|
||||
}
|
||||
|
||||
IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
|
||||
Environment& env, Flow::CFG& cfg) {
|
||||
IR::Program program;
|
||||
|
@ -51,6 +85,7 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
|
|||
Optimization::IdentityRemovalPass(program);
|
||||
Optimization::VerificationPass(program);
|
||||
Optimization::CollectShaderInfoPass(program);
|
||||
CollectInterpolationInfo(env, program);
|
||||
return program;
|
||||
}
|
||||
|
||||
|
|
|
@ -151,16 +151,8 @@ void TranslatorVisitor::IPA(u64 insn) {
|
|||
value = ir.FPMul(value, position_w);
|
||||
}
|
||||
}
|
||||
switch (ipa.interpolation_mode) {
|
||||
case InterpolationMode::Pass:
|
||||
break;
|
||||
case InterpolationMode::Multiply:
|
||||
if (ipa.interpolation_mode == InterpolationMode::Multiply) {
|
||||
value = ir.FPMul(value, F(ipa.multiplier));
|
||||
break;
|
||||
case InterpolationMode::Constant:
|
||||
throw NotImplementedException("IPA.CONSTANT");
|
||||
case InterpolationMode::Sc:
|
||||
throw NotImplementedException("IPA.SC");
|
||||
}
|
||||
|
||||
// Saturated IPAs are generally generated out of clamped varyings.
|
||||
|
|
|
@ -28,7 +28,7 @@ void AddConstantBufferDescriptor(Info& info, u32 index, u32 count) {
|
|||
|
||||
void GetAttribute(Info& info, IR::Attribute attribute) {
|
||||
if (IR::IsGeneric(attribute)) {
|
||||
info.loads_generics.at(IR::GenericAttributeIndex(attribute)) = true;
|
||||
info.input_generics.at(IR::GenericAttributeIndex(attribute)).used = true;
|
||||
return;
|
||||
}
|
||||
switch (attribute) {
|
||||
|
|
|
@ -14,6 +14,7 @@ enum class AttributeType : u8 {
|
|||
Float,
|
||||
SignedInt,
|
||||
UnsignedInt,
|
||||
Disabled,
|
||||
};
|
||||
|
||||
struct Profile {
|
||||
|
|
|
@ -31,6 +31,17 @@ enum class TextureType : u32 {
|
|||
ShadowArrayCube,
|
||||
};
|
||||
|
||||
enum class Interpolation {
|
||||
Smooth,
|
||||
Flat,
|
||||
NoPerspective,
|
||||
};
|
||||
|
||||
struct InputVarying {
|
||||
Interpolation interpolation{Interpolation::Smooth};
|
||||
bool used{false};
|
||||
};
|
||||
|
||||
struct TextureDescriptor {
|
||||
TextureType type;
|
||||
u32 cbuf_index;
|
||||
|
@ -58,7 +69,7 @@ struct Info {
|
|||
bool uses_local_invocation_id{};
|
||||
bool uses_subgroup_invocation_id{};
|
||||
|
||||
std::array<bool, 32> loads_generics{};
|
||||
std::array<InputVarying, 32> input_generics{};
|
||||
bool loads_position{};
|
||||
bool loads_instance_id{};
|
||||
bool loads_vertex_id{};
|
||||
|
|
|
@ -221,10 +221,10 @@ void GraphicsPipeline::MakePipeline(const Device& device, const FixedPipelineSta
|
|||
}
|
||||
}
|
||||
static_vector<VkVertexInputAttributeDescription, 32> vertex_attributes;
|
||||
const auto& input_attributes = stage_infos[0].loads_generics;
|
||||
const auto& input_attributes = stage_infos[0].input_generics;
|
||||
for (size_t index = 0; index < state.attributes.size(); ++index) {
|
||||
const auto& attribute = state.attributes[index];
|
||||
if (!attribute.enabled || !input_attributes[index]) {
|
||||
if (!attribute.enabled || !input_attributes[index].used) {
|
||||
continue;
|
||||
}
|
||||
vertex_attributes.push_back({
|
||||
|
|
|
@ -755,6 +755,9 @@ ComputePipeline PipelineCache::CreateComputePipeline(ShaderPools& pools,
|
|||
}
|
||||
|
||||
static Shader::AttributeType CastAttributeType(const FixedPipelineState::VertexAttribute& attr) {
|
||||
if (attr.enabled == 0) {
|
||||
return Shader::AttributeType::Disabled;
|
||||
}
|
||||
switch (attr.Type()) {
|
||||
case Maxwell::VertexAttribute::Type::SignedNorm:
|
||||
case Maxwell::VertexAttribute::Type::UnsignedNorm:
|
||||
|
|
Loading…
Reference in a new issue