vk_shader_gen_spv: Implement alpha testing
* Should fix all current graphical bugs
This commit is contained in:
@ -260,7 +260,7 @@ void PipelineCache::UseFragmentShader(const Pica::Regs& regs) {
|
|||||||
handle = fragment_shaders_spv.Get(config, instance.GetDevice());
|
handle = fragment_shaders_spv.Get(config, instance.GetDevice());
|
||||||
} else {
|
} else {
|
||||||
handle = fragment_shaders_glsl.Get(config, vk::ShaderStageFlagBits::eFragment,
|
handle = fragment_shaders_glsl.Get(config, vk::ShaderStageFlagBits::eFragment,
|
||||||
instance.GetDevice(), ShaderOptimization::Debug);
|
instance.GetDevice(), ShaderOptimization::High);
|
||||||
}
|
}
|
||||||
|
|
||||||
current_shaders[ProgramType::FS] = handle;
|
current_shaders[ProgramType::FS] = handle;
|
||||||
|
@ -25,7 +25,6 @@ FragmentModule::FragmentModule(const PicaFSConfig& config) : Sirit::Module{0x000
|
|||||||
FragmentModule::~FragmentModule() = default;
|
FragmentModule::~FragmentModule() = default;
|
||||||
|
|
||||||
void FragmentModule::Generate() {
|
void FragmentModule::Generate() {
|
||||||
const PicaFSConfigState& state = config.state;
|
|
||||||
AddLabel(OpLabel());
|
AddLabel(OpLabel());
|
||||||
|
|
||||||
rounded_primary_color = Byteround(OpLoad(vec_ids.Get(4), primary_color_id), 4);
|
rounded_primary_color = Byteround(OpLoad(vec_ids.Get(4), primary_color_id), 4);
|
||||||
@ -33,19 +32,14 @@ void FragmentModule::Generate() {
|
|||||||
secondary_fragment_color = ConstF32(0.f, 0.f, 0.f, 0.f);
|
secondary_fragment_color = ConstF32(0.f, 0.f, 0.f, 0.f);
|
||||||
|
|
||||||
// Do not do any sort of processing if it's obvious we're not going to pass the alpha test
|
// Do not do any sort of processing if it's obvious we're not going to pass the alpha test
|
||||||
if (state.alpha_test_func == Pica::FramebufferRegs::CompareFunc::Never) {
|
if (config.state.alpha_test_func == Pica::FramebufferRegs::CompareFunc::Never) {
|
||||||
OpKill();
|
OpKill();
|
||||||
OpFunctionEnd();
|
OpFunctionEnd();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// After perspective divide, OpenGL transform z_over_w from [-1, 1] to [near, far]. Here we use
|
|
||||||
// default near = 0 and far = 1, and undo the transformation to get the original z_over_w, then
|
|
||||||
// do our own transformation according to PICA specification.
|
|
||||||
WriteDepth();
|
|
||||||
|
|
||||||
// Write shader bytecode to emulate all enabled PICA lights
|
// Write shader bytecode to emulate all enabled PICA lights
|
||||||
if (state.lighting.enable) {
|
if (config.state.lighting.enable) {
|
||||||
WriteLighting();
|
WriteLighting();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,10 +48,19 @@ void FragmentModule::Generate() {
|
|||||||
last_tex_env_out = ConstF32(0.f, 0.f, 0.f, 0.f);
|
last_tex_env_out = ConstF32(0.f, 0.f, 0.f, 0.f);
|
||||||
|
|
||||||
// Write shader bytecode to emulate PICA TEV stages
|
// Write shader bytecode to emulate PICA TEV stages
|
||||||
for (std::size_t index = 0; index < state.tev_stages.size(); ++index) {
|
for (std::size_t index = 0; index < config.state.tev_stages.size(); ++index) {
|
||||||
WriteTevStage(static_cast<s32>(index));
|
WriteTevStage(static_cast<s32>(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (WriteAlphaTestCondition(config.state.alpha_test_func)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// After perspective divide, OpenGL transform z_over_w from [-1, 1] to [near, far]. Here we use
|
||||||
|
// default near = 0 and far = 1, and undo the transformation to get the original z_over_w, then
|
||||||
|
// do our own transformation according to PICA specification.
|
||||||
|
WriteDepth();
|
||||||
|
|
||||||
// Write output color
|
// Write output color
|
||||||
OpStore(color_id, Byteround(last_tex_env_out, 4));
|
OpStore(color_id, Byteround(last_tex_env_out, 4));
|
||||||
OpReturn();
|
OpReturn();
|
||||||
@ -68,9 +71,8 @@ void FragmentModule::WriteDepth() {
|
|||||||
const Id input_pointer_id{TypePointer(spv::StorageClass::Input, f32_id)};
|
const Id input_pointer_id{TypePointer(spv::StorageClass::Input, f32_id)};
|
||||||
const Id gl_frag_coord_z{OpLoad(f32_id, OpAccessChain(input_pointer_id, gl_frag_coord_id, ConstU32(2u)))};
|
const Id gl_frag_coord_z{OpLoad(f32_id, OpAccessChain(input_pointer_id, gl_frag_coord_id, ConstU32(2u)))};
|
||||||
const Id z_over_w{OpFma(f32_id, ConstF32(2.f), gl_frag_coord_z, ConstF32(-1.f))};
|
const Id z_over_w{OpFma(f32_id, ConstF32(2.f), gl_frag_coord_z, ConstF32(-1.f))};
|
||||||
const Id uniform_pointer_id{TypePointer(spv::StorageClass::Uniform, f32_id)};
|
const Id depth_scale{GetShaderDataMember(f32_id, ConstS32(2))};
|
||||||
const Id depth_scale{OpLoad(f32_id, OpAccessChain(uniform_pointer_id, shader_data_id, ConstS32(2)))};
|
const Id depth_offset{GetShaderDataMember(f32_id, ConstS32(3))};
|
||||||
const Id depth_offset{OpLoad(f32_id, OpAccessChain(uniform_pointer_id, shader_data_id, ConstS32(3)))};
|
|
||||||
const Id depth{OpFma(f32_id, z_over_w, depth_scale, depth_offset)};
|
const Id depth{OpFma(f32_id, z_over_w, depth_scale, depth_offset)};
|
||||||
if (config.state.depthmap_enable == Pica::RasterizerRegs::DepthBuffering::WBuffering) {
|
if (config.state.depthmap_enable == Pica::RasterizerRegs::DepthBuffering::WBuffering) {
|
||||||
const Id gl_frag_coord_w{OpLoad(f32_id, OpAccessChain(input_pointer_id, gl_frag_coord_id, ConstU32(3u)))};
|
const Id gl_frag_coord_w{OpLoad(f32_id, OpAccessChain(input_pointer_id, gl_frag_coord_id, ConstU32(3u)))};
|
||||||
@ -154,7 +156,7 @@ void FragmentModule::WriteLighting() {
|
|||||||
const Id tangent{QuaternionRotate(normalized_normquat, surface_tangent)};
|
const Id tangent{QuaternionRotate(normalized_normquat, surface_tangent)};
|
||||||
|
|
||||||
Id shadow{ConstF32(1.f, 1.f, 1.f, 1.f)};
|
Id shadow{ConstF32(1.f, 1.f, 1.f, 1.f)};
|
||||||
if (lighting.enable_shadow && false) {
|
if (lighting.enable_shadow) {
|
||||||
shadow = SampleTexture(lighting.shadow_selector);
|
shadow = SampleTexture(lighting.shadow_selector);
|
||||||
if (lighting.shadow_invert) {
|
if (lighting.shadow_invert) {
|
||||||
shadow = OpFSub(vec_ids.Get(4), ConstF32(1.f, 1.f, 1.f, 1.f), shadow);
|
shadow = OpFSub(vec_ids.Get(4), ConstF32(1.f, 1.f, 1.f, 1.f), shadow);
|
||||||
@ -162,10 +164,10 @@ void FragmentModule::WriteLighting() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto LookupLightingLUTUnsigned = [this](Id lut_index, Id pos) -> Id {
|
const auto LookupLightingLUTUnsigned = [this](Id lut_index, Id pos) -> Id {
|
||||||
const Id pos_int{OpConvertFToS(i32_id, OpFMul(f32_id, pos, ConstF32(255.f)))};
|
const Id pos_int{OpConvertFToS(i32_id, OpFMul(f32_id, pos, ConstF32(256.f)))};
|
||||||
const Id index{OpSClamp(i32_id, pos_int, ConstS32(0), ConstS32(255))};
|
const Id index{OpSClamp(i32_id, pos_int, ConstS32(0), ConstS32(255))};
|
||||||
const Id neg_index{OpFNegate(f32_id, OpConvertSToF(f32_id, index))};
|
const Id neg_index{OpFNegate(f32_id, OpConvertSToF(f32_id, index))};
|
||||||
const Id delta{OpFma(f32_id, pos, ConstF32(255.f), neg_index)};
|
const Id delta{OpFma(f32_id, pos, ConstF32(256.f), neg_index)};
|
||||||
return LookupLightingLUT(lut_index, index, delta);
|
return LookupLightingLUT(lut_index, index, delta);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -174,7 +176,7 @@ void FragmentModule::WriteLighting() {
|
|||||||
const Id index{OpSClamp(i32_id, pos_int, ConstS32(-128), ConstS32(127))};
|
const Id index{OpSClamp(i32_id, pos_int, ConstS32(-128), ConstS32(127))};
|
||||||
const Id neg_index{OpFNegate(f32_id, OpConvertSToF(f32_id, index))};
|
const Id neg_index{OpFNegate(f32_id, OpConvertSToF(f32_id, index))};
|
||||||
const Id delta{OpFma(f32_id, pos, ConstF32(128.f), neg_index)};
|
const Id delta{OpFma(f32_id, pos, ConstF32(128.f), neg_index)};
|
||||||
const Id increment{OpSelect(i32_id, OpSLessThan(bool_id, index, ConstS32(0)), ConstS32(255), ConstS32(0))};
|
const Id increment{OpSelect(i32_id, OpSLessThan(bool_id, index, ConstS32(0)), ConstS32(256), ConstS32(0))};
|
||||||
return LookupLightingLUT(lut_index, OpIAdd(i32_id, index, increment), delta);
|
return LookupLightingLUT(lut_index, OpIAdd(i32_id, index, increment), delta);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -243,10 +245,8 @@ void FragmentModule::WriteLighting() {
|
|||||||
|
|
||||||
const auto GetLightMember = [&](s32 member) -> Id {
|
const auto GetLightMember = [&](s32 member) -> Id {
|
||||||
const Id member_type = member < 6 ? vec_ids.Get(3) : f32_id;
|
const Id member_type = member < 6 ? vec_ids.Get(3) : f32_id;
|
||||||
const Id uniform_pointer_id{TypePointer(spv::StorageClass::Uniform, member_type)};
|
|
||||||
const Id light_num{ConstS32(static_cast<s32>(lighting.light[light_index].num.Value()))};
|
const Id light_num{ConstS32(static_cast<s32>(lighting.light[light_index].num.Value()))};
|
||||||
return OpLoad(member_type, OpAccessChain(uniform_pointer_id, shader_data_id, ConstS32(25),
|
return GetShaderDataMember(member_type, ConstS32(25), light_num, ConstS32(member));
|
||||||
light_num, ConstS32(member)));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Compute light vector (directional or positional)
|
// Compute light vector (directional or positional)
|
||||||
@ -495,8 +495,64 @@ void FragmentModule::WriteTevStage(s32 index) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FragmentModule::WriteAlphaTestCondition(FramebufferRegs::CompareFunc func) {
|
||||||
|
using CompareFunc = FramebufferRegs::CompareFunc;
|
||||||
|
|
||||||
|
const auto Compare = [this, func](Id alpha, Id alphatest_ref) {
|
||||||
|
switch (func) {
|
||||||
|
case CompareFunc::Equal:
|
||||||
|
return OpINotEqual(bool_id, alpha, alphatest_ref);
|
||||||
|
case CompareFunc::NotEqual:
|
||||||
|
return OpIEqual(bool_id, alpha, alphatest_ref);
|
||||||
|
case CompareFunc::LessThan:
|
||||||
|
return OpSGreaterThanEqual(bool_id, alpha, alphatest_ref);
|
||||||
|
case CompareFunc::LessThanOrEqual:
|
||||||
|
return OpSGreaterThan(bool_id, alpha, alphatest_ref);
|
||||||
|
case CompareFunc::GreaterThan:
|
||||||
|
return OpSLessThanEqual(bool_id, alpha, alphatest_ref);
|
||||||
|
case CompareFunc::GreaterThanOrEqual:
|
||||||
|
return OpSLessThan(bool_id, alpha, alphatest_ref);
|
||||||
|
default:
|
||||||
|
return Id{};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (func) {
|
||||||
|
case CompareFunc::Never: // Kill the fragment
|
||||||
|
OpKill();
|
||||||
|
OpFunctionEnd();
|
||||||
|
return true;
|
||||||
|
case CompareFunc::Always: // Do nothing
|
||||||
|
return false;
|
||||||
|
case CompareFunc::Equal:
|
||||||
|
case CompareFunc::NotEqual:
|
||||||
|
case CompareFunc::LessThan:
|
||||||
|
case CompareFunc::LessThanOrEqual:
|
||||||
|
case CompareFunc::GreaterThan:
|
||||||
|
case CompareFunc::GreaterThanOrEqual: {
|
||||||
|
const Id alpha_scaled{OpFMul(f32_id, OpCompositeExtract(f32_id, last_tex_env_out, 3), ConstF32(255.f))};
|
||||||
|
const Id alpha_int{OpConvertFToS(i32_id, alpha_scaled)};
|
||||||
|
const Id alphatest_ref{GetShaderDataMember(i32_id, ConstS32(1))};
|
||||||
|
const Id alpha_comp_ref{Compare(alpha_int, alphatest_ref)};
|
||||||
|
const Id kill_label{OpLabel()};
|
||||||
|
const Id keep_label{OpLabel()};
|
||||||
|
OpSelectionMerge(keep_label, spv::SelectionControlMask::MaskNone);
|
||||||
|
OpBranchConditional(alpha_comp_ref, kill_label, keep_label);
|
||||||
|
AddLabel(kill_label);
|
||||||
|
OpKill();
|
||||||
|
AddLabel(keep_label);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
LOG_CRITICAL(Render_Vulkan, "Unknown alpha test condition {}", func);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Id FragmentModule::SampleTexture(u32 texture_unit) {
|
Id FragmentModule::SampleTexture(u32 texture_unit) {
|
||||||
const PicaFSConfigState& state = config.state;
|
const PicaFSConfigState& state = config.state;
|
||||||
|
const Id zero_vec{ConstF32(0.f, 0.f, 0.f, 0.f)};
|
||||||
|
|
||||||
// PICA's LOD formula for 2D textures.
|
// PICA's LOD formula for 2D textures.
|
||||||
// This LOD formula is the same as the LOD lower limit defined in OpenGL.
|
// This LOD formula is the same as the LOD lower limit defined in OpenGL.
|
||||||
@ -549,11 +605,11 @@ Id FragmentModule::SampleTexture(u32 texture_unit) {
|
|||||||
//case Pica::TexturingRegs::TextureConfig::ShadowCube:
|
//case Pica::TexturingRegs::TextureConfig::ShadowCube:
|
||||||
//return "shadowTextureCube(texcoord0, texcoord0_w)";
|
//return "shadowTextureCube(texcoord0, texcoord0_w)";
|
||||||
case Pica::TexturingRegs::TextureConfig::Disabled:
|
case Pica::TexturingRegs::TextureConfig::Disabled:
|
||||||
return ConstF32(0.f, 0.f, 0.f, 0.f);
|
return zero_vec;
|
||||||
default:
|
default:
|
||||||
LOG_CRITICAL(Render_Vulkan, "Unhandled texture type {:x}", state.texture0_type);
|
LOG_CRITICAL(Render_Vulkan, "Unhandled texture type {:x}", state.texture0_type);
|
||||||
UNIMPLEMENTED();
|
UNIMPLEMENTED();
|
||||||
return void_id;
|
return zero_vec;
|
||||||
}
|
}
|
||||||
case 1:
|
case 1:
|
||||||
return SampleLod(tex1_id, tex1_sampler_id, texcoord1_id);
|
return SampleLod(tex1_id, tex1_sampler_id, texcoord1_id);
|
||||||
@ -567,7 +623,7 @@ Id FragmentModule::SampleTexture(u32 texture_unit) {
|
|||||||
//return "ProcTex()";
|
//return "ProcTex()";
|
||||||
} else {
|
} else {
|
||||||
LOG_DEBUG(Render_Vulkan, "Using Texture3 without enabling it");
|
LOG_DEBUG(Render_Vulkan, "Using Texture3 without enabling it");
|
||||||
return ConstF32(0.f, 0.f, 0.f, 0.f);
|
return zero_vec;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
|
@ -41,6 +41,10 @@ public:
|
|||||||
/// Writes the code to emulate the specified TEV stage
|
/// Writes the code to emulate the specified TEV stage
|
||||||
void WriteTevStage(s32 index);
|
void WriteTevStage(s32 index);
|
||||||
|
|
||||||
|
/// Writes the if-statement condition used to evaluate alpha testing.
|
||||||
|
/// Returns true if the fragment was discarded
|
||||||
|
[[nodiscard]] bool WriteAlphaTestCondition(Pica::FramebufferRegs::CompareFunc func);
|
||||||
|
|
||||||
/// Samples the current fragment texel from the provided texture unit
|
/// Samples the current fragment texel from the provided texture unit
|
||||||
[[nodiscard]] Id SampleTexture(u32 texture_unit);
|
[[nodiscard]] Id SampleTexture(u32 texture_unit);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user