video_core: Refactor GLSL fragment emitter (#7093)
* video_core: Refactor GLSL fragment emitter * shader: Add back custom normal mapsmaster
parent
9b2a5926a6
commit
1f6393e7d5
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,100 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "video_core/shader/generator/pica_fs_config.h"
|
||||
|
||||
namespace Pica::Shader::Generator::GLSL {
|
||||
|
||||
class FragmentModule {
|
||||
public:
|
||||
explicit FragmentModule(const FSConfig& config, const Profile& profile);
|
||||
~FragmentModule();
|
||||
|
||||
/// Emits GLSL source corresponding to the provided pica fragment configuration
|
||||
std::string Generate();
|
||||
|
||||
private:
|
||||
/// Undos the host perspective transformation and applies the PICA one
|
||||
void WriteDepth();
|
||||
|
||||
/// Emits code to emulate the scissor rectangle
|
||||
void WriteScissor();
|
||||
|
||||
/// Writes the code to emulate fragment lighting
|
||||
void WriteLighting();
|
||||
|
||||
/// Writes the code to emulate fog
|
||||
void WriteFog();
|
||||
|
||||
/// Writes the code to emulate gas rendering
|
||||
void WriteGas();
|
||||
|
||||
/// Writes the code to emulate shadow-map rendering
|
||||
void WriteShadow();
|
||||
|
||||
/// Writes the code to emulate logic ops in the fragment shader
|
||||
void WriteLogicOp();
|
||||
|
||||
/// Writes the code to emulate PICA min/max blending factors
|
||||
void WriteBlending();
|
||||
|
||||
/// Writes the specified TEV stage source component(s)
|
||||
void AppendSource(Pica::TexturingRegs::TevStageConfig::Source source, u32 tev_index);
|
||||
|
||||
/// Writes the color components to use for the specified TEV stage color modifier
|
||||
void AppendColorModifier(Pica::TexturingRegs::TevStageConfig::ColorModifier modifier,
|
||||
Pica::TexturingRegs::TevStageConfig::Source source, u32 tev_index);
|
||||
|
||||
/// Writes the alpha component to use for the specified TEV stage alpha modifier
|
||||
void AppendAlphaModifier(Pica::TexturingRegs::TevStageConfig::AlphaModifier modifier,
|
||||
Pica::TexturingRegs::TevStageConfig::Source source, u32 tev_index);
|
||||
|
||||
/// Writes the combiner function for the color components for the specified TEV stage operation
|
||||
void AppendColorCombiner(Pica::TexturingRegs::TevStageConfig::Operation operation);
|
||||
|
||||
/// Writes the combiner function for the alpha component for the specified TEV stage operation
|
||||
void AppendAlphaCombiner(Pica::TexturingRegs::TevStageConfig::Operation operation);
|
||||
|
||||
/// Writes the if-statement condition used to evaluate alpha testing
|
||||
void WriteAlphaTestCondition(Pica::FramebufferRegs::CompareFunc func);
|
||||
|
||||
/// Writes the code to emulate the specified TEV stage
|
||||
void WriteTevStage(u32 index);
|
||||
|
||||
void AppendProcTexShiftOffset(std::string_view v, Pica::TexturingRegs::ProcTexShift mode,
|
||||
Pica::TexturingRegs::ProcTexClamp clamp_mode);
|
||||
|
||||
void AppendProcTexClamp(std::string_view var, Pica::TexturingRegs::ProcTexClamp mode);
|
||||
|
||||
void AppendProcTexCombineAndMap(Pica::TexturingRegs::ProcTexCombiner combiner,
|
||||
std::string_view offset);
|
||||
|
||||
void DefineExtensions();
|
||||
void DefineInterface();
|
||||
void DefineBindings();
|
||||
void DefineHelpers();
|
||||
void DefineLightingHelpers();
|
||||
void DefineShadowHelpers();
|
||||
void DefineProcTexSampler();
|
||||
void DefineTexUnitSampler(u32 i);
|
||||
|
||||
private:
|
||||
const FSConfig& config;
|
||||
const Profile& profile;
|
||||
std::string out;
|
||||
bool use_blend_fallback{};
|
||||
bool use_fragment_shader_interlock{};
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates the GLSL fragment shader program source code for the current Pica state
|
||||
* @param config ShaderCacheKey object generated for the current Pica state, used for the shader
|
||||
* configuration (NOTE: Use state in this struct only, not the Pica registers!)
|
||||
* @returns String of the shader source code
|
||||
*/
|
||||
std::string GenerateFragmentShader(const FSConfig& config, const Profile& profile);
|
||||
|
||||
} // namespace Pica::Shader::Generator::GLSL
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,193 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "video_core/shader/generator/pica_fs_config.h"
|
||||
|
||||
namespace Pica::Shader {
|
||||
|
||||
FramebufferConfig::FramebufferConfig(const Pica::Regs& regs, const Profile& profile) {
|
||||
const auto& output_merger = regs.framebuffer.output_merger;
|
||||
scissor_test_mode.Assign(regs.rasterizer.scissor_test.mode);
|
||||
depthmap_enable.Assign(regs.rasterizer.depthmap_enable);
|
||||
shadow_rendering.Assign(regs.framebuffer.IsShadowRendering());
|
||||
alpha_test_func.Assign(output_merger.alpha_test.enable
|
||||
? output_merger.alpha_test.func.Value()
|
||||
: Pica::FramebufferRegs::CompareFunc::Always);
|
||||
|
||||
// Emulate logic op in the shader if needed and not supported.
|
||||
logic_op.Assign(Pica::FramebufferRegs::LogicOp::Copy);
|
||||
if (!profile.has_logic_op && !regs.framebuffer.output_merger.alphablend_enable) {
|
||||
logic_op.Assign(regs.framebuffer.output_merger.logic_op);
|
||||
}
|
||||
|
||||
const auto alpha_eq = output_merger.alpha_blending.blend_equation_a.Value();
|
||||
const auto rgb_eq = output_merger.alpha_blending.blend_equation_rgb.Value();
|
||||
if (!profile.has_blend_minmax_factor && output_merger.alphablend_enable) {
|
||||
if (rgb_eq == Pica::FramebufferRegs::BlendEquation::Max ||
|
||||
rgb_eq == Pica::FramebufferRegs::BlendEquation::Min) {
|
||||
rgb_blend.eq = rgb_eq;
|
||||
rgb_blend.src_factor = output_merger.alpha_blending.factor_source_rgb;
|
||||
rgb_blend.dst_factor = output_merger.alpha_blending.factor_dest_rgb;
|
||||
}
|
||||
if (alpha_eq == Pica::FramebufferRegs::BlendEquation::Max ||
|
||||
alpha_eq == Pica::FramebufferRegs::BlendEquation::Min) {
|
||||
alpha_blend.eq = alpha_eq;
|
||||
alpha_blend.src_factor = output_merger.alpha_blending.factor_source_a;
|
||||
alpha_blend.dst_factor = output_merger.alpha_blending.factor_dest_a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextureConfig::TextureConfig(const Pica::TexturingRegs& regs, const Profile& profile) {
|
||||
texture0_type.Assign(regs.texture0.type);
|
||||
texture2_use_coord1.Assign(regs.main_config.texture2_use_coord1 != 0);
|
||||
combiner_buffer_input.Assign(regs.tev_combiner_buffer_input.update_mask_rgb.Value() |
|
||||
regs.tev_combiner_buffer_input.update_mask_a.Value() << 4);
|
||||
fog_mode.Assign(regs.fog_mode);
|
||||
fog_flip.Assign(regs.fog_flip != 0);
|
||||
shadow_texture_orthographic.Assign(regs.shadow.orthographic != 0);
|
||||
|
||||
// Emulate custom border color if needed and not supported.
|
||||
const auto pica_textures = regs.GetTextures();
|
||||
for (u32 tex_index = 0; tex_index < 3; tex_index++) {
|
||||
const auto& config = pica_textures[tex_index].config;
|
||||
texture_border_color[tex_index].enable_s.Assign(
|
||||
!profile.has_custom_border_color &&
|
||||
config.wrap_s == Pica::TexturingRegs::TextureConfig::WrapMode::ClampToBorder);
|
||||
texture_border_color[tex_index].enable_t.Assign(
|
||||
!profile.has_custom_border_color &&
|
||||
config.wrap_t == Pica::TexturingRegs::TextureConfig::WrapMode::ClampToBorder);
|
||||
}
|
||||
|
||||
const auto& stages = regs.GetTevStages();
|
||||
for (std::size_t i = 0; i < tev_stages.size(); i++) {
|
||||
const auto& tev_stage = stages[i];
|
||||
tev_stages[i].sources_raw = tev_stage.sources_raw;
|
||||
tev_stages[i].modifiers_raw = tev_stage.modifiers_raw;
|
||||
tev_stages[i].ops_raw = tev_stage.ops_raw;
|
||||
tev_stages[i].scales_raw = tev_stage.scales_raw;
|
||||
if (tev_stage.color_op == Pica::TexturingRegs::TevStageConfig::Operation::Dot3_RGBA) {
|
||||
tev_stages[i].sources_raw &= 0xFFF;
|
||||
tev_stages[i].modifiers_raw &= 0xFFF;
|
||||
tev_stages[i].ops_raw &= 0xF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LightConfig::LightConfig(const Pica::LightingRegs& regs) {
|
||||
if (regs.disable) {
|
||||
return;
|
||||
}
|
||||
|
||||
enable.Assign(1);
|
||||
src_num.Assign(regs.max_light_index + 1);
|
||||
config.Assign(regs.config0.config);
|
||||
enable_primary_alpha.Assign(regs.config0.enable_primary_alpha);
|
||||
enable_secondary_alpha.Assign(regs.config0.enable_secondary_alpha);
|
||||
bump_mode.Assign(regs.config0.bump_mode);
|
||||
bump_selector.Assign(regs.config0.bump_selector);
|
||||
bump_renorm.Assign(regs.config0.disable_bump_renorm == 0);
|
||||
clamp_highlights.Assign(regs.config0.clamp_highlights != 0);
|
||||
|
||||
enable_shadow.Assign(regs.config0.enable_shadow != 0);
|
||||
if (enable_shadow) {
|
||||
shadow_primary.Assign(regs.config0.shadow_primary != 0);
|
||||
shadow_secondary.Assign(regs.config0.shadow_secondary != 0);
|
||||
shadow_invert.Assign(regs.config0.shadow_invert != 0);
|
||||
shadow_alpha.Assign(regs.config0.shadow_alpha != 0);
|
||||
shadow_selector.Assign(regs.config0.shadow_selector);
|
||||
}
|
||||
|
||||
for (u32 light_index = 0; light_index <= regs.max_light_index; ++light_index) {
|
||||
const u32 num = regs.light_enable.GetNum(light_index);
|
||||
const auto& light = regs.light[num];
|
||||
lights[light_index].num.Assign(num);
|
||||
lights[light_index].directional.Assign(light.config.directional != 0);
|
||||
lights[light_index].two_sided_diffuse.Assign(light.config.two_sided_diffuse != 0);
|
||||
lights[light_index].geometric_factor_0.Assign(light.config.geometric_factor_0 != 0);
|
||||
lights[light_index].geometric_factor_1.Assign(light.config.geometric_factor_1 != 0);
|
||||
lights[light_index].dist_atten_enable.Assign(!regs.IsDistAttenDisabled(num));
|
||||
lights[light_index].spot_atten_enable.Assign(!regs.IsSpotAttenDisabled(num));
|
||||
lights[light_index].shadow_enable.Assign(!regs.IsShadowDisabled(num));
|
||||
}
|
||||
|
||||
lut_d0.enable.Assign(regs.config1.disable_lut_d0 == 0);
|
||||
if (lut_d0.enable) {
|
||||
lut_d0.abs_input.Assign(regs.abs_lut_input.disable_d0 == 0);
|
||||
lut_d0.type.Assign(regs.lut_input.d0.Value());
|
||||
lut_d0.scale = regs.lut_scale.GetScale(regs.lut_scale.d0);
|
||||
}
|
||||
|
||||
lut_d1.enable.Assign(regs.config1.disable_lut_d1 == 0);
|
||||
if (lut_d1.enable) {
|
||||
lut_d1.abs_input.Assign(regs.abs_lut_input.disable_d1 == 0);
|
||||
lut_d1.type.Assign(regs.lut_input.d1.Value());
|
||||
lut_d1.scale = regs.lut_scale.GetScale(regs.lut_scale.d1);
|
||||
}
|
||||
|
||||
// This is a dummy field due to lack of the corresponding register
|
||||
lut_sp.enable.Assign(1);
|
||||
lut_sp.abs_input.Assign(regs.abs_lut_input.disable_sp == 0);
|
||||
lut_sp.type.Assign(regs.lut_input.sp.Value());
|
||||
lut_sp.scale = regs.lut_scale.GetScale(regs.lut_scale.sp);
|
||||
|
||||
lut_fr.enable.Assign(regs.config1.disable_lut_fr == 0);
|
||||
if (lut_fr.enable) {
|
||||
lut_fr.abs_input.Assign(regs.abs_lut_input.disable_fr == 0);
|
||||
lut_fr.type.Assign(regs.lut_input.fr.Value());
|
||||
lut_fr.scale = regs.lut_scale.GetScale(regs.lut_scale.fr);
|
||||
}
|
||||
|
||||
lut_rr.enable.Assign(regs.config1.disable_lut_rr == 0);
|
||||
if (lut_rr.enable) {
|
||||
lut_rr.abs_input.Assign(regs.abs_lut_input.disable_rr == 0);
|
||||
lut_rr.type.Assign(regs.lut_input.rr.Value());
|
||||
lut_rr.scale = regs.lut_scale.GetScale(regs.lut_scale.rr);
|
||||
}
|
||||
|
||||
lut_rg.enable.Assign(regs.config1.disable_lut_rg == 0);
|
||||
if (lut_rg.enable) {
|
||||
lut_rg.abs_input.Assign(regs.abs_lut_input.disable_rg == 0);
|
||||
lut_rg.type.Assign(regs.lut_input.rg.Value());
|
||||
lut_rg.scale = regs.lut_scale.GetScale(regs.lut_scale.rg);
|
||||
}
|
||||
|
||||
lut_rb.enable.Assign(regs.config1.disable_lut_rb == 0);
|
||||
if (lut_rb.enable) {
|
||||
lut_rb.abs_input.Assign(regs.abs_lut_input.disable_rb == 0);
|
||||
lut_rb.type.Assign(regs.lut_input.rb.Value());
|
||||
lut_rb.scale = regs.lut_scale.GetScale(regs.lut_scale.rb);
|
||||
}
|
||||
}
|
||||
|
||||
ProcTexConfig::ProcTexConfig(const Pica::TexturingRegs& regs) {
|
||||
if (!regs.main_config.texture3_enable) {
|
||||
return;
|
||||
}
|
||||
|
||||
enable.Assign(1);
|
||||
coord.Assign(regs.main_config.texture3_coordinates);
|
||||
u_clamp.Assign(regs.proctex.u_clamp);
|
||||
v_clamp.Assign(regs.proctex.v_clamp);
|
||||
color_combiner.Assign(regs.proctex.color_combiner);
|
||||
alpha_combiner.Assign(regs.proctex.alpha_combiner);
|
||||
separate_alpha.Assign(regs.proctex.separate_alpha);
|
||||
noise_enable.Assign(regs.proctex.noise_enable);
|
||||
u_shift.Assign(regs.proctex.u_shift);
|
||||
v_shift.Assign(regs.proctex.v_shift);
|
||||
lut_width = regs.proctex_lut.width;
|
||||
lut_offset0 = regs.proctex_lut_offset.level0;
|
||||
lut_offset1 = regs.proctex_lut_offset.level1;
|
||||
lut_offset2 = regs.proctex_lut_offset.level2;
|
||||
lut_offset3 = regs.proctex_lut_offset.level3;
|
||||
lod_min = regs.proctex_lut.lod_min;
|
||||
lod_max = regs.proctex_lut.lod_max;
|
||||
lut_filter.Assign(regs.proctex_lut.filter);
|
||||
}
|
||||
|
||||
FSConfig::FSConfig(const Pica::Regs& regs, const UserConfig& user_, const Profile& profile)
|
||||
: framebuffer{regs, profile}, texture{regs.texturing, profile}, lighting{regs.lighting},
|
||||
proctex{regs.texturing}, user{user_} {}
|
||||
|
||||
} // namespace Pica::Shader
|
@ -0,0 +1,207 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/hash.h"
|
||||
#include "video_core/regs.h"
|
||||
#include "video_core/shader/generator/profile.h"
|
||||
|
||||
namespace Pica::Shader {
|
||||
|
||||
struct BlendConfig {
|
||||
Pica::FramebufferRegs::BlendEquation eq;
|
||||
Pica::FramebufferRegs::BlendFactor src_factor;
|
||||
Pica::FramebufferRegs::BlendFactor dst_factor;
|
||||
};
|
||||
|
||||
struct FramebufferConfig {
|
||||
explicit FramebufferConfig(const Pica::Regs& regs, const Profile& profile);
|
||||
|
||||
union {
|
||||
u32 raw{};
|
||||
BitField<0, 3, Pica::FramebufferRegs::CompareFunc> alpha_test_func;
|
||||
BitField<3, 2, Pica::RasterizerRegs::ScissorMode> scissor_test_mode;
|
||||
BitField<5, 1, Pica::RasterizerRegs::DepthBuffering> depthmap_enable;
|
||||
BitField<6, 4, Pica::FramebufferRegs::LogicOp> logic_op;
|
||||
BitField<10, 1, u32> shadow_rendering;
|
||||
};
|
||||
BlendConfig rgb_blend{};
|
||||
BlendConfig alpha_blend{};
|
||||
};
|
||||
static_assert(std::has_unique_object_representations_v<FramebufferConfig>);
|
||||
|
||||
struct TevStageConfigRaw {
|
||||
u32 sources_raw;
|
||||
u32 modifiers_raw;
|
||||
u32 ops_raw;
|
||||
u32 scales_raw;
|
||||
operator Pica::TexturingRegs::TevStageConfig() const noexcept {
|
||||
return {
|
||||
.sources_raw = sources_raw,
|
||||
.modifiers_raw = modifiers_raw,
|
||||
.ops_raw = ops_raw,
|
||||
.const_color = 0,
|
||||
.scales_raw = scales_raw,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
union TextureBorder {
|
||||
BitField<0, 1, u32> enable_s;
|
||||
BitField<1, 1, u32> enable_t;
|
||||
};
|
||||
|
||||
struct TextureConfig {
|
||||
explicit TextureConfig(const Pica::TexturingRegs& regs, const Profile& profile);
|
||||
|
||||
union {
|
||||
u32 raw{};
|
||||
BitField<0, 3, Pica::TexturingRegs::TextureConfig::TextureType> texture0_type;
|
||||
BitField<3, 1, u32> texture2_use_coord1;
|
||||
BitField<4, 8, u32> combiner_buffer_input;
|
||||
BitField<12, 3, Pica::TexturingRegs::FogMode> fog_mode;
|
||||
BitField<15, 1, u32> fog_flip;
|
||||
BitField<16, 1, u32> shadow_texture_orthographic;
|
||||
};
|
||||
std::array<TextureBorder, 3> texture_border_color{};
|
||||
std::array<TevStageConfigRaw, 6> tev_stages{};
|
||||
};
|
||||
static_assert(std::has_unique_object_representations_v<TextureConfig>);
|
||||
|
||||
union Light {
|
||||
u16 raw;
|
||||
BitField<0, 3, u16> num;
|
||||
BitField<3, 1, u16> directional;
|
||||
BitField<4, 1, u16> two_sided_diffuse;
|
||||
BitField<5, 1, u16> dist_atten_enable;
|
||||
BitField<6, 1, u16> spot_atten_enable;
|
||||
BitField<7, 1, u16> geometric_factor_0;
|
||||
BitField<8, 1, u16> geometric_factor_1;
|
||||
BitField<9, 1, u16> shadow_enable;
|
||||
};
|
||||
static_assert(std::has_unique_object_representations_v<Light>);
|
||||
|
||||
struct LutConfig {
|
||||
union {
|
||||
u32 raw;
|
||||
BitField<0, 1, u32> enable;
|
||||
BitField<1, 1, u32> abs_input;
|
||||
BitField<2, 3, Pica::LightingRegs::LightingLutInput> type;
|
||||
};
|
||||
f32 scale;
|
||||
};
|
||||
|
||||
struct LightConfig {
|
||||
explicit LightConfig(const Pica::LightingRegs& regs);
|
||||
|
||||
union {
|
||||
u32 raw{};
|
||||
BitField<0, 1, u32> enable;
|
||||
BitField<1, 4, u32> src_num;
|
||||
BitField<5, 2, Pica::LightingRegs::LightingBumpMode> bump_mode;
|
||||
BitField<7, 2, u32> bump_selector;
|
||||
BitField<9, 1, u32> bump_renorm;
|
||||
BitField<10, 1, u32> clamp_highlights;
|
||||
BitField<11, 4, Pica::LightingRegs::LightingConfig> config;
|
||||
BitField<15, 1, u32> enable_primary_alpha;
|
||||
BitField<16, 1, u32> enable_secondary_alpha;
|
||||
BitField<17, 1, u32> enable_shadow;
|
||||
BitField<18, 1, u32> shadow_primary;
|
||||
BitField<19, 1, u32> shadow_secondary;
|
||||
BitField<20, 1, u32> shadow_invert;
|
||||
BitField<21, 1, u32> shadow_alpha;
|
||||
BitField<22, 2, u32> shadow_selector;
|
||||
};
|
||||
LutConfig lut_d0{};
|
||||
LutConfig lut_d1{};
|
||||
LutConfig lut_sp{};
|
||||
LutConfig lut_fr{};
|
||||
LutConfig lut_rr{};
|
||||
LutConfig lut_rg{};
|
||||
LutConfig lut_rb{};
|
||||
std::array<Light, 8> lights{};
|
||||
};
|
||||
|
||||
struct ProcTexConfig {
|
||||
explicit ProcTexConfig(const Pica::TexturingRegs& regs);
|
||||
|
||||
union {
|
||||
u32 raw{};
|
||||
BitField<0, 1, u32> enable;
|
||||
BitField<1, 2, u32> coord;
|
||||
BitField<3, 3, Pica::TexturingRegs::ProcTexClamp> u_clamp;
|
||||
BitField<6, 3, Pica::TexturingRegs::ProcTexClamp> v_clamp;
|
||||
BitField<9, 4, Pica::TexturingRegs::ProcTexCombiner> color_combiner;
|
||||
BitField<13, 4, Pica::TexturingRegs::ProcTexCombiner> alpha_combiner;
|
||||
BitField<17, 3, Pica::TexturingRegs::ProcTexFilter> lut_filter;
|
||||
BitField<20, 1, u32> separate_alpha;
|
||||
BitField<21, 1, u32> noise_enable;
|
||||
BitField<22, 2, Pica::TexturingRegs::ProcTexShift> u_shift;
|
||||
BitField<24, 2, Pica::TexturingRegs::ProcTexShift> v_shift;
|
||||
};
|
||||
s32 lut_width{};
|
||||
s32 lut_offset0{};
|
||||
s32 lut_offset1{};
|
||||
s32 lut_offset2{};
|
||||
s32 lut_offset3{};
|
||||
u16 lod_min{};
|
||||
u16 lod_max{};
|
||||
};
|
||||
static_assert(std::has_unique_object_representations_v<ProcTexConfig>);
|
||||
|
||||
union UserConfig {
|
||||
u32 raw{};
|
||||
BitField<0, 1, u32> use_custom_normal;
|
||||
};
|
||||
static_assert(std::has_unique_object_representations_v<UserConfig>);
|
||||
|
||||
struct FSConfig {
|
||||
explicit FSConfig(const Pica::Regs& regs, const UserConfig& user, const Profile& profile);
|
||||
|
||||
[[nodiscard]] bool TevStageUpdatesCombinerBufferColor(u32 stage_index) const {
|
||||
return (stage_index < 4) && (texture.combiner_buffer_input & (1 << stage_index));
|
||||
}
|
||||
|
||||
[[nodiscard]] bool TevStageUpdatesCombinerBufferAlpha(u32 stage_index) const {
|
||||
return (stage_index < 4) && ((texture.combiner_buffer_input >> 4) & (1 << stage_index));
|
||||
}
|
||||
|
||||
[[nodiscard]] bool EmulateBlend() const {
|
||||
return framebuffer.rgb_blend.eq != Pica::FramebufferRegs::BlendEquation::Add ||
|
||||
framebuffer.alpha_blend.eq != Pica::FramebufferRegs::BlendEquation::Add;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool UsesShadowPipeline() const {
|
||||
const auto texture0_type = texture.texture0_type.Value();
|
||||
return texture0_type == Pica::TexturingRegs::TextureConfig::Shadow2D ||
|
||||
texture0_type == Pica::TexturingRegs::TextureConfig::ShadowCube ||
|
||||
framebuffer.shadow_rendering.Value();
|
||||
}
|
||||
|
||||
bool operator==(const FSConfig& other) const noexcept {
|
||||
return std::memcmp(this, &other, sizeof(FSConfig)) == 0;
|
||||
}
|
||||
|
||||
std::size_t Hash() const noexcept {
|
||||
return Common::ComputeHash64(this, sizeof(FSConfig));
|
||||
}
|
||||
|
||||
FramebufferConfig framebuffer;
|
||||
TextureConfig texture;
|
||||
LightConfig lighting;
|
||||
ProcTexConfig proctex;
|
||||
UserConfig user;
|
||||
};
|
||||
|
||||
} // namespace Pica::Shader
|
||||
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<Pica::Shader::FSConfig> {
|
||||
std::size_t operator()(const Pica::Shader::FSConfig& k) const noexcept {
|
||||
return k.Hash();
|
||||
}
|
||||
};
|
||||
} // namespace std
|
@ -0,0 +1,25 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Pica::Shader {
|
||||
|
||||
struct Profile {
|
||||
bool has_separable_shaders{};
|
||||
bool has_clip_planes{};
|
||||
bool has_geometry_shader{};
|
||||
bool has_custom_border_color{};
|
||||
bool has_fragment_shader_interlock{};
|
||||
bool has_blend_minmax_factor{};
|
||||
bool has_minus_one_to_one_range{};
|
||||
bool has_logic_op{};
|
||||
bool has_gl_ext_framebuffer_fetch{};
|
||||
bool has_gl_arm_framebuffer_fetch{};
|
||||
bool has_gl_nv_fragment_shader_interlock{};
|
||||
bool has_gl_intel_fragment_shader_interlock{};
|
||||
bool is_vulkan{};
|
||||
};
|
||||
|
||||
} // namespace Pica::Shader
|
Loading…
Reference in New Issue