shader: Better interpolation and disabled attributes support

This commit is contained in:
ReinUsesLisp 2021-03-27 04:59:58 -03:00 committed by ameerj
parent 675a82416d
commit dbd882ddeb
9 changed files with 101 additions and 25 deletions

View file

@ -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;
}
}
}

View file

@ -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:

View file

@ -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;
}

View file

@ -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.

View file

@ -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) {

View file

@ -14,6 +14,7 @@ enum class AttributeType : u8 {
Float,
SignedInt,
UnsignedInt,
Disabled,
};
struct Profile {

View file

@ -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{};

View file

@ -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({

View file

@ -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: