shader: Split profile and runtime information in separate structs
This commit is contained in:
parent
eb15667905
commit
9e7b6622c2
14 changed files with 300 additions and 308 deletions
|
@ -23,23 +23,25 @@ std::string_view InterpDecorator(Interpolation interp) {
|
|||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_)
|
||||
: info{program.info}, profile{profile_} {
|
||||
EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_,
|
||||
const RuntimeInfo& runtime_info_)
|
||||
: profile{profile_}, runtime_info{runtime_info_} {
|
||||
// FIXME: Temporary partial implementation
|
||||
const auto& info{program.info};
|
||||
u32 cbuf_index{};
|
||||
for (const auto& desc : program.info.constant_buffer_descriptors) {
|
||||
for (const auto& desc : info.constant_buffer_descriptors) {
|
||||
if (desc.count != 1) {
|
||||
throw NotImplementedException("Constant buffer descriptor array");
|
||||
}
|
||||
Add("CBUFFER c{}[]={{program.buffer[{}]}};", desc.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) {
|
||||
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);
|
||||
}
|
||||
stage = program.stage;
|
||||
|
@ -67,8 +69,8 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
|
|||
break;
|
||||
}
|
||||
const std::string_view attr_stage{stage == Stage::Fragment ? "fragment" : "vertex"};
|
||||
for (size_t index = 0; index < program.info.input_generics.size(); ++index) {
|
||||
const auto& generic{program.info.input_generics[index]};
|
||||
for (size_t index = 0; index < info.input_generics.size(); ++index) {
|
||||
const auto& generic{info.input_generics[index]};
|
||||
if (generic.used) {
|
||||
Add("{}ATTRIB in_attr{}[]={{{}.attrib[{}..{}]}};",
|
||||
InterpDecorator(generic.interpolation), index, attr_stage, index, index);
|
||||
|
@ -101,8 +103,8 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
|
|||
index, index);
|
||||
}
|
||||
}
|
||||
for (size_t index = 0; index < program.info.stores_frag_color.size(); ++index) {
|
||||
if (!program.info.stores_frag_color[index]) {
|
||||
for (size_t index = 0; index < info.stores_frag_color.size(); ++index) {
|
||||
if (!info.stores_frag_color[index]) {
|
||||
continue;
|
||||
}
|
||||
if (index == 0) {
|
||||
|
@ -111,28 +113,28 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
|
|||
Add("OUTPUT frag_color{}=result.color[{}];", index, index);
|
||||
}
|
||||
}
|
||||
for (size_t index = 0; index < program.info.stores_generics.size(); ++index) {
|
||||
if (program.info.stores_generics[index]) {
|
||||
for (size_t index = 0; index < info.stores_generics.size(); ++index) {
|
||||
if (info.stores_generics[index]) {
|
||||
Add("OUTPUT out_attr{}[]={{result.attrib[{}..{}]}};", index, index, index);
|
||||
}
|
||||
}
|
||||
image_buffer_bindings.reserve(program.info.image_buffer_descriptors.size());
|
||||
for (const auto& desc : program.info.image_buffer_descriptors) {
|
||||
image_buffer_bindings.reserve(info.image_buffer_descriptors.size());
|
||||
for (const auto& desc : info.image_buffer_descriptors) {
|
||||
image_buffer_bindings.push_back(bindings.image);
|
||||
bindings.image += desc.count;
|
||||
}
|
||||
image_bindings.reserve(program.info.image_descriptors.size());
|
||||
for (const auto& desc : program.info.image_descriptors) {
|
||||
image_bindings.reserve(info.image_descriptors.size());
|
||||
for (const auto& desc : info.image_descriptors) {
|
||||
image_bindings.push_back(bindings.image);
|
||||
bindings.image += desc.count;
|
||||
}
|
||||
texture_buffer_bindings.reserve(program.info.texture_buffer_descriptors.size());
|
||||
for (const auto& desc : program.info.texture_buffer_descriptors) {
|
||||
texture_buffer_bindings.reserve(info.texture_buffer_descriptors.size());
|
||||
for (const auto& desc : info.texture_buffer_descriptors) {
|
||||
texture_buffer_bindings.push_back(bindings.texture);
|
||||
bindings.texture += desc.count;
|
||||
}
|
||||
texture_bindings.reserve(program.info.texture_descriptors.size());
|
||||
for (const auto& desc : program.info.texture_descriptors) {
|
||||
texture_bindings.reserve(info.texture_descriptors.size());
|
||||
for (const auto& desc : info.texture_descriptors) {
|
||||
texture_bindings.push_back(bindings.texture);
|
||||
bindings.texture += desc.count;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
namespace Shader {
|
||||
struct Info;
|
||||
struct Profile;
|
||||
struct RuntimeInfo;
|
||||
} // namespace Shader
|
||||
|
||||
namespace Shader::Backend {
|
||||
|
@ -31,7 +32,8 @@ namespace Shader::Backend::GLASM {
|
|||
|
||||
class EmitContext {
|
||||
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>
|
||||
void Add(const char* format_str, IR::Inst& inst, Args&&... args) {
|
||||
|
@ -56,8 +58,8 @@ public:
|
|||
|
||||
std::string code;
|
||||
RegAlloc reg_alloc{*this};
|
||||
const Info& info;
|
||||
const Profile& profile;
|
||||
const RuntimeInfo& runtime_info;
|
||||
|
||||
std::vector<u32> texture_buffer_bindings;
|
||||
std::vector<u32> image_buffer_bindings;
|
||||
|
|
|
@ -374,8 +374,9 @@ std::string_view GetTessSpacing(TessSpacing spacing) {
|
|||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
std::string EmitGLASM(const Profile& profile, IR::Program& program, Bindings& bindings) {
|
||||
EmitContext ctx{program, bindings, profile};
|
||||
std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info, IR::Program& program,
|
||||
Bindings& bindings) {
|
||||
EmitContext ctx{program, bindings, profile, runtime_info};
|
||||
Precolor(ctx, program);
|
||||
EmitCode(ctx, program);
|
||||
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);
|
||||
break;
|
||||
case Stage::TessellationEval:
|
||||
header +=
|
||||
fmt::format("TESS_MODE {};"
|
||||
"TESS_SPACING {};"
|
||||
"TESS_VERTEX_ORDER {};",
|
||||
GetTessMode(profile.tess_primitive), GetTessSpacing(profile.tess_spacing),
|
||||
profile.tess_clockwise ? "CW" : "CCW");
|
||||
header += fmt::format("TESS_MODE {};"
|
||||
"TESS_SPACING {};"
|
||||
"TESS_VERTEX_ORDER {};",
|
||||
GetTessMode(runtime_info.tess_primitive),
|
||||
GetTessSpacing(runtime_info.tess_spacing),
|
||||
runtime_info.tess_clockwise ? "CW" : "CCW");
|
||||
break;
|
||||
case Stage::Geometry:
|
||||
header += fmt::format("PRIMITIVE_IN {};"
|
||||
"PRIMITIVE_OUT {};"
|
||||
"VERTICES_OUT {};",
|
||||
InputPrimitive(profile.input_topology),
|
||||
InputPrimitive(runtime_info.input_topology),
|
||||
OutputPrimitive(program.output_topology), program.output_vertices);
|
||||
break;
|
||||
case Stage::Compute:
|
||||
|
|
|
@ -12,12 +12,12 @@
|
|||
|
||||
namespace Shader::Backend::GLASM {
|
||||
|
||||
[[nodiscard]] std::string EmitGLASM(const Profile& profile, IR::Program& program,
|
||||
Bindings& binding);
|
||||
[[nodiscard]] std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info,
|
||||
IR::Program& program, Bindings& bindings);
|
||||
|
||||
[[nodiscard]] inline std::string EmitGLASM(const Profile& profile, IR::Program& program) {
|
||||
Bindings binding;
|
||||
return EmitGLASM(profile, program, binding);
|
||||
return EmitGLASM(profile, {}, program, binding);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::GLASM
|
||||
|
|
|
@ -136,7 +136,7 @@ Id DefineInput(EmitContext& ctx, Id type, bool per_invocation,
|
|||
break;
|
||||
case Stage::Geometry:
|
||||
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));
|
||||
}
|
||||
break;
|
||||
|
@ -161,8 +161,8 @@ void DefineGenericOutput(EmitContext& ctx, size_t index, std::optional<u32> invo
|
|||
while (element < 4) {
|
||||
const u32 remainder{4 - element};
|
||||
const TransformFeedbackVarying* xfb_varying{};
|
||||
if (!ctx.profile.xfb_varyings.empty()) {
|
||||
xfb_varying = &ctx.profile.xfb_varyings[base_attr_index + element];
|
||||
if (!ctx.runtime_info.xfb_varyings.empty()) {
|
||||
xfb_varying = &ctx.runtime_info.xfb_varyings[base_attr_index + element];
|
||||
xfb_varying = xfb_varying && xfb_varying->components > 0 ? xfb_varying : nullptr;
|
||||
}
|
||||
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) {
|
||||
const AttributeType type{ctx.profile.generic_input_types.at(index)};
|
||||
const AttributeType type{ctx.runtime_info.generic_input_types.at(index)};
|
||||
switch (type) {
|
||||
case AttributeType::Float:
|
||||
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)
|
||||
: Sirit::Module(profile_.supported_spirv), profile{profile_}, stage{program.stage} {
|
||||
EmitContext::EmitContext(const Profile& profile_, const RuntimeInfo& runtime_info_,
|
||||
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};
|
||||
u32& uniform_binding{is_unified ? binding.unified : binding.uniform_buffer};
|
||||
u32& storage_binding{is_unified ? binding.unified : binding.storage_buffer};
|
||||
u32& texture_binding{is_unified ? binding.unified : binding.texture};
|
||||
u32& image_binding{is_unified ? binding.unified : binding.image};
|
||||
u32& uniform_binding{is_unified ? bindings.unified : bindings.uniform_buffer};
|
||||
u32& storage_binding{is_unified ? bindings.unified : bindings.storage_buffer};
|
||||
u32& texture_binding{is_unified ? bindings.unified : bindings.texture};
|
||||
u32& image_binding{is_unified ? bindings.unified : bindings.image};
|
||||
AddCapability(spv::Capability::Shader);
|
||||
DefineCommonTypes(program.info);
|
||||
DefineCommonConstants();
|
||||
|
@ -1211,7 +1213,7 @@ void EmitContext::DefineInputs(const Info& info) {
|
|||
if (!generic.used) {
|
||||
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) {
|
||||
continue;
|
||||
}
|
||||
|
@ -1256,7 +1258,7 @@ void EmitContext::DefineOutputs(const IR::Program& program) {
|
|||
if (info.stores_position || stage == Stage::VertexB) {
|
||||
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) {
|
||||
throw NotImplementedException("Storing PointSize in fragment stage");
|
||||
}
|
||||
|
|
|
@ -103,7 +103,8 @@ struct GenericElementInfo {
|
|||
|
||||
class EmitContext final : public Sirit::Module {
|
||||
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();
|
||||
|
||||
[[nodiscard]] Id Def(const IR::Value& value);
|
||||
|
@ -150,6 +151,7 @@ public:
|
|||
}
|
||||
|
||||
const Profile& profile;
|
||||
const RuntimeInfo& runtime_info;
|
||||
Stage stage{};
|
||||
|
||||
Id void_id{};
|
||||
|
|
|
@ -226,16 +226,17 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) {
|
|||
case Stage::TessellationEval:
|
||||
execution_model = spv::ExecutionModel::TessellationEvaluation;
|
||||
ctx.AddCapability(spv::Capability::Tessellation);
|
||||
ctx.AddExecutionMode(main, ExecutionMode(ctx.profile.tess_primitive));
|
||||
ctx.AddExecutionMode(main, ExecutionMode(ctx.profile.tess_spacing));
|
||||
ctx.AddExecutionMode(main, ctx.profile.tess_clockwise ? spv::ExecutionMode::VertexOrderCw
|
||||
: spv::ExecutionMode::VertexOrderCcw);
|
||||
ctx.AddExecutionMode(main, ExecutionMode(ctx.runtime_info.tess_primitive));
|
||||
ctx.AddExecutionMode(main, ExecutionMode(ctx.runtime_info.tess_spacing));
|
||||
ctx.AddExecutionMode(main, ctx.runtime_info.tess_clockwise
|
||||
? spv::ExecutionMode::VertexOrderCw
|
||||
: spv::ExecutionMode::VertexOrderCcw);
|
||||
break;
|
||||
case Stage::Geometry:
|
||||
execution_model = spv::ExecutionModel::Geometry;
|
||||
ctx.AddCapability(spv::Capability::Geometry);
|
||||
ctx.AddCapability(spv::Capability::GeometryStreams);
|
||||
switch (ctx.profile.input_topology) {
|
||||
switch (ctx.runtime_info.input_topology) {
|
||||
case InputTopology::Points:
|
||||
ctx.AddExecutionMode(main, spv::ExecutionMode::InputPoints);
|
||||
break;
|
||||
|
@ -279,7 +280,7 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) {
|
|||
if (program.info.stores_frag_depth) {
|
||||
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);
|
||||
}
|
||||
break;
|
||||
|
@ -402,7 +403,7 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct
|
|||
if (info.uses_sample_id) {
|
||||
ctx.AddCapability(spv::Capability::SampleRateShading);
|
||||
}
|
||||
if (!ctx.profile.xfb_varyings.empty()) {
|
||||
if (!ctx.runtime_info.xfb_varyings.empty()) {
|
||||
ctx.AddCapability(spv::Capability::TransformFeedback);
|
||||
}
|
||||
if (info.uses_derivatives) {
|
||||
|
@ -433,8 +434,9 @@ void PatchPhiNodes(IR::Program& program, EmitContext& ctx) {
|
|||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
std::vector<u32> EmitSPIRV(const Profile& profile, IR::Program& program, Bindings& binding) {
|
||||
EmitContext ctx{profile, program, binding};
|
||||
std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info,
|
||||
IR::Program& program, Bindings& bindings) {
|
||||
EmitContext ctx{profile, runtime_info, program, bindings};
|
||||
const Id main{DefineMain(ctx, program)};
|
||||
DefineEntryPoint(program, ctx, main);
|
||||
if (profile.support_float_controls) {
|
||||
|
|
|
@ -16,12 +16,12 @@
|
|||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
|
||||
[[nodiscard]] std::vector<u32> EmitSPIRV(const Profile& profile, IR::Program& program,
|
||||
Bindings& binding);
|
||||
[[nodiscard]] std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info,
|
||||
IR::Program& program, Bindings& bindings);
|
||||
|
||||
[[nodiscard]] inline std::vector<u32> EmitSPIRV(const Profile& profile, IR::Program& program) {
|
||||
Bindings binding;
|
||||
return EmitSPIRV(profile, program, binding);
|
||||
return EmitSPIRV(profile, {}, program, binding);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
|
|
|
@ -17,7 +17,7 @@ struct AttrInfo {
|
|||
};
|
||||
|
||||
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) {
|
||||
case AttributeType::Float:
|
||||
return AttrInfo{ctx.input_f32, ctx.F32[1], false};
|
||||
|
@ -468,7 +468,7 @@ Id EmitIsHelperInvocation(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) {
|
||||
|
|
|
@ -18,8 +18,8 @@ void ConvertDepthMode(EmitContext& ctx) {
|
|||
}
|
||||
|
||||
void SetFixedPipelinePointSize(EmitContext& ctx) {
|
||||
if (ctx.profile.fixed_state_point_size) {
|
||||
const float point_size{*ctx.profile.fixed_state_point_size};
|
||||
if (ctx.runtime_info.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));
|
||||
}
|
||||
}
|
||||
|
@ -62,7 +62,10 @@ Id ComparisonFunction(EmitContext& ctx, CompareFunction comparison, Id operand_1
|
|||
}
|
||||
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
|
@ -76,7 +79,7 @@ void AlphaTest(EmitContext& ctx) {
|
|||
|
||||
const Id true_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)};
|
||||
|
||||
ctx.OpSelectionMerge(true_label, spv::SelectionControlMask::MaskNone);
|
||||
|
@ -113,7 +116,7 @@ void EmitPrologue(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);
|
||||
}
|
||||
if (ctx.stage == Stage::Fragment) {
|
||||
|
@ -122,7 +125,7 @@ void EmitEpilogue(EmitContext& ctx) {
|
|||
}
|
||||
|
||||
void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream) {
|
||||
if (ctx.profile.convert_depth_mode) {
|
||||
if (ctx.runtime_info.convert_depth_mode) {
|
||||
ConvertDepthMode(ctx);
|
||||
}
|
||||
if (stream.IsImmediate()) {
|
||||
|
|
|
@ -81,19 +81,22 @@ struct Profile {
|
|||
bool support_viewport_mask{};
|
||||
bool support_typeless_image_loads{};
|
||||
bool support_demote_to_helper_invocation{};
|
||||
bool warp_size_potentially_larger_than_guest{};
|
||||
bool support_int64_atomics{};
|
||||
|
||||
bool warp_size_potentially_larger_than_guest{};
|
||||
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{};
|
||||
// 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{};
|
||||
// Signed instructions with unsigned data types are misinterpreted
|
||||
/// Signed instructions with unsigned data types are misinterpreted
|
||||
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{};
|
||||
};
|
||||
|
||||
struct RuntimeInfo {
|
||||
std::array<AttributeType, 32> generic_input_types{};
|
||||
bool convert_depth_mode{};
|
||||
bool force_early_z{};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue