mirror of
https://github.com/PabloMK7/citra
synced 2024-11-15 05:08:23 +00:00
renderer/vulkan: Emulate custom border colors in shaders when unavailable. (#6878)
This commit is contained in:
parent
f3d92dd3b8
commit
6ddf4b241f
10 changed files with 322 additions and 195 deletions
|
@ -12,8 +12,16 @@ set(HASH_FILES
|
||||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_gen.h"
|
"${VIDEO_CORE}/renderer_opengl/gl_shader_gen.h"
|
||||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_util.cpp"
|
"${VIDEO_CORE}/renderer_opengl/gl_shader_util.cpp"
|
||||||
"${VIDEO_CORE}/renderer_opengl/gl_shader_util.h"
|
"${VIDEO_CORE}/renderer_opengl/gl_shader_util.h"
|
||||||
|
"${VIDEO_CORE}/renderer_vulkan/vk_shader_gen.cpp"
|
||||||
|
"${VIDEO_CORE}/renderer_vulkan/vk_shader_gen.h"
|
||||||
|
"${VIDEO_CORE}/renderer_vulkan/vk_shader_gen_spv.cpp"
|
||||||
|
"${VIDEO_CORE}/renderer_vulkan/vk_shader_gen_spv.h"
|
||||||
|
"${VIDEO_CORE}/renderer_vulkan/vk_shader_util.cpp"
|
||||||
|
"${VIDEO_CORE}/renderer_vulkan/vk_shader_util.h"
|
||||||
"${VIDEO_CORE}/shader/shader.cpp"
|
"${VIDEO_CORE}/shader/shader.cpp"
|
||||||
"${VIDEO_CORE}/shader/shader.h"
|
"${VIDEO_CORE}/shader/shader.h"
|
||||||
|
"${VIDEO_CORE}/shader/shader_uniforms.cpp"
|
||||||
|
"${VIDEO_CORE}/shader/shader_uniforms.h"
|
||||||
"${VIDEO_CORE}/pica.cpp"
|
"${VIDEO_CORE}/pica.cpp"
|
||||||
"${VIDEO_CORE}/pica.h"
|
"${VIDEO_CORE}/pica.h"
|
||||||
"${VIDEO_CORE}/regs_framebuffer.h"
|
"${VIDEO_CORE}/regs_framebuffer.h"
|
||||||
|
|
|
@ -599,6 +599,17 @@ void RasterizerAccelerated::NotifyPicaRegisterChanged(u32 id) {
|
||||||
SyncTextureLodBias(2);
|
SyncTextureLodBias(2);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// Texture borders
|
||||||
|
case PICA_REG_INDEX(texturing.texture0.border_color):
|
||||||
|
SyncTextureBorderColor(0);
|
||||||
|
break;
|
||||||
|
case PICA_REG_INDEX(texturing.texture1.border_color):
|
||||||
|
SyncTextureBorderColor(1);
|
||||||
|
break;
|
||||||
|
case PICA_REG_INDEX(texturing.texture2.border_color):
|
||||||
|
SyncTextureBorderColor(2);
|
||||||
|
break;
|
||||||
|
|
||||||
// Clipping plane
|
// Clipping plane
|
||||||
case PICA_REG_INDEX(rasterizer.clip_coef[0]):
|
case PICA_REG_INDEX(rasterizer.clip_coef[0]):
|
||||||
case PICA_REG_INDEX(rasterizer.clip_coef[1]):
|
case PICA_REG_INDEX(rasterizer.clip_coef[1]):
|
||||||
|
@ -821,6 +832,16 @@ void RasterizerAccelerated::SyncTextureLodBias(int tex_index) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RasterizerAccelerated::SyncTextureBorderColor(int tex_index) {
|
||||||
|
const auto pica_textures = regs.texturing.GetTextures();
|
||||||
|
const auto params = pica_textures[tex_index].config;
|
||||||
|
const Common::Vec4f border_color = ColorRGBA8(params.border_color.raw);
|
||||||
|
if (border_color != uniform_block_data.data.tex_border_color[tex_index]) {
|
||||||
|
uniform_block_data.data.tex_border_color[tex_index] = border_color;
|
||||||
|
uniform_block_data.dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void RasterizerAccelerated::SyncClipCoef() {
|
void RasterizerAccelerated::SyncClipCoef() {
|
||||||
const auto raw_clip_coef = regs.rasterizer.GetClipCoef();
|
const auto raw_clip_coef = regs.rasterizer.GetClipCoef();
|
||||||
const Common::Vec4f new_clip_coef = {raw_clip_coef.x.ToFloat32(), raw_clip_coef.y.ToFloat32(),
|
const Common::Vec4f new_clip_coef = {raw_clip_coef.x.ToFloat32(), raw_clip_coef.y.ToFloat32(),
|
||||||
|
|
|
@ -97,6 +97,9 @@ protected:
|
||||||
/// Syncs the texture LOD bias to match the PICA register
|
/// Syncs the texture LOD bias to match the PICA register
|
||||||
void SyncTextureLodBias(int tex_index);
|
void SyncTextureLodBias(int tex_index);
|
||||||
|
|
||||||
|
/// Syncs the texture border color to match the PICA registers
|
||||||
|
void SyncTextureBorderColor(int tex_index);
|
||||||
|
|
||||||
/// Syncs the clip coefficients to match the PICA register
|
/// Syncs the clip coefficients to match the PICA register
|
||||||
void SyncClipCoef();
|
void SyncClipCoef();
|
||||||
|
|
||||||
|
|
|
@ -409,7 +409,9 @@ bool Instance::CreateDevice() {
|
||||||
const bool has_extended_dynamic_state =
|
const bool has_extended_dynamic_state =
|
||||||
add_extension(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, is_arm || is_qualcomm,
|
add_extension(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, is_arm || is_qualcomm,
|
||||||
"it is broken on Qualcomm and ARM drivers");
|
"it is broken on Qualcomm and ARM drivers");
|
||||||
const bool has_custom_border_color = add_extension(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
|
const bool has_custom_border_color =
|
||||||
|
add_extension(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, is_qualcomm,
|
||||||
|
"it is broken on most Qualcomm driver versions");
|
||||||
const bool has_index_type_uint8 = add_extension(VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME);
|
const bool has_index_type_uint8 = add_extension(VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME);
|
||||||
const bool has_pipeline_creation_cache_control =
|
const bool has_pipeline_creation_cache_control =
|
||||||
add_extension(VK_EXT_PIPELINE_CREATION_CACHE_CONTROL_EXTENSION_NAME);
|
add_extension(VK_EXT_PIPELINE_CREATION_CACHE_CONTROL_EXTENSION_NAME);
|
||||||
|
|
|
@ -69,6 +69,17 @@ PicaFSConfig::PicaFSConfig(const Pica::Regs& regs, const Instance& instance) {
|
||||||
|
|
||||||
state.texture2_use_coord1.Assign(regs.texturing.main_config.texture2_use_coord1 != 0);
|
state.texture2_use_coord1.Assign(regs.texturing.main_config.texture2_use_coord1 != 0);
|
||||||
|
|
||||||
|
const auto pica_textures = regs.texturing.GetTextures();
|
||||||
|
for (u32 tex_index = 0; tex_index < 3; tex_index++) {
|
||||||
|
const auto config = pica_textures[tex_index].config;
|
||||||
|
state.texture_border_color[tex_index].enable_s.Assign(
|
||||||
|
!instance.IsCustomBorderColorSupported() &&
|
||||||
|
config.wrap_s == TexturingRegs::TextureConfig::WrapMode::ClampToBorder);
|
||||||
|
state.texture_border_color[tex_index].enable_t.Assign(
|
||||||
|
!instance.IsCustomBorderColorSupported() &&
|
||||||
|
config.wrap_t == TexturingRegs::TextureConfig::WrapMode::ClampToBorder);
|
||||||
|
}
|
||||||
|
|
||||||
// Emulate logic op in the shader if not supported. This is mostly for mobile GPUs
|
// Emulate logic op in the shader if not supported. This is mostly for mobile GPUs
|
||||||
const bool emulate_logic_op = instance.NeedsLogicOpEmulation() &&
|
const bool emulate_logic_op = instance.NeedsLogicOpEmulation() &&
|
||||||
!Pica::g_state.regs.framebuffer.output_merger.alphablend_enable;
|
!Pica::g_state.regs.framebuffer.output_merger.alphablend_enable;
|
||||||
|
@ -284,54 +295,6 @@ static bool IsPassThroughTevStage(const TevStageConfig& stage) {
|
||||||
stage.GetColorMultiplier() == 1 && stage.GetAlphaMultiplier() == 1);
|
stage.GetColorMultiplier() == 1 && stage.GetAlphaMultiplier() == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string SampleTexture(const PicaFSConfig& config, unsigned texture_unit) {
|
|
||||||
const auto& state = config.state;
|
|
||||||
switch (texture_unit) {
|
|
||||||
case 0:
|
|
||||||
// Only unit 0 respects the texturing type
|
|
||||||
switch (state.texture0_type) {
|
|
||||||
case TexturingRegs::TextureConfig::Texture2D:
|
|
||||||
return "textureLod(tex0, texcoord0, getLod(texcoord0 * "
|
|
||||||
"vec2(textureSize(tex0, 0))) + tex_lod_bias[0])";
|
|
||||||
case TexturingRegs::TextureConfig::Projection2D:
|
|
||||||
// TODO (wwylele): find the exact LOD formula for projection texture
|
|
||||||
return "textureProj(tex0, vec3(texcoord0, texcoord0_w))";
|
|
||||||
case TexturingRegs::TextureConfig::TextureCube:
|
|
||||||
return "texture(tex_cube, vec3(texcoord0, texcoord0_w))";
|
|
||||||
case TexturingRegs::TextureConfig::Shadow2D:
|
|
||||||
return "shadowTexture(texcoord0, texcoord0_w)";
|
|
||||||
case TexturingRegs::TextureConfig::ShadowCube:
|
|
||||||
return "shadowTextureCube(texcoord0, texcoord0_w)";
|
|
||||||
case TexturingRegs::TextureConfig::Disabled:
|
|
||||||
return "vec4(0.0)";
|
|
||||||
default:
|
|
||||||
LOG_CRITICAL(HW_GPU, "Unhandled texture type {:x}", state.texture0_type);
|
|
||||||
UNIMPLEMENTED();
|
|
||||||
return "texture(tex0, texcoord0)";
|
|
||||||
}
|
|
||||||
case 1:
|
|
||||||
return "textureLod(tex1, texcoord1, getLod(texcoord1 * "
|
|
||||||
"vec2(textureSize(tex1, 0))) + tex_lod_bias[1])";
|
|
||||||
case 2:
|
|
||||||
if (state.texture2_use_coord1)
|
|
||||||
return "textureLod(tex2, texcoord1, getLod(texcoord1 * "
|
|
||||||
"vec2(textureSize(tex2, 0))) + tex_lod_bias[2])";
|
|
||||||
else
|
|
||||||
return "textureLod(tex2, texcoord2, getLod(texcoord2 * "
|
|
||||||
"vec2(textureSize(tex2, 0))) + tex_lod_bias[2])";
|
|
||||||
case 3:
|
|
||||||
if (state.proctex.enable) {
|
|
||||||
return "ProcTex()";
|
|
||||||
} else {
|
|
||||||
LOG_DEBUG(Render_OpenGL, "Using Texture3 without enabling it");
|
|
||||||
return "vec4(0.0)";
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
UNREACHABLE();
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Writes the specified TEV stage source component(s)
|
/// Writes the specified TEV stage source component(s)
|
||||||
static void AppendSource(std::string& out, const PicaFSConfig& config,
|
static void AppendSource(std::string& out, const PicaFSConfig& config,
|
||||||
TevStageConfig::Source source, std::string_view index_name) {
|
TevStageConfig::Source source, std::string_view index_name) {
|
||||||
|
@ -347,16 +310,16 @@ static void AppendSource(std::string& out, const PicaFSConfig& config,
|
||||||
out += "secondary_fragment_color";
|
out += "secondary_fragment_color";
|
||||||
break;
|
break;
|
||||||
case Source::Texture0:
|
case Source::Texture0:
|
||||||
out += SampleTexture(config, 0);
|
out += "sampleTexUnit0()";
|
||||||
break;
|
break;
|
||||||
case Source::Texture1:
|
case Source::Texture1:
|
||||||
out += SampleTexture(config, 1);
|
out += "sampleTexUnit1()";
|
||||||
break;
|
break;
|
||||||
case Source::Texture2:
|
case Source::Texture2:
|
||||||
out += SampleTexture(config, 2);
|
out += "sampleTexUnit2()";
|
||||||
break;
|
break;
|
||||||
case Source::Texture3:
|
case Source::Texture3:
|
||||||
out += SampleTexture(config, 3);
|
out += "sampleTexUnit3()";
|
||||||
break;
|
break;
|
||||||
case Source::PreviousBuffer:
|
case Source::PreviousBuffer:
|
||||||
out += "combiner_buffer";
|
out += "combiner_buffer";
|
||||||
|
@ -656,7 +619,7 @@ static void WriteLighting(std::string& out, const PicaFSConfig& config) {
|
||||||
|
|
||||||
// Compute fragment normals and tangents
|
// Compute fragment normals and tangents
|
||||||
const auto perturbation = [&] {
|
const auto perturbation = [&] {
|
||||||
return fmt::format("2.0 * ({}).rgb - 1.0", SampleTexture(config, lighting.bump_selector));
|
return fmt::format("2.0 * (sampleTexUnit{}()).rgb - 1.0", lighting.bump_selector);
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (lighting.bump_mode) {
|
switch (lighting.bump_mode) {
|
||||||
|
@ -700,7 +663,7 @@ static void WriteLighting(std::string& out, const PicaFSConfig& config) {
|
||||||
"vec3 tangent = quaternion_rotate(normalized_normquat, surface_tangent);\n";
|
"vec3 tangent = quaternion_rotate(normalized_normquat, surface_tangent);\n";
|
||||||
|
|
||||||
if (lighting.enable_shadow) {
|
if (lighting.enable_shadow) {
|
||||||
std::string shadow_texture = SampleTexture(config, lighting.shadow_selector);
|
std::string shadow_texture = fmt::format("sampleTexUnit{}()", lighting.shadow_selector);
|
||||||
if (lighting.shadow_invert) {
|
if (lighting.shadow_invert) {
|
||||||
out += fmt::format("vec4 shadow = vec4(1.0) - {};\n", shadow_texture);
|
out += fmt::format("vec4 shadow = vec4(1.0) - {};\n", shadow_texture);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1310,6 +1273,7 @@ float mix2(vec4 s, vec2 a) {
|
||||||
|
|
||||||
vec4 shadowTexture(vec2 uv, float w) {
|
vec4 shadowTexture(vec2 uv, float w) {
|
||||||
)";
|
)";
|
||||||
|
|
||||||
if (!config.state.shadow_texture_orthographic) {
|
if (!config.state.shadow_texture_orthographic) {
|
||||||
out += "uv /= w;";
|
out += "uv /= w;";
|
||||||
}
|
}
|
||||||
|
@ -1344,9 +1308,7 @@ vec4 shadowTextureCube(vec2 uv, float w) {
|
||||||
uv = -c.xy;
|
uv = -c.xy;
|
||||||
if (c.z > 0.0) uv.x = -uv.x;
|
if (c.z > 0.0) uv.x = -uv.x;
|
||||||
}
|
}
|
||||||
)";
|
uint z = uint(max(0, int(min(w, 1.0) * float(0xFFFFFF)) - shadow_texture_bias));
|
||||||
out += "uint z = uint(max(0, int(min(w, 1.0) * float(0xFFFFFF)) - shadow_texture_bias));";
|
|
||||||
out += R"(
|
|
||||||
vec2 coord = vec2(size) * (uv / w * vec2(0.5) + vec2(0.5)) - vec2(0.5);
|
vec2 coord = vec2(size) * (uv / w * vec2(0.5) + vec2(0.5)) - vec2(0.5);
|
||||||
vec2 coord_floor = floor(coord);
|
vec2 coord_floor = floor(coord);
|
||||||
vec2 f = coord - coord_floor;
|
vec2 f = coord - coord_floor;
|
||||||
|
@ -1409,10 +1371,92 @@ vec4 shadowTextureCube(vec2 uv, float w) {
|
||||||
CompareShadow(pixels.w, z));
|
CompareShadow(pixels.w, z));
|
||||||
return vec4(mix2(s, f));
|
return vec4(mix2(s, f));
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
|
||||||
if (config.state.proctex.enable)
|
if (config.state.proctex.enable) {
|
||||||
AppendProcTexSampler(out, config);
|
AppendProcTexSampler(out, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (u32 texture_unit = 0; texture_unit < 4; texture_unit++) {
|
||||||
|
out += fmt::format("vec4 sampleTexUnit{}() {{", texture_unit);
|
||||||
|
if (texture_unit == 0 && state.texture0_type == TexturingRegs::TextureConfig::Disabled) {
|
||||||
|
out += "return vec4(0.0);}";
|
||||||
|
continue;
|
||||||
|
} else if (texture_unit == 3) {
|
||||||
|
if (state.proctex.enable) {
|
||||||
|
out += "return ProcTex();}";
|
||||||
|
} else {
|
||||||
|
out += "return vec4(0.0);}";
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 texcoord_num = texture_unit == 2 && state.texture2_use_coord1 ? 1 : texture_unit;
|
||||||
|
if (config.state.texture_border_color[texture_unit].enable_s) {
|
||||||
|
out += fmt::format(R"(
|
||||||
|
if (texcoord{}.x < 0 || texcoord{}.x > 1) {{
|
||||||
|
return tex_border_color[{}];
|
||||||
|
}}
|
||||||
|
)",
|
||||||
|
texcoord_num, texcoord_num, texture_unit);
|
||||||
|
}
|
||||||
|
if (config.state.texture_border_color[texture_unit].enable_t) {
|
||||||
|
out += fmt::format(R"(
|
||||||
|
if (texcoord{}.y < 0 || texcoord{}.y > 1) {{
|
||||||
|
return tex_border_color[{}];
|
||||||
|
}}
|
||||||
|
)",
|
||||||
|
texcoord_num, texcoord_num, texture_unit);
|
||||||
|
}
|
||||||
|
// TODO: 3D border?
|
||||||
|
|
||||||
|
switch (texture_unit) {
|
||||||
|
case 0:
|
||||||
|
// Only unit 0 respects the texturing type
|
||||||
|
switch (state.texture0_type) {
|
||||||
|
case TexturingRegs::TextureConfig::Texture2D:
|
||||||
|
out += "return textureLod(tex0, texcoord0, getLod(texcoord0 * "
|
||||||
|
"vec2(textureSize(tex0, 0))) + tex_lod_bias[0]);";
|
||||||
|
break;
|
||||||
|
case TexturingRegs::TextureConfig::Projection2D:
|
||||||
|
// TODO (wwylele): find the exact LOD formula for projection texture
|
||||||
|
out += "return textureProj(tex0, vec3(texcoord0, texcoord0_w));";
|
||||||
|
break;
|
||||||
|
case TexturingRegs::TextureConfig::TextureCube:
|
||||||
|
out += "return texture(tex_cube, vec3(texcoord0, texcoord0_w));";
|
||||||
|
break;
|
||||||
|
case TexturingRegs::TextureConfig::Shadow2D:
|
||||||
|
out += "return shadowTexture(texcoord0, texcoord0_w);";
|
||||||
|
break;
|
||||||
|
case TexturingRegs::TextureConfig::ShadowCube:
|
||||||
|
out += "return shadowTextureCube(texcoord0, texcoord0_w);";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG_CRITICAL(HW_GPU, "Unhandled texture type {:x}", state.texture0_type);
|
||||||
|
UNIMPLEMENTED();
|
||||||
|
out += "return texture(tex0, texcoord0);";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
out += "return textureLod(tex1, texcoord1, getLod(texcoord1 * vec2(textureSize(tex1, "
|
||||||
|
"0))) + tex_lod_bias[1]);";
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if (state.texture2_use_coord1) {
|
||||||
|
out += "return textureLod(tex2, texcoord1, getLod(texcoord1 * "
|
||||||
|
"vec2(textureSize(tex2, 0))) + tex_lod_bias[1]);";
|
||||||
|
} else {
|
||||||
|
out += "return textureLod(tex2, texcoord2, getLod(texcoord2 * "
|
||||||
|
"vec2(textureSize(tex2, 0))) + tex_lod_bias[2]);";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
out += "}";
|
||||||
|
}
|
||||||
|
|
||||||
// We round the interpolated primary color to the nearest 1/255th
|
// We round the interpolated primary color to the nearest 1/255th
|
||||||
// This maintains the PICA's 8 bits of precision
|
// This maintains the PICA's 8 bits of precision
|
||||||
|
|
|
@ -57,6 +57,11 @@ struct PicaFSConfigState {
|
||||||
BitField<28, 1, u32> shadow_texture_orthographic;
|
BitField<28, 1, u32> shadow_texture_orthographic;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
union {
|
||||||
|
BitField<0, 1, u32> enable_s;
|
||||||
|
BitField<1, 1, u32> enable_t;
|
||||||
|
} texture_border_color[3];
|
||||||
|
|
||||||
std::array<TevStageConfigRaw, 6> tev_stages;
|
std::array<TevStageConfigRaw, 6> tev_stages;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
|
|
@ -21,8 +21,8 @@ FragmentModule::FragmentModule(Core::TelemetrySession& telemetry_, const PicaFSC
|
||||||
DefineArithmeticTypes();
|
DefineArithmeticTypes();
|
||||||
DefineUniformStructs();
|
DefineUniformStructs();
|
||||||
DefineInterface();
|
DefineInterface();
|
||||||
if (config.state.proctex.enable) {
|
for (u32 i = 0; i < NUM_TEX_UNITS; i++) {
|
||||||
DefineProcTexSampler();
|
DefineTexSampler(i);
|
||||||
}
|
}
|
||||||
DefineEntryPoint();
|
DefineEntryPoint();
|
||||||
}
|
}
|
||||||
|
@ -225,7 +225,8 @@ void FragmentModule::WriteLighting() {
|
||||||
|
|
||||||
// Compute fragment normals and tangents
|
// Compute fragment normals and tangents
|
||||||
const auto perturbation = [&]() -> Id {
|
const auto perturbation = [&]() -> Id {
|
||||||
const Id texel{SampleTexture(lighting.bump_selector)};
|
const Id texel{
|
||||||
|
OpFunctionCall(vec_ids.Get(4), sample_tex_unit_func[lighting.bump_selector])};
|
||||||
const Id texel_rgb{OpVectorShuffle(vec_ids.Get(3), texel, texel, 0, 1, 2)};
|
const Id texel_rgb{OpVectorShuffle(vec_ids.Get(3), texel, texel, 0, 1, 2)};
|
||||||
const Id rgb_mul_two{OpVectorTimesScalar(vec_ids.Get(3), texel_rgb, ConstF32(2.f))};
|
const Id rgb_mul_two{OpVectorTimesScalar(vec_ids.Get(3), texel_rgb, ConstF32(2.f))};
|
||||||
return OpFSub(vec_ids.Get(3), rgb_mul_two, ConstF32(1.f, 1.f, 1.f));
|
return OpFSub(vec_ids.Get(3), rgb_mul_two, ConstF32(1.f, 1.f, 1.f));
|
||||||
|
@ -284,7 +285,7 @@ void FragmentModule::WriteLighting() {
|
||||||
|
|
||||||
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) {
|
if (lighting.enable_shadow) {
|
||||||
shadow = SampleTexture(lighting.shadow_selector);
|
shadow = OpFunctionCall(vec_ids.Get(4), sample_tex_unit_func[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);
|
||||||
}
|
}
|
||||||
|
@ -710,89 +711,6 @@ void FragmentModule::WriteAlphaTestCondition(FramebufferRegs::CompareFunc func)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Id FragmentModule::SampleTexture(u32 texture_unit) {
|
|
||||||
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.
|
|
||||||
// This LOD formula is the same as the LOD lower limit defined in OpenGL.
|
|
||||||
// f(x, y) >= max{m_u, m_v, m_w}
|
|
||||||
// (See OpenGL 4.6 spec, 8.14.1 - Scale Factor and Level-of-Detail)
|
|
||||||
const auto sample_lod = [this, texture_unit](Id tex_id, Id texcoord_id) {
|
|
||||||
const Id sampled_image{OpLoad(TypeSampledImage(image2d_id), tex_id)};
|
|
||||||
const Id tex_image{OpImage(image2d_id, sampled_image)};
|
|
||||||
const Id tex_size{OpImageQuerySizeLod(ivec_ids.Get(2), tex_image, ConstS32(0))};
|
|
||||||
const Id texcoord{OpLoad(vec_ids.Get(2), texcoord_id)};
|
|
||||||
const Id coord{OpFMul(vec_ids.Get(2), texcoord, OpConvertSToF(vec_ids.Get(2), tex_size))};
|
|
||||||
const Id abs_dfdx_coord{OpFAbs(vec_ids.Get(2), OpDPdx(vec_ids.Get(2), coord))};
|
|
||||||
const Id abs_dfdy_coord{OpFAbs(vec_ids.Get(2), OpDPdy(vec_ids.Get(2), coord))};
|
|
||||||
const Id d{OpFMax(vec_ids.Get(2), abs_dfdx_coord, abs_dfdy_coord)};
|
|
||||||
const Id dx_dy_max{
|
|
||||||
OpFMax(f32_id, OpCompositeExtract(f32_id, d, 0), OpCompositeExtract(f32_id, d, 1))};
|
|
||||||
const Id lod{OpLog2(f32_id, dx_dy_max)};
|
|
||||||
const Id lod_bias{GetShaderDataMember(f32_id, ConstS32(28), ConstU32(texture_unit))};
|
|
||||||
const Id biased_lod{OpFAdd(f32_id, lod, lod_bias)};
|
|
||||||
return OpImageSampleExplicitLod(vec_ids.Get(4), sampled_image, texcoord,
|
|
||||||
spv::ImageOperandsMask::Lod, biased_lod);
|
|
||||||
};
|
|
||||||
|
|
||||||
const auto sample = [this](Id tex_id, bool projection) {
|
|
||||||
const Id image_type = tex_id.value == tex_cube_id.value ? image_cube_id : image2d_id;
|
|
||||||
const Id sampled_image{OpLoad(TypeSampledImage(image_type), tex_id)};
|
|
||||||
const Id texcoord0{OpLoad(vec_ids.Get(2), texcoord0_id)};
|
|
||||||
const Id texcoord0_w{OpLoad(f32_id, texcoord0_w_id)};
|
|
||||||
const Id coord{OpCompositeConstruct(vec_ids.Get(3),
|
|
||||||
OpCompositeExtract(f32_id, texcoord0, 0),
|
|
||||||
OpCompositeExtract(f32_id, texcoord0, 1), texcoord0_w)};
|
|
||||||
if (projection) {
|
|
||||||
return OpImageSampleProjImplicitLod(vec_ids.Get(4), sampled_image, coord);
|
|
||||||
} else {
|
|
||||||
return OpImageSampleImplicitLod(vec_ids.Get(4), sampled_image, coord);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (texture_unit) {
|
|
||||||
case 0:
|
|
||||||
// Only unit 0 respects the texturing type
|
|
||||||
switch (state.texture0_type) {
|
|
||||||
case Pica::TexturingRegs::TextureConfig::Texture2D:
|
|
||||||
return sample_lod(tex0_id, texcoord0_id);
|
|
||||||
case Pica::TexturingRegs::TextureConfig::Projection2D:
|
|
||||||
return sample(tex0_id, true);
|
|
||||||
case Pica::TexturingRegs::TextureConfig::TextureCube:
|
|
||||||
return sample(tex_cube_id, false);
|
|
||||||
case Pica::TexturingRegs::TextureConfig::Shadow2D:
|
|
||||||
return SampleShadow();
|
|
||||||
// case Pica::TexturingRegs::TextureConfig::ShadowCube:
|
|
||||||
// return "shadowTextureCube(texcoord0, texcoord0_w)";
|
|
||||||
case Pica::TexturingRegs::TextureConfig::Disabled:
|
|
||||||
return zero_vec;
|
|
||||||
default:
|
|
||||||
LOG_CRITICAL(Render_Vulkan, "Unhandled texture type {:x}", state.texture0_type);
|
|
||||||
UNIMPLEMENTED();
|
|
||||||
return zero_vec;
|
|
||||||
}
|
|
||||||
case 1:
|
|
||||||
return sample_lod(tex1_id, texcoord1_id);
|
|
||||||
case 2:
|
|
||||||
if (state.texture2_use_coord1) {
|
|
||||||
return sample_lod(tex2_id, texcoord1_id);
|
|
||||||
} else {
|
|
||||||
return sample_lod(tex2_id, texcoord2_id);
|
|
||||||
}
|
|
||||||
case 3:
|
|
||||||
if (state.proctex.enable) {
|
|
||||||
return OpFunctionCall(vec_ids.Get(4), proctex_func);
|
|
||||||
} else {
|
|
||||||
LOG_DEBUG(Render_Vulkan, "Using Texture3 without enabling it");
|
|
||||||
return zero_vec;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
UNREACHABLE();
|
|
||||||
return void_id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Id FragmentModule::CompareShadow(Id pixel, Id z) {
|
Id FragmentModule::CompareShadow(Id pixel, Id z) {
|
||||||
const Id pixel_d24{OpShiftRightLogical(u32_id, pixel, ConstS32(8))};
|
const Id pixel_d24{OpShiftRightLogical(u32_id, pixel, ConstS32(8))};
|
||||||
const Id pixel_s8{OpConvertUToF(f32_id, OpBitwiseAnd(u32_id, pixel, ConstU32(255u)))};
|
const Id pixel_s8{OpConvertUToF(f32_id, OpBitwiseAnd(u32_id, pixel, ConstU32(255u)))};
|
||||||
|
@ -802,7 +720,7 @@ Id FragmentModule::CompareShadow(Id pixel, Id z) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Id FragmentModule::SampleShadow() {
|
Id FragmentModule::SampleShadow() {
|
||||||
const Id texcoord0{OpLoad(vec_ids.Get(2), texcoord0_id)};
|
const Id texcoord0{OpLoad(vec_ids.Get(2), texcoord_id[0])};
|
||||||
const Id texcoord0_w{OpLoad(f32_id, texcoord0_w_id)};
|
const Id texcoord0_w{OpLoad(f32_id, texcoord0_w_id)};
|
||||||
const Id abs_min_w{OpFMul(f32_id, OpFMin(f32_id, OpFAbs(f32_id, texcoord0_w), ConstF32(1.f)),
|
const Id abs_min_w{OpFMul(f32_id, OpFMin(f32_id, OpFAbs(f32_id, texcoord0_w), ConstF32(1.f)),
|
||||||
ConstF32(16777215.f))};
|
ConstF32(16777215.f))};
|
||||||
|
@ -941,11 +859,145 @@ Id FragmentModule::AppendProcTexCombineAndMap(ProcTexCombiner combiner, Id u, Id
|
||||||
return ProcTexLookupLUT(offset, combined);
|
return ProcTexLookupLUT(offset, combined);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FragmentModule::DefineProcTexSampler() {
|
void FragmentModule::DefineTexSampler(u32 texture_unit) {
|
||||||
|
const PicaFSConfigState& state = config.state;
|
||||||
|
|
||||||
const Id func_type{TypeFunction(vec_ids.Get(4))};
|
const Id func_type{TypeFunction(vec_ids.Get(4))};
|
||||||
proctex_func = OpFunction(vec_ids.Get(4), spv::FunctionControlMask::MaskNone, func_type);
|
sample_tex_unit_func[texture_unit] =
|
||||||
|
OpFunction(vec_ids.Get(4), spv::FunctionControlMask::MaskNone, func_type);
|
||||||
AddLabel(OpLabel());
|
AddLabel(OpLabel());
|
||||||
|
|
||||||
|
const Id zero_vec{ConstF32(0.f, 0.f, 0.f, 0.f)};
|
||||||
|
|
||||||
|
if (texture_unit == 0 && state.texture0_type == TexturingRegs::TextureConfig::Disabled) {
|
||||||
|
OpReturnValue(zero_vec);
|
||||||
|
OpFunctionEnd();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (texture_unit == 3) {
|
||||||
|
if (state.proctex.enable) {
|
||||||
|
OpReturnValue(ProcTexSampler());
|
||||||
|
} else {
|
||||||
|
OpReturnValue(zero_vec);
|
||||||
|
}
|
||||||
|
OpFunctionEnd();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Id border_label{OpLabel()};
|
||||||
|
const Id not_border_label{OpLabel()};
|
||||||
|
|
||||||
|
u32 texcoord_num = texture_unit == 2 && state.texture2_use_coord1 ? 1 : texture_unit;
|
||||||
|
const Id texcoord{OpLoad(vec_ids.Get(2), texcoord_id[texcoord_num])};
|
||||||
|
|
||||||
|
auto& texture_border_color = state.texture_border_color[texture_unit];
|
||||||
|
if (texture_border_color.enable_s || texture_border_color.enable_t) {
|
||||||
|
const Id texcoord_s{OpCompositeExtract(f32_id, texcoord, 0)};
|
||||||
|
const Id texcoord_t{OpCompositeExtract(f32_id, texcoord, 1)};
|
||||||
|
|
||||||
|
const Id s_lt_zero{OpFOrdLessThan(bool_id, texcoord_s, ConstF32(0.0f))};
|
||||||
|
const Id s_gt_one{OpFOrdGreaterThan(bool_id, texcoord_s, ConstF32(1.0f))};
|
||||||
|
const Id t_lt_zero{OpFOrdLessThan(bool_id, texcoord_t, ConstF32(0.0f))};
|
||||||
|
const Id t_gt_one{OpFOrdGreaterThan(bool_id, texcoord_t, ConstF32(1.0f))};
|
||||||
|
|
||||||
|
Id cond{};
|
||||||
|
if (texture_border_color.enable_s && texture_border_color.enable_t) {
|
||||||
|
cond = OpAny(bool_id, OpCompositeConstruct(bvec_ids.Get(4), s_lt_zero, s_gt_one,
|
||||||
|
t_lt_zero, t_gt_one));
|
||||||
|
} else if (texture_border_color.enable_s) {
|
||||||
|
cond = OpAny(bool_id, OpCompositeConstruct(bvec_ids.Get(2), s_lt_zero, s_gt_one));
|
||||||
|
} else if (texture_border_color.enable_t) {
|
||||||
|
cond = OpAny(bool_id, OpCompositeConstruct(bvec_ids.Get(2), t_lt_zero, t_gt_one));
|
||||||
|
}
|
||||||
|
|
||||||
|
OpSelectionMerge(not_border_label, spv::SelectionControlMask::MaskNone);
|
||||||
|
OpBranchConditional(cond, border_label, not_border_label);
|
||||||
|
|
||||||
|
AddLabel(border_label);
|
||||||
|
const Id border_color{
|
||||||
|
GetShaderDataMember(vec_ids.Get(4), ConstS32(29), ConstU32(texture_unit))};
|
||||||
|
OpReturnValue(border_color);
|
||||||
|
|
||||||
|
AddLabel(not_border_label);
|
||||||
|
}
|
||||||
|
|
||||||
|
// PICA's LOD formula for 2D textures.
|
||||||
|
// This LOD formula is the same as the LOD lower limit defined in OpenGL.
|
||||||
|
// f(x, y) >= max{m_u, m_v, m_w}
|
||||||
|
// (See OpenGL 4.6 spec, 8.14.1 - Scale Factor and Level-of-Detail)
|
||||||
|
const auto sample_lod = [&](Id tex_id) {
|
||||||
|
const Id sampled_image{OpLoad(TypeSampledImage(image2d_id), tex_id)};
|
||||||
|
const Id tex_image{OpImage(image2d_id, sampled_image)};
|
||||||
|
const Id tex_size{OpImageQuerySizeLod(ivec_ids.Get(2), tex_image, ConstS32(0))};
|
||||||
|
const Id coord{OpFMul(vec_ids.Get(2), texcoord, OpConvertSToF(vec_ids.Get(2), tex_size))};
|
||||||
|
const Id abs_dfdx_coord{OpFAbs(vec_ids.Get(2), OpDPdx(vec_ids.Get(2), coord))};
|
||||||
|
const Id abs_dfdy_coord{OpFAbs(vec_ids.Get(2), OpDPdy(vec_ids.Get(2), coord))};
|
||||||
|
const Id d{OpFMax(vec_ids.Get(2), abs_dfdx_coord, abs_dfdy_coord)};
|
||||||
|
const Id dx_dy_max{
|
||||||
|
OpFMax(f32_id, OpCompositeExtract(f32_id, d, 0), OpCompositeExtract(f32_id, d, 1))};
|
||||||
|
const Id lod{OpLog2(f32_id, dx_dy_max)};
|
||||||
|
const Id lod_bias{GetShaderDataMember(f32_id, ConstS32(28), ConstU32(texture_unit))};
|
||||||
|
const Id biased_lod{OpFAdd(f32_id, lod, lod_bias)};
|
||||||
|
return OpImageSampleExplicitLod(vec_ids.Get(4), sampled_image, texcoord,
|
||||||
|
spv::ImageOperandsMask::Lod, biased_lod);
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto sample_3d = [&](Id tex_id, bool projection) {
|
||||||
|
const Id image_type = tex_id.value == tex_cube_id.value ? image_cube_id : image2d_id;
|
||||||
|
const Id sampled_image{OpLoad(TypeSampledImage(image_type), tex_id)};
|
||||||
|
const Id texcoord0_w{OpLoad(f32_id, texcoord0_w_id)};
|
||||||
|
const Id coord{OpCompositeConstruct(vec_ids.Get(3), OpCompositeExtract(f32_id, texcoord, 0),
|
||||||
|
OpCompositeExtract(f32_id, texcoord, 1), texcoord0_w)};
|
||||||
|
if (projection) {
|
||||||
|
return OpImageSampleProjImplicitLod(vec_ids.Get(4), sampled_image, coord);
|
||||||
|
} else {
|
||||||
|
return OpImageSampleImplicitLod(vec_ids.Get(4), sampled_image, coord);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Id ret_val{void_id};
|
||||||
|
switch (texture_unit) {
|
||||||
|
case 0:
|
||||||
|
// Only unit 0 respects the texturing type
|
||||||
|
switch (state.texture0_type) {
|
||||||
|
case Pica::TexturingRegs::TextureConfig::Texture2D:
|
||||||
|
ret_val = sample_lod(tex0_id);
|
||||||
|
break;
|
||||||
|
case Pica::TexturingRegs::TextureConfig::Projection2D:
|
||||||
|
ret_val = sample_3d(tex0_id, true);
|
||||||
|
break;
|
||||||
|
case Pica::TexturingRegs::TextureConfig::TextureCube:
|
||||||
|
ret_val = sample_3d(tex_cube_id, false);
|
||||||
|
break;
|
||||||
|
case Pica::TexturingRegs::TextureConfig::Shadow2D:
|
||||||
|
ret_val = SampleShadow();
|
||||||
|
// case Pica::TexturingRegs::TextureConfig::ShadowCube:
|
||||||
|
// return "shadowTextureCube(texcoord0, texcoord0_w)";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG_CRITICAL(Render_Vulkan, "Unhandled texture type {:x}", state.texture0_type);
|
||||||
|
UNIMPLEMENTED();
|
||||||
|
ret_val = zero_vec;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
ret_val = sample_lod(tex1_id);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
ret_val = sample_lod(tex2_id);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
OpReturnValue(ret_val);
|
||||||
|
OpFunctionEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
Id FragmentModule::ProcTexSampler() {
|
||||||
// Define noise tables at the beginning of the function
|
// Define noise tables at the beginning of the function
|
||||||
if (config.state.proctex.noise_enable) {
|
if (config.state.proctex.noise_enable) {
|
||||||
noise1d_table =
|
noise1d_table =
|
||||||
|
@ -957,24 +1009,11 @@ void FragmentModule::DefineProcTexSampler() {
|
||||||
|
|
||||||
Id uv{};
|
Id uv{};
|
||||||
if (config.state.proctex.coord < 3) {
|
if (config.state.proctex.coord < 3) {
|
||||||
Id texcoord_id{};
|
const Id texcoord{OpLoad(vec_ids.Get(2), texcoord_id[config.state.proctex.coord.Value()])};
|
||||||
switch (config.state.proctex.coord.Value()) {
|
|
||||||
case 0:
|
|
||||||
texcoord_id = texcoord0_id;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
texcoord_id = texcoord1_id;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
texcoord_id = texcoord2_id;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Id texcoord{OpLoad(vec_ids.Get(2), texcoord_id)};
|
|
||||||
uv = OpFAbs(vec_ids.Get(2), texcoord);
|
uv = OpFAbs(vec_ids.Get(2), texcoord);
|
||||||
} else {
|
} else {
|
||||||
LOG_CRITICAL(Render_Vulkan, "Unexpected proctex.coord >= 3");
|
LOG_CRITICAL(Render_Vulkan, "Unexpected proctex.coord >= 3");
|
||||||
uv = OpFAbs(vec_ids.Get(2), OpLoad(vec_ids.Get(2), texcoord0_id));
|
uv = OpFAbs(vec_ids.Get(2), OpLoad(vec_ids.Get(2), texcoord_id[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
// This LOD formula is the same as the LOD upper limit defined in OpenGL.
|
// This LOD formula is the same as the LOD upper limit defined in OpenGL.
|
||||||
|
@ -1058,8 +1097,7 @@ void FragmentModule::DefineProcTexSampler() {
|
||||||
final_color = OpCompositeInsert(vec_ids.Get(4), final_alpha, final_color, 3);
|
final_color = OpCompositeInsert(vec_ids.Get(4), final_alpha, final_color, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
OpReturnValue(final_color);
|
return final_color;
|
||||||
OpFunctionEnd();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Id FragmentModule::Byteround(Id variable_id, u32 size) {
|
Id FragmentModule::Byteround(Id variable_id, u32 size) {
|
||||||
|
@ -1226,13 +1264,13 @@ Id FragmentModule::AppendSource(TevStageConfig::Source source, s32 index) {
|
||||||
case Source::SecondaryFragmentColor:
|
case Source::SecondaryFragmentColor:
|
||||||
return secondary_fragment_color;
|
return secondary_fragment_color;
|
||||||
case Source::Texture0:
|
case Source::Texture0:
|
||||||
return SampleTexture(0);
|
return OpFunctionCall(vec_ids.Get(4), sample_tex_unit_func[0]);
|
||||||
case Source::Texture1:
|
case Source::Texture1:
|
||||||
return SampleTexture(1);
|
return OpFunctionCall(vec_ids.Get(4), sample_tex_unit_func[1]);
|
||||||
case Source::Texture2:
|
case Source::Texture2:
|
||||||
return SampleTexture(2);
|
return OpFunctionCall(vec_ids.Get(4), sample_tex_unit_func[2]);
|
||||||
case Source::Texture3:
|
case Source::Texture3:
|
||||||
return SampleTexture(3);
|
return OpFunctionCall(vec_ids.Get(4), sample_tex_unit_func[3]);
|
||||||
case Source::PreviousBuffer:
|
case Source::PreviousBuffer:
|
||||||
return combiner_buffer;
|
return combiner_buffer;
|
||||||
case Source::Constant:
|
case Source::Constant:
|
||||||
|
@ -1428,9 +1466,9 @@ void FragmentModule::DefineEntryPoint() {
|
||||||
|
|
||||||
const Id main_type{TypeFunction(TypeVoid())};
|
const Id main_type{TypeFunction(TypeVoid())};
|
||||||
const Id main_func{OpFunction(TypeVoid(), spv::FunctionControlMask::MaskNone, main_type)};
|
const Id main_func{OpFunction(TypeVoid(), spv::FunctionControlMask::MaskNone, main_type)};
|
||||||
AddEntryPoint(spv::ExecutionModel::Fragment, main_func, "main", primary_color_id, texcoord0_id,
|
AddEntryPoint(spv::ExecutionModel::Fragment, main_func, "main", primary_color_id,
|
||||||
texcoord1_id, texcoord2_id, texcoord0_w_id, normquat_id, view_id, color_id,
|
texcoord_id[0], texcoord_id[1], texcoord_id[2], texcoord0_w_id, normquat_id,
|
||||||
gl_frag_coord_id, gl_frag_depth_id);
|
view_id, color_id, gl_frag_coord_id, gl_frag_depth_id);
|
||||||
AddExecutionMode(main_func, spv::ExecutionMode::OriginUpperLeft);
|
AddExecutionMode(main_func, spv::ExecutionMode::OriginUpperLeft);
|
||||||
AddExecutionMode(main_func, spv::ExecutionMode::DepthReplacing);
|
AddExecutionMode(main_func, spv::ExecutionMode::DepthReplacing);
|
||||||
}
|
}
|
||||||
|
@ -1443,21 +1481,25 @@ void FragmentModule::DefineUniformStructs() {
|
||||||
const Id light_src_array_id{TypeArray(light_src_struct_id, ConstU32(NUM_LIGHTS))};
|
const Id light_src_array_id{TypeArray(light_src_struct_id, ConstU32(NUM_LIGHTS))};
|
||||||
const Id lighting_lut_array_id{TypeArray(ivec_ids.Get(4), ConstU32(NUM_LIGHTING_SAMPLERS / 4))};
|
const Id lighting_lut_array_id{TypeArray(ivec_ids.Get(4), ConstU32(NUM_LIGHTING_SAMPLERS / 4))};
|
||||||
const Id const_color_array_id{TypeArray(vec_ids.Get(4), ConstU32(NUM_TEV_STAGES))};
|
const Id const_color_array_id{TypeArray(vec_ids.Get(4), ConstU32(NUM_TEV_STAGES))};
|
||||||
|
const Id border_color_array_id{TypeArray(vec_ids.Get(4), ConstU32(NUM_NON_PROC_TEX_UNITS))};
|
||||||
|
|
||||||
const Id shader_data_struct_id{TypeStruct(
|
const Id shader_data_struct_id{
|
||||||
i32_id, i32_id, f32_id, f32_id, f32_id, f32_id, i32_id, i32_id, i32_id, i32_id, i32_id,
|
TypeStruct(i32_id, i32_id, f32_id, f32_id, f32_id, f32_id, i32_id, i32_id, i32_id, i32_id,
|
||||||
i32_id, i32_id, i32_id, i32_id, i32_id, f32_id, i32_id, u32_id, lighting_lut_array_id,
|
i32_id, i32_id, i32_id, i32_id, i32_id, i32_id, f32_id, i32_id, u32_id,
|
||||||
vec_ids.Get(3), vec_ids.Get(2), vec_ids.Get(2), vec_ids.Get(2), vec_ids.Get(3),
|
lighting_lut_array_id, vec_ids.Get(3), vec_ids.Get(2), vec_ids.Get(2),
|
||||||
light_src_array_id, const_color_array_id, vec_ids.Get(4), vec_ids.Get(3), vec_ids.Get(4))};
|
vec_ids.Get(2), vec_ids.Get(3), light_src_array_id, const_color_array_id,
|
||||||
|
vec_ids.Get(4), vec_ids.Get(3), border_color_array_id, vec_ids.Get(4))};
|
||||||
|
|
||||||
constexpr std::array light_src_offsets{0u, 16u, 32u, 48u, 64u, 80u, 92u, 96u};
|
constexpr std::array light_src_offsets{0u, 16u, 32u, 48u, 64u, 80u, 92u, 96u};
|
||||||
constexpr std::array shader_data_offsets{
|
constexpr std::array shader_data_offsets{0u, 4u, 8u, 12u, 16u, 20u, 24u, 28u,
|
||||||
0u, 4u, 8u, 12u, 16u, 20u, 24u, 28u, 32u, 36u, 40u, 44u, 48u, 52u, 56u,
|
32u, 36u, 40u, 44u, 48u, 52u, 56u, 60u,
|
||||||
60u, 64u, 68u, 72u, 80u, 176u, 192u, 200u, 208u, 224u, 240u, 1136u, 1232u, 1248u, 1264u};
|
64u, 68u, 72u, 80u, 176u, 192u, 200u, 208u,
|
||||||
|
224u, 240u, 1136u, 1232u, 1248u, 1264u, 1312u};
|
||||||
|
|
||||||
Decorate(lighting_lut_array_id, spv::Decoration::ArrayStride, 16u);
|
Decorate(lighting_lut_array_id, spv::Decoration::ArrayStride, 16u);
|
||||||
Decorate(light_src_array_id, spv::Decoration::ArrayStride, 112u);
|
Decorate(light_src_array_id, spv::Decoration::ArrayStride, 112u);
|
||||||
Decorate(const_color_array_id, spv::Decoration::ArrayStride, 16u);
|
Decorate(const_color_array_id, spv::Decoration::ArrayStride, 16u);
|
||||||
|
Decorate(border_color_array_id, spv::Decoration::ArrayStride, 16u);
|
||||||
for (u32 i = 0; i < static_cast<u32>(light_src_offsets.size()); i++) {
|
for (u32 i = 0; i < static_cast<u32>(light_src_offsets.size()); i++) {
|
||||||
MemberDecorate(light_src_struct_id, i, spv::Decoration::Offset, light_src_offsets[i]);
|
MemberDecorate(light_src_struct_id, i, spv::Decoration::Offset, light_src_offsets[i]);
|
||||||
}
|
}
|
||||||
|
@ -1475,9 +1517,9 @@ void FragmentModule::DefineUniformStructs() {
|
||||||
void FragmentModule::DefineInterface() {
|
void FragmentModule::DefineInterface() {
|
||||||
// Define interface block
|
// Define interface block
|
||||||
primary_color_id = DefineInput(vec_ids.Get(4), 1);
|
primary_color_id = DefineInput(vec_ids.Get(4), 1);
|
||||||
texcoord0_id = DefineInput(vec_ids.Get(2), 2);
|
texcoord_id[0] = DefineInput(vec_ids.Get(2), 2);
|
||||||
texcoord1_id = DefineInput(vec_ids.Get(2), 3);
|
texcoord_id[1] = DefineInput(vec_ids.Get(2), 3);
|
||||||
texcoord2_id = DefineInput(vec_ids.Get(2), 4);
|
texcoord_id[2] = DefineInput(vec_ids.Get(2), 4);
|
||||||
texcoord0_w_id = DefineInput(f32_id, 5);
|
texcoord0_w_id = DefineInput(f32_id, 5);
|
||||||
normquat_id = DefineInput(vec_ids.Get(4), 6);
|
normquat_id = DefineInput(vec_ids.Get(4), 6);
|
||||||
view_id = DefineInput(vec_ids.Get(3), 7);
|
view_id = DefineInput(vec_ids.Get(3), 7);
|
||||||
|
|
|
@ -30,6 +30,8 @@ class FragmentModule : public Sirit::Module {
|
||||||
static constexpr u32 NUM_TEV_STAGES = 6;
|
static constexpr u32 NUM_TEV_STAGES = 6;
|
||||||
static constexpr u32 NUM_LIGHTS = 8;
|
static constexpr u32 NUM_LIGHTS = 8;
|
||||||
static constexpr u32 NUM_LIGHTING_SAMPLERS = 24;
|
static constexpr u32 NUM_LIGHTING_SAMPLERS = 24;
|
||||||
|
static constexpr u32 NUM_TEX_UNITS = 4;
|
||||||
|
static constexpr u32 NUM_NON_PROC_TEX_UNITS = 3;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit FragmentModule(Core::TelemetrySession& telemetry, const PicaFSConfig& config);
|
explicit FragmentModule(Core::TelemetrySession& telemetry, const PicaFSConfig& config);
|
||||||
|
@ -57,15 +59,15 @@ private:
|
||||||
/// 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);
|
||||||
|
|
||||||
/// Defines the tex3 proctex sampling function
|
/// Defines the basic texture sampling functions for a unit
|
||||||
void DefineProcTexSampler();
|
void DefineTexSampler(u32 texture_unit);
|
||||||
|
|
||||||
|
/// Function for sampling the procedurally generated texture unit.
|
||||||
|
Id ProcTexSampler();
|
||||||
|
|
||||||
/// Writes the if-statement condition used to evaluate alpha testing.
|
/// Writes the if-statement condition used to evaluate alpha testing.
|
||||||
void WriteAlphaTestCondition(Pica::FramebufferRegs::CompareFunc func);
|
void WriteAlphaTestCondition(Pica::FramebufferRegs::CompareFunc func);
|
||||||
|
|
||||||
/// Samples the current fragment texel from the provided texture unit
|
|
||||||
[[nodiscard]] Id SampleTexture(u32 texture_unit);
|
|
||||||
|
|
||||||
/// Samples the current fragment texel from shadow plane
|
/// Samples the current fragment texel from shadow plane
|
||||||
[[nodiscard]] Id SampleShadow();
|
[[nodiscard]] Id SampleShadow();
|
||||||
|
|
||||||
|
@ -237,9 +239,7 @@ private:
|
||||||
Id shader_data_id{};
|
Id shader_data_id{};
|
||||||
|
|
||||||
Id primary_color_id{};
|
Id primary_color_id{};
|
||||||
Id texcoord0_id{};
|
Id texcoord_id[NUM_NON_PROC_TEX_UNITS]{};
|
||||||
Id texcoord1_id{};
|
|
||||||
Id texcoord2_id{};
|
|
||||||
Id texcoord0_w_id{};
|
Id texcoord0_w_id{};
|
||||||
Id normquat_id{};
|
Id normquat_id{};
|
||||||
Id view_id{};
|
Id view_id{};
|
||||||
|
@ -276,7 +276,7 @@ private:
|
||||||
Id alpha_results_2{};
|
Id alpha_results_2{};
|
||||||
Id alpha_results_3{};
|
Id alpha_results_3{};
|
||||||
|
|
||||||
Id proctex_func{};
|
Id sample_tex_unit_func[NUM_TEX_UNITS]{};
|
||||||
Id noise1d_table{};
|
Id noise1d_table{};
|
||||||
Id noise2d_table{};
|
Id noise2d_table{};
|
||||||
Id lut_offsets{};
|
Id lut_offsets{};
|
||||||
|
|
|
@ -67,6 +67,7 @@ layout ({}std140) uniform shader_data {{
|
||||||
vec4 const_color[NUM_TEV_STAGES];
|
vec4 const_color[NUM_TEV_STAGES];
|
||||||
vec4 tev_combiner_buffer_color;
|
vec4 tev_combiner_buffer_color;
|
||||||
vec3 tex_lod_bias;
|
vec3 tex_lod_bias;
|
||||||
|
vec4 tex_border_color[3];
|
||||||
vec4 clip_coef;
|
vec4 clip_coef;
|
||||||
}};
|
}};
|
||||||
)";
|
)";
|
||||||
|
|
|
@ -64,10 +64,11 @@ struct UniformData {
|
||||||
alignas(16) Common::Vec4f const_color[6]; // A vec4 color for each of the six tev stages
|
alignas(16) Common::Vec4f const_color[6]; // A vec4 color for each of the six tev stages
|
||||||
alignas(16) Common::Vec4f tev_combiner_buffer_color;
|
alignas(16) Common::Vec4f tev_combiner_buffer_color;
|
||||||
alignas(16) Common::Vec3f tex_lod_bias;
|
alignas(16) Common::Vec3f tex_lod_bias;
|
||||||
|
alignas(16) Common::Vec4f tex_border_color[3];
|
||||||
alignas(16) Common::Vec4f clip_coef;
|
alignas(16) Common::Vec4f clip_coef;
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(sizeof(UniformData) == 0x500,
|
static_assert(sizeof(UniformData) == 0x530,
|
||||||
"The size of the UniformData does not match the structure in the shader");
|
"The size of the UniformData does not match the structure in the shader");
|
||||||
static_assert(sizeof(UniformData) < 16384,
|
static_assert(sizeof(UniformData) < 16384,
|
||||||
"UniformData structure must be less than 16kb as per the OpenGL spec");
|
"UniformData structure must be less than 16kb as per the OpenGL spec");
|
||||||
|
|
Loading…
Reference in a new issue