shader: Split profile and runtime information in separate structs
This commit is contained in:
parent
eb15667905
commit
9e7b6622c2
|
@ -23,23 +23,25 @@ std::string_view InterpDecorator(Interpolation interp) {
|
||||||
}
|
}
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_)
|
EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_,
|
||||||
: info{program.info}, profile{profile_} {
|
const RuntimeInfo& runtime_info_)
|
||||||
|
: profile{profile_}, runtime_info{runtime_info_} {
|
||||||
// FIXME: Temporary partial implementation
|
// FIXME: Temporary partial implementation
|
||||||
|
const auto& info{program.info};
|
||||||
u32 cbuf_index{};
|
u32 cbuf_index{};
|
||||||
for (const auto& desc : program.info.constant_buffer_descriptors) {
|
for (const auto& desc : info.constant_buffer_descriptors) {
|
||||||
if (desc.count != 1) {
|
if (desc.count != 1) {
|
||||||
throw NotImplementedException("Constant buffer descriptor array");
|
throw NotImplementedException("Constant buffer descriptor array");
|
||||||
}
|
}
|
||||||
Add("CBUFFER c{}[]={{program.buffer[{}]}};", desc.index, cbuf_index);
|
Add("CBUFFER c{}[]={{program.buffer[{}]}};", desc.index, cbuf_index);
|
||||||
++cbuf_index;
|
++cbuf_index;
|
||||||
}
|
}
|
||||||
for (const auto& desc : program.info.storage_buffers_descriptors) {
|
for (const auto& desc : info.storage_buffers_descriptors) {
|
||||||
if (desc.count != 1) {
|
if (desc.count != 1) {
|
||||||
throw NotImplementedException("Storage buffer descriptor array");
|
throw NotImplementedException("Storage buffer descriptor array");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (const size_t num = program.info.storage_buffers_descriptors.size(); num > 0) {
|
if (const size_t num = info.storage_buffers_descriptors.size(); num > 0) {
|
||||||
Add("PARAM c[{}]={{program.local[0..{}]}};", num, num - 1);
|
Add("PARAM c[{}]={{program.local[0..{}]}};", num, num - 1);
|
||||||
}
|
}
|
||||||
stage = program.stage;
|
stage = program.stage;
|
||||||
|
@ -67,8 +69,8 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
const std::string_view attr_stage{stage == Stage::Fragment ? "fragment" : "vertex"};
|
const std::string_view attr_stage{stage == Stage::Fragment ? "fragment" : "vertex"};
|
||||||
for (size_t index = 0; index < program.info.input_generics.size(); ++index) {
|
for (size_t index = 0; index < info.input_generics.size(); ++index) {
|
||||||
const auto& generic{program.info.input_generics[index]};
|
const auto& generic{info.input_generics[index]};
|
||||||
if (generic.used) {
|
if (generic.used) {
|
||||||
Add("{}ATTRIB in_attr{}[]={{{}.attrib[{}..{}]}};",
|
Add("{}ATTRIB in_attr{}[]={{{}.attrib[{}..{}]}};",
|
||||||
InterpDecorator(generic.interpolation), index, attr_stage, index, index);
|
InterpDecorator(generic.interpolation), index, attr_stage, index, index);
|
||||||
|
@ -101,8 +103,8 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
|
||||||
index, index);
|
index, index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (size_t index = 0; index < program.info.stores_frag_color.size(); ++index) {
|
for (size_t index = 0; index < info.stores_frag_color.size(); ++index) {
|
||||||
if (!program.info.stores_frag_color[index]) {
|
if (!info.stores_frag_color[index]) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
|
@ -111,28 +113,28 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
|
||||||
Add("OUTPUT frag_color{}=result.color[{}];", index, index);
|
Add("OUTPUT frag_color{}=result.color[{}];", index, index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (size_t index = 0; index < program.info.stores_generics.size(); ++index) {
|
for (size_t index = 0; index < info.stores_generics.size(); ++index) {
|
||||||
if (program.info.stores_generics[index]) {
|
if (info.stores_generics[index]) {
|
||||||
Add("OUTPUT out_attr{}[]={{result.attrib[{}..{}]}};", index, index, index);
|
Add("OUTPUT out_attr{}[]={{result.attrib[{}..{}]}};", index, index, index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
image_buffer_bindings.reserve(program.info.image_buffer_descriptors.size());
|
image_buffer_bindings.reserve(info.image_buffer_descriptors.size());
|
||||||
for (const auto& desc : program.info.image_buffer_descriptors) {
|
for (const auto& desc : info.image_buffer_descriptors) {
|
||||||
image_buffer_bindings.push_back(bindings.image);
|
image_buffer_bindings.push_back(bindings.image);
|
||||||
bindings.image += desc.count;
|
bindings.image += desc.count;
|
||||||
}
|
}
|
||||||
image_bindings.reserve(program.info.image_descriptors.size());
|
image_bindings.reserve(info.image_descriptors.size());
|
||||||
for (const auto& desc : program.info.image_descriptors) {
|
for (const auto& desc : info.image_descriptors) {
|
||||||
image_bindings.push_back(bindings.image);
|
image_bindings.push_back(bindings.image);
|
||||||
bindings.image += desc.count;
|
bindings.image += desc.count;
|
||||||
}
|
}
|
||||||
texture_buffer_bindings.reserve(program.info.texture_buffer_descriptors.size());
|
texture_buffer_bindings.reserve(info.texture_buffer_descriptors.size());
|
||||||
for (const auto& desc : program.info.texture_buffer_descriptors) {
|
for (const auto& desc : info.texture_buffer_descriptors) {
|
||||||
texture_buffer_bindings.push_back(bindings.texture);
|
texture_buffer_bindings.push_back(bindings.texture);
|
||||||
bindings.texture += desc.count;
|
bindings.texture += desc.count;
|
||||||
}
|
}
|
||||||
texture_bindings.reserve(program.info.texture_descriptors.size());
|
texture_bindings.reserve(info.texture_descriptors.size());
|
||||||
for (const auto& desc : program.info.texture_descriptors) {
|
for (const auto& desc : info.texture_descriptors) {
|
||||||
texture_bindings.push_back(bindings.texture);
|
texture_bindings.push_back(bindings.texture);
|
||||||
bindings.texture += desc.count;
|
bindings.texture += desc.count;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
namespace Shader {
|
namespace Shader {
|
||||||
struct Info;
|
struct Info;
|
||||||
struct Profile;
|
struct Profile;
|
||||||
|
struct RuntimeInfo;
|
||||||
} // namespace Shader
|
} // namespace Shader
|
||||||
|
|
||||||
namespace Shader::Backend {
|
namespace Shader::Backend {
|
||||||
|
@ -31,7 +32,8 @@ namespace Shader::Backend::GLASM {
|
||||||
|
|
||||||
class EmitContext {
|
class EmitContext {
|
||||||
public:
|
public:
|
||||||
explicit EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_);
|
explicit EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_,
|
||||||
|
const RuntimeInfo& runtime_info_);
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
void Add(const char* format_str, IR::Inst& inst, Args&&... args) {
|
void Add(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||||
|
@ -56,8 +58,8 @@ public:
|
||||||
|
|
||||||
std::string code;
|
std::string code;
|
||||||
RegAlloc reg_alloc{*this};
|
RegAlloc reg_alloc{*this};
|
||||||
const Info& info;
|
|
||||||
const Profile& profile;
|
const Profile& profile;
|
||||||
|
const RuntimeInfo& runtime_info;
|
||||||
|
|
||||||
std::vector<u32> texture_buffer_bindings;
|
std::vector<u32> texture_buffer_bindings;
|
||||||
std::vector<u32> image_buffer_bindings;
|
std::vector<u32> image_buffer_bindings;
|
||||||
|
|
|
@ -374,8 +374,9 @@ std::string_view GetTessSpacing(TessSpacing spacing) {
|
||||||
}
|
}
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
std::string EmitGLASM(const Profile& profile, IR::Program& program, Bindings& bindings) {
|
std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info, IR::Program& program,
|
||||||
EmitContext ctx{program, bindings, profile};
|
Bindings& bindings) {
|
||||||
|
EmitContext ctx{program, bindings, profile, runtime_info};
|
||||||
Precolor(ctx, program);
|
Precolor(ctx, program);
|
||||||
EmitCode(ctx, program);
|
EmitCode(ctx, program);
|
||||||
std::string header{StageHeader(program.stage)};
|
std::string header{StageHeader(program.stage)};
|
||||||
|
@ -385,18 +386,18 @@ std::string EmitGLASM(const Profile& profile, IR::Program& program, Bindings& bi
|
||||||
header += fmt::format("VERTICES_OUT {};", program.invocations);
|
header += fmt::format("VERTICES_OUT {};", program.invocations);
|
||||||
break;
|
break;
|
||||||
case Stage::TessellationEval:
|
case Stage::TessellationEval:
|
||||||
header +=
|
header += fmt::format("TESS_MODE {};"
|
||||||
fmt::format("TESS_MODE {};"
|
"TESS_SPACING {};"
|
||||||
"TESS_SPACING {};"
|
"TESS_VERTEX_ORDER {};",
|
||||||
"TESS_VERTEX_ORDER {};",
|
GetTessMode(runtime_info.tess_primitive),
|
||||||
GetTessMode(profile.tess_primitive), GetTessSpacing(profile.tess_spacing),
|
GetTessSpacing(runtime_info.tess_spacing),
|
||||||
profile.tess_clockwise ? "CW" : "CCW");
|
runtime_info.tess_clockwise ? "CW" : "CCW");
|
||||||
break;
|
break;
|
||||||
case Stage::Geometry:
|
case Stage::Geometry:
|
||||||
header += fmt::format("PRIMITIVE_IN {};"
|
header += fmt::format("PRIMITIVE_IN {};"
|
||||||
"PRIMITIVE_OUT {};"
|
"PRIMITIVE_OUT {};"
|
||||||
"VERTICES_OUT {};",
|
"VERTICES_OUT {};",
|
||||||
InputPrimitive(profile.input_topology),
|
InputPrimitive(runtime_info.input_topology),
|
||||||
OutputPrimitive(program.output_topology), program.output_vertices);
|
OutputPrimitive(program.output_topology), program.output_vertices);
|
||||||
break;
|
break;
|
||||||
case Stage::Compute:
|
case Stage::Compute:
|
||||||
|
|
|
@ -12,12 +12,12 @@
|
||||||
|
|
||||||
namespace Shader::Backend::GLASM {
|
namespace Shader::Backend::GLASM {
|
||||||
|
|
||||||
[[nodiscard]] std::string EmitGLASM(const Profile& profile, IR::Program& program,
|
[[nodiscard]] std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info,
|
||||||
Bindings& binding);
|
IR::Program& program, Bindings& bindings);
|
||||||
|
|
||||||
[[nodiscard]] inline std::string EmitGLASM(const Profile& profile, IR::Program& program) {
|
[[nodiscard]] inline std::string EmitGLASM(const Profile& profile, IR::Program& program) {
|
||||||
Bindings binding;
|
Bindings binding;
|
||||||
return EmitGLASM(profile, program, binding);
|
return EmitGLASM(profile, {}, program, binding);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Shader::Backend::GLASM
|
} // namespace Shader::Backend::GLASM
|
||||||
|
|
|
@ -136,7 +136,7 @@ Id DefineInput(EmitContext& ctx, Id type, bool per_invocation,
|
||||||
break;
|
break;
|
||||||
case Stage::Geometry:
|
case Stage::Geometry:
|
||||||
if (per_invocation) {
|
if (per_invocation) {
|
||||||
const u32 num_vertices{NumVertices(ctx.profile.input_topology)};
|
const u32 num_vertices{NumVertices(ctx.runtime_info.input_topology)};
|
||||||
type = ctx.TypeArray(type, ctx.Const(num_vertices));
|
type = ctx.TypeArray(type, ctx.Const(num_vertices));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -161,8 +161,8 @@ void DefineGenericOutput(EmitContext& ctx, size_t index, std::optional<u32> invo
|
||||||
while (element < 4) {
|
while (element < 4) {
|
||||||
const u32 remainder{4 - element};
|
const u32 remainder{4 - element};
|
||||||
const TransformFeedbackVarying* xfb_varying{};
|
const TransformFeedbackVarying* xfb_varying{};
|
||||||
if (!ctx.profile.xfb_varyings.empty()) {
|
if (!ctx.runtime_info.xfb_varyings.empty()) {
|
||||||
xfb_varying = &ctx.profile.xfb_varyings[base_attr_index + element];
|
xfb_varying = &ctx.runtime_info.xfb_varyings[base_attr_index + element];
|
||||||
xfb_varying = xfb_varying && xfb_varying->components > 0 ? xfb_varying : nullptr;
|
xfb_varying = xfb_varying && xfb_varying->components > 0 ? xfb_varying : nullptr;
|
||||||
}
|
}
|
||||||
const u32 num_components{xfb_varying ? xfb_varying->components : remainder};
|
const u32 num_components{xfb_varying ? xfb_varying->components : remainder};
|
||||||
|
@ -208,7 +208,7 @@ Id GetAttributeType(EmitContext& ctx, AttributeType type) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<AttrInfo> AttrTypes(EmitContext& ctx, u32 index) {
|
std::optional<AttrInfo> AttrTypes(EmitContext& ctx, u32 index) {
|
||||||
const AttributeType type{ctx.profile.generic_input_types.at(index)};
|
const AttributeType type{ctx.runtime_info.generic_input_types.at(index)};
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case AttributeType::Float:
|
case AttributeType::Float:
|
||||||
return AttrInfo{ctx.input_f32, ctx.F32[1], false};
|
return AttrInfo{ctx.input_f32, ctx.F32[1], false};
|
||||||
|
@ -441,13 +441,15 @@ void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_vie
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EmitContext::EmitContext(const Profile& profile_, IR::Program& program, Bindings& binding)
|
EmitContext::EmitContext(const Profile& profile_, const RuntimeInfo& runtime_info_,
|
||||||
: Sirit::Module(profile_.supported_spirv), profile{profile_}, stage{program.stage} {
|
IR::Program& program, Bindings& bindings)
|
||||||
|
: Sirit::Module(profile_.supported_spirv), profile{profile_},
|
||||||
|
runtime_info{runtime_info_}, stage{program.stage} {
|
||||||
const bool is_unified{profile.unified_descriptor_binding};
|
const bool is_unified{profile.unified_descriptor_binding};
|
||||||
u32& uniform_binding{is_unified ? binding.unified : binding.uniform_buffer};
|
u32& uniform_binding{is_unified ? bindings.unified : bindings.uniform_buffer};
|
||||||
u32& storage_binding{is_unified ? binding.unified : binding.storage_buffer};
|
u32& storage_binding{is_unified ? bindings.unified : bindings.storage_buffer};
|
||||||
u32& texture_binding{is_unified ? binding.unified : binding.texture};
|
u32& texture_binding{is_unified ? bindings.unified : bindings.texture};
|
||||||
u32& image_binding{is_unified ? binding.unified : binding.image};
|
u32& image_binding{is_unified ? bindings.unified : bindings.image};
|
||||||
AddCapability(spv::Capability::Shader);
|
AddCapability(spv::Capability::Shader);
|
||||||
DefineCommonTypes(program.info);
|
DefineCommonTypes(program.info);
|
||||||
DefineCommonConstants();
|
DefineCommonConstants();
|
||||||
|
@ -1211,7 +1213,7 @@ void EmitContext::DefineInputs(const Info& info) {
|
||||||
if (!generic.used) {
|
if (!generic.used) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const AttributeType input_type{profile.generic_input_types[index]};
|
const AttributeType input_type{runtime_info.generic_input_types[index]};
|
||||||
if (input_type == AttributeType::Disabled) {
|
if (input_type == AttributeType::Disabled) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1256,7 +1258,7 @@ void EmitContext::DefineOutputs(const IR::Program& program) {
|
||||||
if (info.stores_position || stage == Stage::VertexB) {
|
if (info.stores_position || stage == Stage::VertexB) {
|
||||||
output_position = DefineOutput(*this, F32[4], invocations, spv::BuiltIn::Position);
|
output_position = DefineOutput(*this, F32[4], invocations, spv::BuiltIn::Position);
|
||||||
}
|
}
|
||||||
if (info.stores_point_size || profile.fixed_state_point_size) {
|
if (info.stores_point_size || runtime_info.fixed_state_point_size) {
|
||||||
if (stage == Stage::Fragment) {
|
if (stage == Stage::Fragment) {
|
||||||
throw NotImplementedException("Storing PointSize in fragment stage");
|
throw NotImplementedException("Storing PointSize in fragment stage");
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,7 +103,8 @@ struct GenericElementInfo {
|
||||||
|
|
||||||
class EmitContext final : public Sirit::Module {
|
class EmitContext final : public Sirit::Module {
|
||||||
public:
|
public:
|
||||||
explicit EmitContext(const Profile& profile, IR::Program& program, Bindings& binding);
|
explicit EmitContext(const Profile& profile, const RuntimeInfo& runtime_info,
|
||||||
|
IR::Program& program, Bindings& binding);
|
||||||
~EmitContext();
|
~EmitContext();
|
||||||
|
|
||||||
[[nodiscard]] Id Def(const IR::Value& value);
|
[[nodiscard]] Id Def(const IR::Value& value);
|
||||||
|
@ -150,6 +151,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
const Profile& profile;
|
const Profile& profile;
|
||||||
|
const RuntimeInfo& runtime_info;
|
||||||
Stage stage{};
|
Stage stage{};
|
||||||
|
|
||||||
Id void_id{};
|
Id void_id{};
|
||||||
|
|
|
@ -226,16 +226,17 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) {
|
||||||
case Stage::TessellationEval:
|
case Stage::TessellationEval:
|
||||||
execution_model = spv::ExecutionModel::TessellationEvaluation;
|
execution_model = spv::ExecutionModel::TessellationEvaluation;
|
||||||
ctx.AddCapability(spv::Capability::Tessellation);
|
ctx.AddCapability(spv::Capability::Tessellation);
|
||||||
ctx.AddExecutionMode(main, ExecutionMode(ctx.profile.tess_primitive));
|
ctx.AddExecutionMode(main, ExecutionMode(ctx.runtime_info.tess_primitive));
|
||||||
ctx.AddExecutionMode(main, ExecutionMode(ctx.profile.tess_spacing));
|
ctx.AddExecutionMode(main, ExecutionMode(ctx.runtime_info.tess_spacing));
|
||||||
ctx.AddExecutionMode(main, ctx.profile.tess_clockwise ? spv::ExecutionMode::VertexOrderCw
|
ctx.AddExecutionMode(main, ctx.runtime_info.tess_clockwise
|
||||||
: spv::ExecutionMode::VertexOrderCcw);
|
? spv::ExecutionMode::VertexOrderCw
|
||||||
|
: spv::ExecutionMode::VertexOrderCcw);
|
||||||
break;
|
break;
|
||||||
case Stage::Geometry:
|
case Stage::Geometry:
|
||||||
execution_model = spv::ExecutionModel::Geometry;
|
execution_model = spv::ExecutionModel::Geometry;
|
||||||
ctx.AddCapability(spv::Capability::Geometry);
|
ctx.AddCapability(spv::Capability::Geometry);
|
||||||
ctx.AddCapability(spv::Capability::GeometryStreams);
|
ctx.AddCapability(spv::Capability::GeometryStreams);
|
||||||
switch (ctx.profile.input_topology) {
|
switch (ctx.runtime_info.input_topology) {
|
||||||
case InputTopology::Points:
|
case InputTopology::Points:
|
||||||
ctx.AddExecutionMode(main, spv::ExecutionMode::InputPoints);
|
ctx.AddExecutionMode(main, spv::ExecutionMode::InputPoints);
|
||||||
break;
|
break;
|
||||||
|
@ -279,7 +280,7 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) {
|
||||||
if (program.info.stores_frag_depth) {
|
if (program.info.stores_frag_depth) {
|
||||||
ctx.AddExecutionMode(main, spv::ExecutionMode::DepthReplacing);
|
ctx.AddExecutionMode(main, spv::ExecutionMode::DepthReplacing);
|
||||||
}
|
}
|
||||||
if (ctx.profile.force_early_z) {
|
if (ctx.runtime_info.force_early_z) {
|
||||||
ctx.AddExecutionMode(main, spv::ExecutionMode::EarlyFragmentTests);
|
ctx.AddExecutionMode(main, spv::ExecutionMode::EarlyFragmentTests);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -402,7 +403,7 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct
|
||||||
if (info.uses_sample_id) {
|
if (info.uses_sample_id) {
|
||||||
ctx.AddCapability(spv::Capability::SampleRateShading);
|
ctx.AddCapability(spv::Capability::SampleRateShading);
|
||||||
}
|
}
|
||||||
if (!ctx.profile.xfb_varyings.empty()) {
|
if (!ctx.runtime_info.xfb_varyings.empty()) {
|
||||||
ctx.AddCapability(spv::Capability::TransformFeedback);
|
ctx.AddCapability(spv::Capability::TransformFeedback);
|
||||||
}
|
}
|
||||||
if (info.uses_derivatives) {
|
if (info.uses_derivatives) {
|
||||||
|
@ -433,8 +434,9 @@ void PatchPhiNodes(IR::Program& program, EmitContext& ctx) {
|
||||||
}
|
}
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
std::vector<u32> EmitSPIRV(const Profile& profile, IR::Program& program, Bindings& binding) {
|
std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info,
|
||||||
EmitContext ctx{profile, program, binding};
|
IR::Program& program, Bindings& bindings) {
|
||||||
|
EmitContext ctx{profile, runtime_info, program, bindings};
|
||||||
const Id main{DefineMain(ctx, program)};
|
const Id main{DefineMain(ctx, program)};
|
||||||
DefineEntryPoint(program, ctx, main);
|
DefineEntryPoint(program, ctx, main);
|
||||||
if (profile.support_float_controls) {
|
if (profile.support_float_controls) {
|
||||||
|
|
|
@ -16,12 +16,12 @@
|
||||||
|
|
||||||
namespace Shader::Backend::SPIRV {
|
namespace Shader::Backend::SPIRV {
|
||||||
|
|
||||||
[[nodiscard]] std::vector<u32> EmitSPIRV(const Profile& profile, IR::Program& program,
|
[[nodiscard]] std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info,
|
||||||
Bindings& binding);
|
IR::Program& program, Bindings& bindings);
|
||||||
|
|
||||||
[[nodiscard]] inline std::vector<u32> EmitSPIRV(const Profile& profile, IR::Program& program) {
|
[[nodiscard]] inline std::vector<u32> EmitSPIRV(const Profile& profile, IR::Program& program) {
|
||||||
Bindings binding;
|
Bindings binding;
|
||||||
return EmitSPIRV(profile, program, binding);
|
return EmitSPIRV(profile, {}, program, binding);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Shader::Backend::SPIRV
|
} // namespace Shader::Backend::SPIRV
|
||||||
|
|
|
@ -17,7 +17,7 @@ struct AttrInfo {
|
||||||
};
|
};
|
||||||
|
|
||||||
std::optional<AttrInfo> AttrTypes(EmitContext& ctx, u32 index) {
|
std::optional<AttrInfo> AttrTypes(EmitContext& ctx, u32 index) {
|
||||||
const AttributeType type{ctx.profile.generic_input_types.at(index)};
|
const AttributeType type{ctx.runtime_info.generic_input_types.at(index)};
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case AttributeType::Float:
|
case AttributeType::Float:
|
||||||
return AttrInfo{ctx.input_f32, ctx.F32[1], false};
|
return AttrInfo{ctx.input_f32, ctx.F32[1], false};
|
||||||
|
@ -468,7 +468,7 @@ Id EmitIsHelperInvocation(EmitContext& ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Id EmitYDirection(EmitContext& ctx) {
|
Id EmitYDirection(EmitContext& ctx) {
|
||||||
return ctx.Const(ctx.profile.y_negate ? -1.0f : 1.0f);
|
return ctx.Const(ctx.runtime_info.y_negate ? -1.0f : 1.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
Id EmitLoadLocal(EmitContext& ctx, Id word_offset) {
|
Id EmitLoadLocal(EmitContext& ctx, Id word_offset) {
|
||||||
|
|
|
@ -18,8 +18,8 @@ void ConvertDepthMode(EmitContext& ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetFixedPipelinePointSize(EmitContext& ctx) {
|
void SetFixedPipelinePointSize(EmitContext& ctx) {
|
||||||
if (ctx.profile.fixed_state_point_size) {
|
if (ctx.runtime_info.fixed_state_point_size) {
|
||||||
const float point_size{*ctx.profile.fixed_state_point_size};
|
const float point_size{*ctx.runtime_info.fixed_state_point_size};
|
||||||
ctx.OpStore(ctx.output_point_size, ctx.Const(point_size));
|
ctx.OpStore(ctx.output_point_size, ctx.Const(point_size));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,10 @@ Id ComparisonFunction(EmitContext& ctx, CompareFunction comparison, Id operand_1
|
||||||
}
|
}
|
||||||
|
|
||||||
void AlphaTest(EmitContext& ctx) {
|
void AlphaTest(EmitContext& ctx) {
|
||||||
const auto comparison{*ctx.profile.alpha_test_func};
|
if (!ctx.runtime_info.alpha_test_func) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto comparison{*ctx.runtime_info.alpha_test_func};
|
||||||
if (comparison == CompareFunction::Always) {
|
if (comparison == CompareFunction::Always) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -76,7 +79,7 @@ void AlphaTest(EmitContext& ctx) {
|
||||||
|
|
||||||
const Id true_label{ctx.OpLabel()};
|
const Id true_label{ctx.OpLabel()};
|
||||||
const Id discard_label{ctx.OpLabel()};
|
const Id discard_label{ctx.OpLabel()};
|
||||||
const Id alpha_reference{ctx.Const(ctx.profile.alpha_test_reference)};
|
const Id alpha_reference{ctx.Const(ctx.runtime_info.alpha_test_reference)};
|
||||||
const Id condition{ComparisonFunction(ctx, comparison, alpha, alpha_reference)};
|
const Id condition{ComparisonFunction(ctx, comparison, alpha, alpha_reference)};
|
||||||
|
|
||||||
ctx.OpSelectionMerge(true_label, spv::SelectionControlMask::MaskNone);
|
ctx.OpSelectionMerge(true_label, spv::SelectionControlMask::MaskNone);
|
||||||
|
@ -113,7 +116,7 @@ void EmitPrologue(EmitContext& ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitEpilogue(EmitContext& ctx) {
|
void EmitEpilogue(EmitContext& ctx) {
|
||||||
if (ctx.stage == Stage::VertexB && ctx.profile.convert_depth_mode) {
|
if (ctx.stage == Stage::VertexB && ctx.runtime_info.convert_depth_mode) {
|
||||||
ConvertDepthMode(ctx);
|
ConvertDepthMode(ctx);
|
||||||
}
|
}
|
||||||
if (ctx.stage == Stage::Fragment) {
|
if (ctx.stage == Stage::Fragment) {
|
||||||
|
@ -122,7 +125,7 @@ void EmitEpilogue(EmitContext& ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream) {
|
void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream) {
|
||||||
if (ctx.profile.convert_depth_mode) {
|
if (ctx.runtime_info.convert_depth_mode) {
|
||||||
ConvertDepthMode(ctx);
|
ConvertDepthMode(ctx);
|
||||||
}
|
}
|
||||||
if (stream.IsImmediate()) {
|
if (stream.IsImmediate()) {
|
||||||
|
|
|
@ -81,19 +81,22 @@ struct Profile {
|
||||||
bool support_viewport_mask{};
|
bool support_viewport_mask{};
|
||||||
bool support_typeless_image_loads{};
|
bool support_typeless_image_loads{};
|
||||||
bool support_demote_to_helper_invocation{};
|
bool support_demote_to_helper_invocation{};
|
||||||
bool warp_size_potentially_larger_than_guest{};
|
|
||||||
bool support_int64_atomics{};
|
bool support_int64_atomics{};
|
||||||
|
|
||||||
|
bool warp_size_potentially_larger_than_guest{};
|
||||||
bool lower_left_origin_mode{};
|
bool lower_left_origin_mode{};
|
||||||
|
|
||||||
// FClamp is broken and OpFMax + OpFMin should be used instead
|
/// OpFClamp is broken and OpFMax + OpFMin should be used instead
|
||||||
bool has_broken_spirv_clamp{};
|
bool has_broken_spirv_clamp{};
|
||||||
// Offset image operands with an unsigned type do not work
|
/// Offset image operands with an unsigned type do not work
|
||||||
bool has_broken_unsigned_image_offsets{};
|
bool has_broken_unsigned_image_offsets{};
|
||||||
// Signed instructions with unsigned data types are misinterpreted
|
/// Signed instructions with unsigned data types are misinterpreted
|
||||||
bool has_broken_signed_operations{};
|
bool has_broken_signed_operations{};
|
||||||
// Ignores SPIR-V ordered vs unordered using GLSL semantics
|
/// Ignores SPIR-V ordered vs unordered using GLSL semantics
|
||||||
bool ignore_nan_fp_comparisons{};
|
bool ignore_nan_fp_comparisons{};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RuntimeInfo {
|
||||||
std::array<AttributeType, 32> generic_input_types{};
|
std::array<AttributeType, 32> generic_input_types{};
|
||||||
bool convert_depth_mode{};
|
bool convert_depth_mode{};
|
||||||
bool force_early_z{};
|
bool force_early_z{};
|
||||||
|
|
|
@ -61,33 +61,15 @@ const Shader::Profile profile{
|
||||||
.support_viewport_mask = true,
|
.support_viewport_mask = true,
|
||||||
.support_typeless_image_loads = true,
|
.support_typeless_image_loads = true,
|
||||||
.support_demote_to_helper_invocation = false,
|
.support_demote_to_helper_invocation = false,
|
||||||
.warp_size_potentially_larger_than_guest = true,
|
|
||||||
.support_int64_atomics = false,
|
.support_int64_atomics = false,
|
||||||
|
|
||||||
|
.warp_size_potentially_larger_than_guest = true,
|
||||||
.lower_left_origin_mode = true,
|
.lower_left_origin_mode = true,
|
||||||
|
|
||||||
.has_broken_spirv_clamp = true,
|
.has_broken_spirv_clamp = true,
|
||||||
.has_broken_unsigned_image_offsets = true,
|
.has_broken_unsigned_image_offsets = true,
|
||||||
.has_broken_signed_operations = true,
|
.has_broken_signed_operations = true,
|
||||||
.ignore_nan_fp_comparisons = true,
|
.ignore_nan_fp_comparisons = true,
|
||||||
|
|
||||||
.generic_input_types = {},
|
|
||||||
.convert_depth_mode = false,
|
|
||||||
.force_early_z = false,
|
|
||||||
|
|
||||||
.tess_primitive = {},
|
|
||||||
.tess_spacing = {},
|
|
||||||
.tess_clockwise = false,
|
|
||||||
|
|
||||||
.input_topology = Shader::InputTopology::Triangles,
|
|
||||||
|
|
||||||
.fixed_state_point_size = std::nullopt,
|
|
||||||
|
|
||||||
.alpha_test_func = Shader::CompareFunction::Always,
|
|
||||||
.alpha_test_reference = 0.0f,
|
|
||||||
|
|
||||||
.y_negate = false,
|
|
||||||
|
|
||||||
.xfb_varyings = {},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
using Shader::Backend::GLASM::EmitGLASM;
|
using Shader::Backend::GLASM::EmitGLASM;
|
||||||
|
@ -302,10 +284,10 @@ std::unique_ptr<GraphicsProgram> ShaderCache::CreateGraphicsProgram(
|
||||||
const size_t stage_index{index - 1};
|
const size_t stage_index{index - 1};
|
||||||
infos[stage_index] = &program.info;
|
infos[stage_index] = &program.info;
|
||||||
if (device.UseAssemblyShaders()) {
|
if (device.UseAssemblyShaders()) {
|
||||||
const std::string code{EmitGLASM(profile, program, binding)};
|
const std::string code{EmitGLASM(profile, {}, program, binding)};
|
||||||
assembly_programs[stage_index] = CompileProgram(code, AssemblyStage(stage_index));
|
assembly_programs[stage_index] = CompileProgram(code, AssemblyStage(stage_index));
|
||||||
} else {
|
} else {
|
||||||
const std::vector<u32> code{EmitSPIRV(profile, program, binding)};
|
const std::vector<u32> code{EmitSPIRV(profile, {}, program, binding)};
|
||||||
AddShader(Stage(stage_index), source_program.handle, code);
|
AddShader(Stage(stage_index), source_program.handle, code);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,6 +89,208 @@ Shader::CompareFunction MaxwellToCompareFunction(Maxwell::ComparisonOp compariso
|
||||||
UNIMPLEMENTED_MSG("Unimplemented comparison op={}", comparison);
|
UNIMPLEMENTED_MSG("Unimplemented comparison op={}", comparison);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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:
|
||||||
|
case Maxwell::VertexAttribute::Type::UnsignedScaled:
|
||||||
|
case Maxwell::VertexAttribute::Type::SignedScaled:
|
||||||
|
case Maxwell::VertexAttribute::Type::Float:
|
||||||
|
return Shader::AttributeType::Float;
|
||||||
|
case Maxwell::VertexAttribute::Type::SignedInt:
|
||||||
|
return Shader::AttributeType::SignedInt;
|
||||||
|
case Maxwell::VertexAttribute::Type::UnsignedInt:
|
||||||
|
return Shader::AttributeType::UnsignedInt;
|
||||||
|
}
|
||||||
|
return Shader::AttributeType::Float;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Shader::TransformFeedbackVarying> MakeTransformFeedbackVaryings(
|
||||||
|
const GraphicsPipelineCacheKey& key) {
|
||||||
|
static constexpr std::array VECTORS{
|
||||||
|
28, // gl_Position
|
||||||
|
32, // Generic 0
|
||||||
|
36, // Generic 1
|
||||||
|
40, // Generic 2
|
||||||
|
44, // Generic 3
|
||||||
|
48, // Generic 4
|
||||||
|
52, // Generic 5
|
||||||
|
56, // Generic 6
|
||||||
|
60, // Generic 7
|
||||||
|
64, // Generic 8
|
||||||
|
68, // Generic 9
|
||||||
|
72, // Generic 10
|
||||||
|
76, // Generic 11
|
||||||
|
80, // Generic 12
|
||||||
|
84, // Generic 13
|
||||||
|
88, // Generic 14
|
||||||
|
92, // Generic 15
|
||||||
|
96, // Generic 16
|
||||||
|
100, // Generic 17
|
||||||
|
104, // Generic 18
|
||||||
|
108, // Generic 19
|
||||||
|
112, // Generic 20
|
||||||
|
116, // Generic 21
|
||||||
|
120, // Generic 22
|
||||||
|
124, // Generic 23
|
||||||
|
128, // Generic 24
|
||||||
|
132, // Generic 25
|
||||||
|
136, // Generic 26
|
||||||
|
140, // Generic 27
|
||||||
|
144, // Generic 28
|
||||||
|
148, // Generic 29
|
||||||
|
152, // Generic 30
|
||||||
|
156, // Generic 31
|
||||||
|
160, // gl_FrontColor
|
||||||
|
164, // gl_FrontSecondaryColor
|
||||||
|
160, // gl_BackColor
|
||||||
|
164, // gl_BackSecondaryColor
|
||||||
|
192, // gl_TexCoord[0]
|
||||||
|
196, // gl_TexCoord[1]
|
||||||
|
200, // gl_TexCoord[2]
|
||||||
|
204, // gl_TexCoord[3]
|
||||||
|
208, // gl_TexCoord[4]
|
||||||
|
212, // gl_TexCoord[5]
|
||||||
|
216, // gl_TexCoord[6]
|
||||||
|
220, // gl_TexCoord[7]
|
||||||
|
};
|
||||||
|
std::vector<Shader::TransformFeedbackVarying> xfb(256);
|
||||||
|
for (size_t buffer = 0; buffer < Maxwell::NumTransformFeedbackBuffers; ++buffer) {
|
||||||
|
const auto& locations = key.state.xfb_state.varyings[buffer];
|
||||||
|
const auto& layout = key.state.xfb_state.layouts[buffer];
|
||||||
|
const u32 varying_count = layout.varying_count;
|
||||||
|
u32 highest = 0;
|
||||||
|
for (u32 offset = 0; offset < varying_count; ++offset) {
|
||||||
|
const u32 base_offset = offset;
|
||||||
|
const u8 location = locations[offset];
|
||||||
|
|
||||||
|
Shader::TransformFeedbackVarying varying;
|
||||||
|
varying.buffer = layout.stream;
|
||||||
|
varying.stride = layout.stride;
|
||||||
|
varying.offset = offset * 4;
|
||||||
|
varying.components = 1;
|
||||||
|
|
||||||
|
if (std::ranges::find(VECTORS, Common::AlignDown(location, 4)) != VECTORS.end()) {
|
||||||
|
UNIMPLEMENTED_IF_MSG(location % 4 != 0, "Unaligned TFB");
|
||||||
|
|
||||||
|
const u8 base_index = location / 4;
|
||||||
|
while (offset + 1 < varying_count && base_index == locations[offset + 1] / 4) {
|
||||||
|
++offset;
|
||||||
|
++varying.components;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xfb[location] = varying;
|
||||||
|
highest = std::max(highest, (base_offset + varying.components) * 4);
|
||||||
|
}
|
||||||
|
UNIMPLEMENTED_IF(highest != layout.stride);
|
||||||
|
}
|
||||||
|
return xfb;
|
||||||
|
}
|
||||||
|
|
||||||
|
Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineCacheKey& key,
|
||||||
|
const Shader::IR::Program& program) {
|
||||||
|
Shader::RuntimeInfo info;
|
||||||
|
|
||||||
|
const Shader::Stage stage{program.stage};
|
||||||
|
const bool has_geometry{key.unique_hashes[4] != 0};
|
||||||
|
const bool gl_ndc{key.state.ndc_minus_one_to_one != 0};
|
||||||
|
const float point_size{Common::BitCast<float>(key.state.point_size)};
|
||||||
|
switch (stage) {
|
||||||
|
case Shader::Stage::VertexB:
|
||||||
|
if (!has_geometry) {
|
||||||
|
if (key.state.topology == Maxwell::PrimitiveTopology::Points) {
|
||||||
|
info.fixed_state_point_size = point_size;
|
||||||
|
}
|
||||||
|
if (key.state.xfb_enabled != 0) {
|
||||||
|
info.xfb_varyings = MakeTransformFeedbackVaryings(key);
|
||||||
|
}
|
||||||
|
info.convert_depth_mode = gl_ndc;
|
||||||
|
}
|
||||||
|
std::ranges::transform(key.state.attributes, info.generic_input_types.begin(),
|
||||||
|
&CastAttributeType);
|
||||||
|
break;
|
||||||
|
case Shader::Stage::TessellationEval:
|
||||||
|
// We have to flip tessellation clockwise for some reason...
|
||||||
|
info.tess_clockwise = key.state.tessellation_clockwise == 0;
|
||||||
|
info.tess_primitive = [&key] {
|
||||||
|
const u32 raw{key.state.tessellation_primitive.Value()};
|
||||||
|
switch (static_cast<Maxwell::TessellationPrimitive>(raw)) {
|
||||||
|
case Maxwell::TessellationPrimitive::Isolines:
|
||||||
|
return Shader::TessPrimitive::Isolines;
|
||||||
|
case Maxwell::TessellationPrimitive::Triangles:
|
||||||
|
return Shader::TessPrimitive::Triangles;
|
||||||
|
case Maxwell::TessellationPrimitive::Quads:
|
||||||
|
return Shader::TessPrimitive::Quads;
|
||||||
|
}
|
||||||
|
UNREACHABLE();
|
||||||
|
return Shader::TessPrimitive::Triangles;
|
||||||
|
}();
|
||||||
|
info.tess_spacing = [&] {
|
||||||
|
const u32 raw{key.state.tessellation_spacing};
|
||||||
|
switch (static_cast<Maxwell::TessellationSpacing>(raw)) {
|
||||||
|
case Maxwell::TessellationSpacing::Equal:
|
||||||
|
return Shader::TessSpacing::Equal;
|
||||||
|
case Maxwell::TessellationSpacing::FractionalOdd:
|
||||||
|
return Shader::TessSpacing::FractionalOdd;
|
||||||
|
case Maxwell::TessellationSpacing::FractionalEven:
|
||||||
|
return Shader::TessSpacing::FractionalEven;
|
||||||
|
}
|
||||||
|
UNREACHABLE();
|
||||||
|
return Shader::TessSpacing::Equal;
|
||||||
|
}();
|
||||||
|
break;
|
||||||
|
case Shader::Stage::Geometry:
|
||||||
|
if (program.output_topology == Shader::OutputTopology::PointList) {
|
||||||
|
info.fixed_state_point_size = point_size;
|
||||||
|
}
|
||||||
|
if (key.state.xfb_enabled != 0) {
|
||||||
|
info.xfb_varyings = MakeTransformFeedbackVaryings(key);
|
||||||
|
}
|
||||||
|
info.convert_depth_mode = gl_ndc;
|
||||||
|
break;
|
||||||
|
case Shader::Stage::Fragment:
|
||||||
|
info.alpha_test_func = MaxwellToCompareFunction(
|
||||||
|
key.state.UnpackComparisonOp(key.state.alpha_test_func.Value()));
|
||||||
|
info.alpha_test_reference = Common::BitCast<float>(key.state.alpha_test_ref);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch (key.state.topology) {
|
||||||
|
case Maxwell::PrimitiveTopology::Points:
|
||||||
|
info.input_topology = Shader::InputTopology::Points;
|
||||||
|
break;
|
||||||
|
case Maxwell::PrimitiveTopology::Lines:
|
||||||
|
case Maxwell::PrimitiveTopology::LineLoop:
|
||||||
|
case Maxwell::PrimitiveTopology::LineStrip:
|
||||||
|
info.input_topology = Shader::InputTopology::Lines;
|
||||||
|
break;
|
||||||
|
case Maxwell::PrimitiveTopology::Triangles:
|
||||||
|
case Maxwell::PrimitiveTopology::TriangleStrip:
|
||||||
|
case Maxwell::PrimitiveTopology::TriangleFan:
|
||||||
|
case Maxwell::PrimitiveTopology::Quads:
|
||||||
|
case Maxwell::PrimitiveTopology::QuadStrip:
|
||||||
|
case Maxwell::PrimitiveTopology::Polygon:
|
||||||
|
case Maxwell::PrimitiveTopology::Patches:
|
||||||
|
info.input_topology = Shader::InputTopology::Triangles;
|
||||||
|
break;
|
||||||
|
case Maxwell::PrimitiveTopology::LinesAdjacency:
|
||||||
|
case Maxwell::PrimitiveTopology::LineStripAdjacency:
|
||||||
|
info.input_topology = Shader::InputTopology::LinesAdjacency;
|
||||||
|
break;
|
||||||
|
case Maxwell::PrimitiveTopology::TrianglesAdjacency:
|
||||||
|
case Maxwell::PrimitiveTopology::TriangleStripAdjacency:
|
||||||
|
info.input_topology = Shader::InputTopology::TrianglesAdjacency;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
info.force_early_z = key.state.early_z != 0;
|
||||||
|
info.y_negate = key.state.y_negate != 0;
|
||||||
|
return info;
|
||||||
|
}
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
size_t ComputePipelineCacheKey::Hash() const noexcept {
|
size_t ComputePipelineCacheKey::Hash() const noexcept {
|
||||||
|
@ -124,7 +326,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, Tegra::Engines::Maxw
|
||||||
serialization_thread(1, "yuzu:PipelineSerialization") {
|
serialization_thread(1, "yuzu:PipelineSerialization") {
|
||||||
const auto& float_control{device.FloatControlProperties()};
|
const auto& float_control{device.FloatControlProperties()};
|
||||||
const VkDriverIdKHR driver_id{device.GetDriverID()};
|
const VkDriverIdKHR driver_id{device.GetDriverID()};
|
||||||
base_profile = Shader::Profile{
|
profile = Shader::Profile{
|
||||||
.supported_spirv = device.IsKhrSpirv1_4Supported() ? 0x00010400U : 0x00010000U,
|
.supported_spirv = device.IsKhrSpirv1_4Supported() ? 0x00010400U : 0x00010000U,
|
||||||
.unified_descriptor_binding = true,
|
.unified_descriptor_binding = true,
|
||||||
.support_descriptor_aliasing = true,
|
.support_descriptor_aliasing = true,
|
||||||
|
@ -153,14 +355,10 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, Tegra::Engines::Maxw
|
||||||
.support_viewport_mask = device.IsNvViewportArray2Supported(),
|
.support_viewport_mask = device.IsNvViewportArray2Supported(),
|
||||||
.support_typeless_image_loads = device.IsFormatlessImageLoadSupported(),
|
.support_typeless_image_loads = device.IsFormatlessImageLoadSupported(),
|
||||||
.support_demote_to_helper_invocation = true,
|
.support_demote_to_helper_invocation = true,
|
||||||
.warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(),
|
|
||||||
.support_int64_atomics = device.IsExtShaderAtomicInt64Supported(),
|
.support_int64_atomics = device.IsExtShaderAtomicInt64Supported(),
|
||||||
|
.warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(),
|
||||||
.has_broken_spirv_clamp = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS_KHR,
|
.has_broken_spirv_clamp = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS_KHR,
|
||||||
.has_broken_unsigned_image_offsets = false,
|
.has_broken_unsigned_image_offsets = false,
|
||||||
.generic_input_types{},
|
|
||||||
.fixed_state_point_size{},
|
|
||||||
.alpha_test_func{},
|
|
||||||
.xfb_varyings{},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -329,8 +527,8 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
|
||||||
const size_t stage_index{index - 1};
|
const size_t stage_index{index - 1};
|
||||||
infos[stage_index] = &program.info;
|
infos[stage_index] = &program.info;
|
||||||
|
|
||||||
const Shader::Profile profile{MakeProfile(key, program)};
|
const Shader::RuntimeInfo runtime_info{MakeRuntimeInfo(key, program)};
|
||||||
const std::vector<u32> code{EmitSPIRV(profile, program, binding)};
|
const std::vector<u32> code{EmitSPIRV(profile, runtime_info, program, binding)};
|
||||||
device.SaveShader(code);
|
device.SaveShader(code);
|
||||||
modules[stage_index] = BuildShader(device, code);
|
modules[stage_index] = BuildShader(device, code);
|
||||||
if (device.HasDebuggingToolAttached()) {
|
if (device.HasDebuggingToolAttached()) {
|
||||||
|
@ -391,7 +589,7 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
|
||||||
|
|
||||||
Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
|
Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
|
||||||
Shader::IR::Program program{TranslateProgram(pools.inst, pools.block, env, cfg)};
|
Shader::IR::Program program{TranslateProgram(pools.inst, pools.block, env, cfg)};
|
||||||
const std::vector<u32> code{EmitSPIRV(base_profile, program)};
|
const std::vector<u32> code{EmitSPIRV(profile, program)};
|
||||||
device.SaveShader(code);
|
device.SaveShader(code);
|
||||||
vk::ShaderModule spv_module{BuildShader(device, code)};
|
vk::ShaderModule spv_module{BuildShader(device, code)};
|
||||||
if (device.HasDebuggingToolAttached()) {
|
if (device.HasDebuggingToolAttached()) {
|
||||||
|
@ -403,206 +601,4 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
|
||||||
thread_worker, program.info, std::move(spv_module));
|
thread_worker, program.info, std::move(spv_module));
|
||||||
}
|
}
|
||||||
|
|
||||||
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:
|
|
||||||
case Maxwell::VertexAttribute::Type::UnsignedScaled:
|
|
||||||
case Maxwell::VertexAttribute::Type::SignedScaled:
|
|
||||||
case Maxwell::VertexAttribute::Type::Float:
|
|
||||||
return Shader::AttributeType::Float;
|
|
||||||
case Maxwell::VertexAttribute::Type::SignedInt:
|
|
||||||
return Shader::AttributeType::SignedInt;
|
|
||||||
case Maxwell::VertexAttribute::Type::UnsignedInt:
|
|
||||||
return Shader::AttributeType::UnsignedInt;
|
|
||||||
}
|
|
||||||
return Shader::AttributeType::Float;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::vector<Shader::TransformFeedbackVarying> MakeTransformFeedbackVaryings(
|
|
||||||
const GraphicsPipelineCacheKey& key) {
|
|
||||||
static constexpr std::array VECTORS{
|
|
||||||
28, // gl_Position
|
|
||||||
32, // Generic 0
|
|
||||||
36, // Generic 1
|
|
||||||
40, // Generic 2
|
|
||||||
44, // Generic 3
|
|
||||||
48, // Generic 4
|
|
||||||
52, // Generic 5
|
|
||||||
56, // Generic 6
|
|
||||||
60, // Generic 7
|
|
||||||
64, // Generic 8
|
|
||||||
68, // Generic 9
|
|
||||||
72, // Generic 10
|
|
||||||
76, // Generic 11
|
|
||||||
80, // Generic 12
|
|
||||||
84, // Generic 13
|
|
||||||
88, // Generic 14
|
|
||||||
92, // Generic 15
|
|
||||||
96, // Generic 16
|
|
||||||
100, // Generic 17
|
|
||||||
104, // Generic 18
|
|
||||||
108, // Generic 19
|
|
||||||
112, // Generic 20
|
|
||||||
116, // Generic 21
|
|
||||||
120, // Generic 22
|
|
||||||
124, // Generic 23
|
|
||||||
128, // Generic 24
|
|
||||||
132, // Generic 25
|
|
||||||
136, // Generic 26
|
|
||||||
140, // Generic 27
|
|
||||||
144, // Generic 28
|
|
||||||
148, // Generic 29
|
|
||||||
152, // Generic 30
|
|
||||||
156, // Generic 31
|
|
||||||
160, // gl_FrontColor
|
|
||||||
164, // gl_FrontSecondaryColor
|
|
||||||
160, // gl_BackColor
|
|
||||||
164, // gl_BackSecondaryColor
|
|
||||||
192, // gl_TexCoord[0]
|
|
||||||
196, // gl_TexCoord[1]
|
|
||||||
200, // gl_TexCoord[2]
|
|
||||||
204, // gl_TexCoord[3]
|
|
||||||
208, // gl_TexCoord[4]
|
|
||||||
212, // gl_TexCoord[5]
|
|
||||||
216, // gl_TexCoord[6]
|
|
||||||
220, // gl_TexCoord[7]
|
|
||||||
};
|
|
||||||
std::vector<Shader::TransformFeedbackVarying> xfb(256);
|
|
||||||
for (size_t buffer = 0; buffer < Maxwell::NumTransformFeedbackBuffers; ++buffer) {
|
|
||||||
const auto& locations = key.state.xfb_state.varyings[buffer];
|
|
||||||
const auto& layout = key.state.xfb_state.layouts[buffer];
|
|
||||||
const u32 varying_count = layout.varying_count;
|
|
||||||
u32 highest = 0;
|
|
||||||
for (u32 offset = 0; offset < varying_count; ++offset) {
|
|
||||||
const u32 base_offset = offset;
|
|
||||||
const u8 location = locations[offset];
|
|
||||||
|
|
||||||
Shader::TransformFeedbackVarying varying;
|
|
||||||
varying.buffer = layout.stream;
|
|
||||||
varying.stride = layout.stride;
|
|
||||||
varying.offset = offset * 4;
|
|
||||||
varying.components = 1;
|
|
||||||
|
|
||||||
if (std::ranges::find(VECTORS, Common::AlignDown(location, 4)) != VECTORS.end()) {
|
|
||||||
UNIMPLEMENTED_IF_MSG(location % 4 != 0, "Unaligned TFB");
|
|
||||||
|
|
||||||
const u8 base_index = location / 4;
|
|
||||||
while (offset + 1 < varying_count && base_index == locations[offset + 1] / 4) {
|
|
||||||
++offset;
|
|
||||||
++varying.components;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
xfb[location] = varying;
|
|
||||||
highest = std::max(highest, (base_offset + varying.components) * 4);
|
|
||||||
}
|
|
||||||
UNIMPLEMENTED_IF(highest != layout.stride);
|
|
||||||
}
|
|
||||||
return xfb;
|
|
||||||
}
|
|
||||||
|
|
||||||
Shader::Profile PipelineCache::MakeProfile(const GraphicsPipelineCacheKey& key,
|
|
||||||
const Shader::IR::Program& program) {
|
|
||||||
Shader::Profile profile{base_profile};
|
|
||||||
|
|
||||||
const Shader::Stage stage{program.stage};
|
|
||||||
const bool has_geometry{key.unique_hashes[4] != 0};
|
|
||||||
const bool gl_ndc{key.state.ndc_minus_one_to_one != 0};
|
|
||||||
const float point_size{Common::BitCast<float>(key.state.point_size)};
|
|
||||||
switch (stage) {
|
|
||||||
case Shader::Stage::VertexB:
|
|
||||||
if (!has_geometry) {
|
|
||||||
if (key.state.topology == Maxwell::PrimitiveTopology::Points) {
|
|
||||||
profile.fixed_state_point_size = point_size;
|
|
||||||
}
|
|
||||||
if (key.state.xfb_enabled != 0) {
|
|
||||||
profile.xfb_varyings = MakeTransformFeedbackVaryings(key);
|
|
||||||
}
|
|
||||||
profile.convert_depth_mode = gl_ndc;
|
|
||||||
}
|
|
||||||
std::ranges::transform(key.state.attributes, profile.generic_input_types.begin(),
|
|
||||||
&CastAttributeType);
|
|
||||||
break;
|
|
||||||
case Shader::Stage::TessellationEval:
|
|
||||||
// We have to flip tessellation clockwise for some reason...
|
|
||||||
profile.tess_clockwise = key.state.tessellation_clockwise == 0;
|
|
||||||
profile.tess_primitive = [&key] {
|
|
||||||
const u32 raw{key.state.tessellation_primitive.Value()};
|
|
||||||
switch (static_cast<Maxwell::TessellationPrimitive>(raw)) {
|
|
||||||
case Maxwell::TessellationPrimitive::Isolines:
|
|
||||||
return Shader::TessPrimitive::Isolines;
|
|
||||||
case Maxwell::TessellationPrimitive::Triangles:
|
|
||||||
return Shader::TessPrimitive::Triangles;
|
|
||||||
case Maxwell::TessellationPrimitive::Quads:
|
|
||||||
return Shader::TessPrimitive::Quads;
|
|
||||||
}
|
|
||||||
UNREACHABLE();
|
|
||||||
return Shader::TessPrimitive::Triangles;
|
|
||||||
}();
|
|
||||||
profile.tess_spacing = [&] {
|
|
||||||
const u32 raw{key.state.tessellation_spacing};
|
|
||||||
switch (static_cast<Maxwell::TessellationSpacing>(raw)) {
|
|
||||||
case Maxwell::TessellationSpacing::Equal:
|
|
||||||
return Shader::TessSpacing::Equal;
|
|
||||||
case Maxwell::TessellationSpacing::FractionalOdd:
|
|
||||||
return Shader::TessSpacing::FractionalOdd;
|
|
||||||
case Maxwell::TessellationSpacing::FractionalEven:
|
|
||||||
return Shader::TessSpacing::FractionalEven;
|
|
||||||
}
|
|
||||||
UNREACHABLE();
|
|
||||||
return Shader::TessSpacing::Equal;
|
|
||||||
}();
|
|
||||||
break;
|
|
||||||
case Shader::Stage::Geometry:
|
|
||||||
if (program.output_topology == Shader::OutputTopology::PointList) {
|
|
||||||
profile.fixed_state_point_size = point_size;
|
|
||||||
}
|
|
||||||
if (key.state.xfb_enabled != 0) {
|
|
||||||
profile.xfb_varyings = MakeTransformFeedbackVaryings(key);
|
|
||||||
}
|
|
||||||
profile.convert_depth_mode = gl_ndc;
|
|
||||||
break;
|
|
||||||
case Shader::Stage::Fragment:
|
|
||||||
profile.alpha_test_func = MaxwellToCompareFunction(
|
|
||||||
key.state.UnpackComparisonOp(key.state.alpha_test_func.Value()));
|
|
||||||
profile.alpha_test_reference = Common::BitCast<float>(key.state.alpha_test_ref);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
switch (key.state.topology) {
|
|
||||||
case Maxwell::PrimitiveTopology::Points:
|
|
||||||
profile.input_topology = Shader::InputTopology::Points;
|
|
||||||
break;
|
|
||||||
case Maxwell::PrimitiveTopology::Lines:
|
|
||||||
case Maxwell::PrimitiveTopology::LineLoop:
|
|
||||||
case Maxwell::PrimitiveTopology::LineStrip:
|
|
||||||
profile.input_topology = Shader::InputTopology::Lines;
|
|
||||||
break;
|
|
||||||
case Maxwell::PrimitiveTopology::Triangles:
|
|
||||||
case Maxwell::PrimitiveTopology::TriangleStrip:
|
|
||||||
case Maxwell::PrimitiveTopology::TriangleFan:
|
|
||||||
case Maxwell::PrimitiveTopology::Quads:
|
|
||||||
case Maxwell::PrimitiveTopology::QuadStrip:
|
|
||||||
case Maxwell::PrimitiveTopology::Polygon:
|
|
||||||
case Maxwell::PrimitiveTopology::Patches:
|
|
||||||
profile.input_topology = Shader::InputTopology::Triangles;
|
|
||||||
break;
|
|
||||||
case Maxwell::PrimitiveTopology::LinesAdjacency:
|
|
||||||
case Maxwell::PrimitiveTopology::LineStripAdjacency:
|
|
||||||
profile.input_topology = Shader::InputTopology::LinesAdjacency;
|
|
||||||
break;
|
|
||||||
case Maxwell::PrimitiveTopology::TrianglesAdjacency:
|
|
||||||
case Maxwell::PrimitiveTopology::TriangleStripAdjacency:
|
|
||||||
profile.input_topology = Shader::InputTopology::TrianglesAdjacency;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
profile.force_early_z = key.state.early_z != 0;
|
|
||||||
profile.y_negate = key.state.y_negate != 0;
|
|
||||||
return profile;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
|
|
@ -129,9 +129,6 @@ private:
|
||||||
Shader::Environment& env,
|
Shader::Environment& env,
|
||||||
bool build_in_parallel);
|
bool build_in_parallel);
|
||||||
|
|
||||||
Shader::Profile MakeProfile(const GraphicsPipelineCacheKey& key,
|
|
||||||
const Shader::IR::Program& program);
|
|
||||||
|
|
||||||
const Device& device;
|
const Device& device;
|
||||||
VKScheduler& scheduler;
|
VKScheduler& scheduler;
|
||||||
DescriptorPool& descriptor_pool;
|
DescriptorPool& descriptor_pool;
|
||||||
|
@ -148,7 +145,7 @@ private:
|
||||||
|
|
||||||
ShaderPools main_pools;
|
ShaderPools main_pools;
|
||||||
|
|
||||||
Shader::Profile base_profile;
|
Shader::Profile profile;
|
||||||
std::filesystem::path pipeline_cache_filename;
|
std::filesystem::path pipeline_cache_filename;
|
||||||
|
|
||||||
Common::ThreadWorker workers;
|
Common::ThreadWorker workers;
|
||||||
|
|
Reference in New Issue