mirror of
https://git.suyu.dev/suyu/suyu.git
synced 2024-11-27 09:12:46 +01:00
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);
|
return ctx.TypeVector(ctx.TypeInt(32, true), 4);
|
||||||
case AttributeType::UnsignedInt:
|
case AttributeType::UnsignedInt:
|
||||||
return ctx.U32[4];
|
return ctx.U32[4];
|
||||||
|
case AttributeType::Disabled:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
throw InvalidArgument("Invalid attribute type {}", type);
|
throw InvalidArgument("Invalid attribute type {}", type);
|
||||||
}
|
}
|
||||||
|
@ -305,15 +307,36 @@ void EmitContext::DefineInputs(const Info& info) {
|
||||||
if (info.loads_front_face) {
|
if (info.loads_front_face) {
|
||||||
front_face = DefineInput(*this, U1, spv::BuiltIn::FrontFacing);
|
front_face = DefineInput(*this, U1, spv::BuiltIn::FrontFacing);
|
||||||
}
|
}
|
||||||
for (size_t index = 0; index < info.loads_generics.size(); ++index) {
|
for (size_t index = 0; index < info.input_generics.size(); ++index) {
|
||||||
if (!info.loads_generics[index]) {
|
const InputVarying generic{info.input_generics[index]};
|
||||||
|
if (!generic.used) {
|
||||||
continue;
|
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)};
|
const Id id{DefineInput(*this, type)};
|
||||||
Decorate(id, spv::Decoration::Location, static_cast<u32>(index));
|
Decorate(id, spv::Decoration::Location, static_cast<u32>(index));
|
||||||
Name(id, fmt::format("in_attr{}", index));
|
Name(id, fmt::format("in_attr{}", index));
|
||||||
input_generics[index] = id;
|
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 Shader::Backend::SPIRV {
|
||||||
namespace {
|
namespace {
|
||||||
std::tuple<Id, Id, bool> AttrTypes(EmitContext& ctx, u32 index) {
|
struct AttrInfo {
|
||||||
const bool is_first_reader{ctx.stage == Stage::VertexB};
|
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)};
|
const AttributeType type{ctx.profile.generic_input_types.at(index)};
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case AttributeType::Float:
|
case AttributeType::Float:
|
||||||
return {ctx.input_f32, ctx.F32[1], false};
|
return AttrInfo{ctx.input_f32, ctx.F32[1], false};
|
||||||
case AttributeType::UnsignedInt:
|
case AttributeType::UnsignedInt:
|
||||||
return {ctx.input_u32, ctx.U32[1], true};
|
return AttrInfo{ctx.input_u32, ctx.U32[1], true};
|
||||||
case AttributeType::SignedInt:
|
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);
|
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); }};
|
const auto element_id{[&] { return ctx.Constant(ctx.U32[1], element); }};
|
||||||
if (IR::IsGeneric(attr)) {
|
if (IR::IsGeneric(attr)) {
|
||||||
const u32 index{IR::GenericAttributeIndex(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 generic_id{ctx.input_generics.at(index)};
|
||||||
const Id pointer{ctx.OpAccessChain(pointer_type, generic_id, element_id())};
|
const Id pointer{ctx.OpAccessChain(type->pointer, generic_id, element_id())};
|
||||||
const Id value{ctx.OpLoad(type, pointer)};
|
const Id value{ctx.OpLoad(type->id, pointer)};
|
||||||
return needs_cast ? ctx.OpBitcast(ctx.F32[1], value) : value;
|
return type->needs_cast ? ctx.OpBitcast(ctx.F32[1], value) : value;
|
||||||
}
|
}
|
||||||
switch (attr) {
|
switch (attr) {
|
||||||
case IR::Attribute::PositionX:
|
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,
|
IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
|
||||||
Environment& env, Flow::CFG& cfg) {
|
Environment& env, Flow::CFG& cfg) {
|
||||||
IR::Program program;
|
IR::Program program;
|
||||||
|
@ -51,6 +85,7 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
|
||||||
Optimization::IdentityRemovalPass(program);
|
Optimization::IdentityRemovalPass(program);
|
||||||
Optimization::VerificationPass(program);
|
Optimization::VerificationPass(program);
|
||||||
Optimization::CollectShaderInfoPass(program);
|
Optimization::CollectShaderInfoPass(program);
|
||||||
|
CollectInterpolationInfo(env, program);
|
||||||
return program;
|
return program;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -151,16 +151,8 @@ void TranslatorVisitor::IPA(u64 insn) {
|
||||||
value = ir.FPMul(value, position_w);
|
value = ir.FPMul(value, position_w);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch (ipa.interpolation_mode) {
|
if (ipa.interpolation_mode == InterpolationMode::Multiply) {
|
||||||
case InterpolationMode::Pass:
|
|
||||||
break;
|
|
||||||
case InterpolationMode::Multiply:
|
|
||||||
value = ir.FPMul(value, F(ipa.multiplier));
|
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.
|
// 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) {
|
void GetAttribute(Info& info, IR::Attribute attribute) {
|
||||||
if (IR::IsGeneric(attribute)) {
|
if (IR::IsGeneric(attribute)) {
|
||||||
info.loads_generics.at(IR::GenericAttributeIndex(attribute)) = true;
|
info.input_generics.at(IR::GenericAttributeIndex(attribute)).used = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switch (attribute) {
|
switch (attribute) {
|
||||||
|
|
|
@ -14,6 +14,7 @@ enum class AttributeType : u8 {
|
||||||
Float,
|
Float,
|
||||||
SignedInt,
|
SignedInt,
|
||||||
UnsignedInt,
|
UnsignedInt,
|
||||||
|
Disabled,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Profile {
|
struct Profile {
|
||||||
|
|
|
@ -31,6 +31,17 @@ enum class TextureType : u32 {
|
||||||
ShadowArrayCube,
|
ShadowArrayCube,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class Interpolation {
|
||||||
|
Smooth,
|
||||||
|
Flat,
|
||||||
|
NoPerspective,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct InputVarying {
|
||||||
|
Interpolation interpolation{Interpolation::Smooth};
|
||||||
|
bool used{false};
|
||||||
|
};
|
||||||
|
|
||||||
struct TextureDescriptor {
|
struct TextureDescriptor {
|
||||||
TextureType type;
|
TextureType type;
|
||||||
u32 cbuf_index;
|
u32 cbuf_index;
|
||||||
|
@ -58,7 +69,7 @@ struct Info {
|
||||||
bool uses_local_invocation_id{};
|
bool uses_local_invocation_id{};
|
||||||
bool uses_subgroup_invocation_id{};
|
bool uses_subgroup_invocation_id{};
|
||||||
|
|
||||||
std::array<bool, 32> loads_generics{};
|
std::array<InputVarying, 32> input_generics{};
|
||||||
bool loads_position{};
|
bool loads_position{};
|
||||||
bool loads_instance_id{};
|
bool loads_instance_id{};
|
||||||
bool loads_vertex_id{};
|
bool loads_vertex_id{};
|
||||||
|
|
|
@ -221,10 +221,10 @@ void GraphicsPipeline::MakePipeline(const Device& device, const FixedPipelineSta
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static_vector<VkVertexInputAttributeDescription, 32> vertex_attributes;
|
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) {
|
for (size_t index = 0; index < state.attributes.size(); ++index) {
|
||||||
const auto& attribute = state.attributes[index];
|
const auto& attribute = state.attributes[index];
|
||||||
if (!attribute.enabled || !input_attributes[index]) {
|
if (!attribute.enabled || !input_attributes[index].used) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
vertex_attributes.push_back({
|
vertex_attributes.push_back({
|
||||||
|
|
|
@ -755,6 +755,9 @@ ComputePipeline PipelineCache::CreateComputePipeline(ShaderPools& pools,
|
||||||
}
|
}
|
||||||
|
|
||||||
static Shader::AttributeType CastAttributeType(const FixedPipelineState::VertexAttribute& attr) {
|
static Shader::AttributeType CastAttributeType(const FixedPipelineState::VertexAttribute& attr) {
|
||||||
|
if (attr.enabled == 0) {
|
||||||
|
return Shader::AttributeType::Disabled;
|
||||||
|
}
|
||||||
switch (attr.Type()) {
|
switch (attr.Type()) {
|
||||||
case Maxwell::VertexAttribute::Type::SignedNorm:
|
case Maxwell::VertexAttribute::Type::SignedNorm:
|
||||||
case Maxwell::VertexAttribute::Type::UnsignedNorm:
|
case Maxwell::VertexAttribute::Type::UnsignedNorm:
|
||||||
|
|
Loading…
Reference in a new issue