diff --git a/rpcs3/Emu/RSX/Common/TextureUtils.cpp b/rpcs3/Emu/RSX/Common/TextureUtils.cpp index b70f6669e3..0dc0fae3ea 100644 --- a/rpcs3/Emu/RSX/Common/TextureUtils.cpp +++ b/rpcs3/Emu/RSX/Common/TextureUtils.cpp @@ -1502,6 +1502,24 @@ namespace rsx } } + bool is_float_depth_format(rsx::surface_depth_format2 format) + { + switch (format) + { + case rsx::surface_depth_format2::z16_float: + case rsx::surface_depth_format2::z24s8_float: + return true; + default: + return false; + } + } + + f32 encode_emulated_depth(f32 depth) + { + // Halving the IEEE bit pattern of a non-negative float is monotonic and maps every finite value below 1 + return std::bit_cast((std::bit_cast(std::max(depth, 0.f)) & 0x7fffffffu) >> 1); + } + /** * Returns number of texel lines decoded in one pitch-length number of bytes */ diff --git a/rpcs3/Emu/RSX/Common/TextureUtils.h b/rpcs3/Emu/RSX/Common/TextureUtils.h index 9abb29b531..1998f4ba96 100644 --- a/rpcs3/Emu/RSX/Common/TextureUtils.h +++ b/rpcs3/Emu/RSX/Common/TextureUtils.h @@ -367,6 +367,10 @@ namespace rsx f32 get_depth_clear_value(rsx::surface_depth_format2 format, u32 raw); f32 clamp_depth_bounds_value(rsx::surface_depth_format2 format, f32 value); bool is_depth_stencil_format(rsx::surface_depth_format2 format); + bool is_float_depth_format(rsx::surface_depth_format2 format); + + // Storage encoding of float depth when the host cannot hold depth values above 1. Must match encode_emulated_depth in GLSL. + f32 encode_emulated_depth(f32 depth); /** * Format feature support. There is not simple format to determine what is supported here, results are from hw tests diff --git a/rpcs3/Emu/RSX/Core/RSXDrawCommands.cpp b/rpcs3/Emu/RSX/Core/RSXDrawCommands.cpp index 9610a7d394..b4dad4ac4f 100644 --- a/rpcs3/Emu/RSX/Core/RSXDrawCommands.cpp +++ b/rpcs3/Emu/RSX/Core/RSXDrawCommands.cpp @@ -696,6 +696,15 @@ namespace rsx rop_control.set_blend_target_mask(blend_enable_mask); } + if (fragment_program.ctrl & RSX_SHADER_CONTROL_EMULATE_DEPTH_RANGE) + { + // Hardware clipping is disabled for these draws, see the fragment epilogue + if (REGS(m_ctx)->depth_clamp_enabled() || !REGS(m_ctx)->depth_clip_enabled()) + { + rop_control.enable_depth_clamp(); + } + } + // Generate wpos coefficients // wpos equation is now as follows (ignoring pixel center offset): // wpos.y = (frag_coord / resolution_scale) * ((window_origin!=top)?-1.: 1.) + ((window_origin!=top)? window_height : 0) diff --git a/rpcs3/Emu/RSX/GL/GLCommonDecompiler.cpp b/rpcs3/Emu/RSX/GL/GLCommonDecompiler.cpp index c296b988a5..8c7417c693 100644 --- a/rpcs3/Emu/RSX/GL/GLCommonDecompiler.cpp +++ b/rpcs3/Emu/RSX/GL/GLCommonDecompiler.cpp @@ -20,7 +20,8 @@ namespace gl {"tc6", 12}, {"tc7", 13}, {"tc8", 14}, - {"tc9", 15} + {"tc9", 15}, + {"depth_range", 16} // clip range for depth range emulation }}; int get_varying_register_location(std::string_view varying_register_name) diff --git a/rpcs3/Emu/RSX/GL/GLCompute.cpp b/rpcs3/Emu/RSX/GL/GLCompute.cpp index c5acfb7697..0f9c518561 100644 --- a/rpcs3/Emu/RSX/GL/GLCompute.cpp +++ b/rpcs3/Emu/RSX/GL/GLCompute.cpp @@ -233,6 +233,12 @@ namespace gl work_kernel = fmt::replace_all(work_kernel, "bswap_u32(value)", "value", 1); } + if (gl::emulate_extended_depth_range()) + { + // Float depth surfaces hold the emulated encoding (half the bit pattern) + work_kernel = fmt::replace_all(work_kernel, "f32_to_d24f(depth)", "f32_to_d24f(depth << 1)"); + } + cs_shuffle_base::build(""); } @@ -290,6 +296,11 @@ namespace gl work_kernel = fmt::replace_all(work_kernel, "value = bswap_u32(value)", "// value = bswap_u32(value)", 1); } + if (gl::emulate_extended_depth_range()) + { + work_kernel = fmt::replace_all(work_kernel, "d24f_to_f32(depth)", "(d24f_to_f32(depth) >> 1)"); + } + cs_shuffle_base::build(""); } diff --git a/rpcs3/Emu/RSX/GL/GLCompute.h b/rpcs3/Emu/RSX/GL/GLCompute.h index 0b19aa663d..c49eaa079d 100644 --- a/rpcs3/Emu/RSX/GL/GLCompute.h +++ b/rpcs3/Emu/RSX/GL/GLCompute.h @@ -144,6 +144,12 @@ namespace gl " tmp = bswap_u32(tmp);\n"; } + if (gl::emulate_extended_depth_range()) + { + // Float depth surfaces hold the emulated encoding (half the bit pattern) + work_kernel += " tmp.xy <<= 1;\n"; + } + // Convert work_kernel += " tmp.z = pack_e4m12_pack16(tmp.xy);\n"; @@ -175,6 +181,11 @@ namespace gl // Convert work_kernel += " tmp.yz = unpack_e4m12_pack16(tmp.x);\n"; + if (gl::emulate_extended_depth_range()) + { + work_kernel += " tmp.yz >>= 1;\n"; + } + if constexpr (_SwapDst) { work_kernel += " tmp.yz = bswap_u32(tmp.yz);\n"; diff --git a/rpcs3/Emu/RSX/GL/GLDraw.cpp b/rpcs3/Emu/RSX/GL/GLDraw.cpp index cbf318070a..da6492e8ca 100644 --- a/rpcs3/Emu/RSX/GL/GLDraw.cpp +++ b/rpcs3/Emu/RSX/GL/GLDraw.cpp @@ -84,7 +84,16 @@ void GLGSRender::update_draw_state() { // Z-buffer is active. gl_state.depth_mask(rsx::method_registers.depth_write_enabled()); - gl_state.enable(rsx::method_registers.depth_clamp_enabled() || !rsx::method_registers.depth_clip_enabled(), GL_DEPTH_CLAMP); + const bool emulate_depth_range = requires_depth_range_emulation(); + if (!emulate_depth_range) [[ likely ]] + { + gl_state.enable(rsx::method_registers.depth_clamp_enabled() || !rsx::method_registers.depth_clip_enabled(), GL_DEPTH_CLAMP); + } + else + { + // Depth clip and clamp are emulated in the fragment shader + gl_state.enable(GL_DEPTH_CLAMP); + } if (gl_state.enable(rsx::method_registers.depth_test_enabled(), GL_DEPTH_TEST)) { @@ -95,9 +104,16 @@ void GLGSRender::update_draw_state() (gl_state.enable(REGS(m_ctx)->depth_bounds_test_enabled(), GL_DEPTH_BOUNDS_TEST_EXT))) { const auto depth_format = REGS(m_ctx)->surface_depth_fmt(); - gl_state.depth_bounds( - rsx::clamp_depth_bounds_value(depth_format, REGS(m_ctx)->depth_bounds_min()), - rsx::clamp_depth_bounds_value(depth_format, REGS(m_ctx)->depth_bounds_max())); + f32 bounds_min = rsx::clamp_depth_bounds_value(depth_format, REGS(m_ctx)->depth_bounds_min()); + f32 bounds_max = rsx::clamp_depth_bounds_value(depth_format, REGS(m_ctx)->depth_bounds_max()); + + if (emulate_depth_range) [[ unlikely ]] + { + bounds_min = rsx::encode_emulated_depth(bounds_min); + bounds_max = rsx::encode_emulated_depth(bounds_max); + } + + gl_state.depth_bounds(bounds_min, bounds_max); } if (gl::get_driver_caps().NV_depth_buffer_float_supported) diff --git a/rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp b/rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp index 4339362012..a696b81847 100644 --- a/rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp +++ b/rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp @@ -3,6 +3,7 @@ #include "Emu/system_config.h" #include "GLCommonDecompiler.h" +#include "GLHelpers.h" #include "../Program/GLSLCommon.h" std::string GLFragmentDecompilerThread::getFloatTypeName(usz elementCount) @@ -86,6 +87,11 @@ void GLFragmentDecompilerThread::insertInputs(std::stringstream & OS) }, gl::get_varying_register_location ); + + if (m_prog.ctrl & RSX_SHADER_CONTROL_EMULATE_DEPTH_RANGE) + { + OS << "layout(location=" << gl::get_varying_register_location("depth_range") << ") in flat vec2 depth_range;\n"; + } } void GLFragmentDecompilerThread::insertOutputs(std::stringstream & OS) @@ -227,7 +233,7 @@ void GLFragmentDecompilerThread::insertGlobalFunctions(std::stringstream &OS) m_shader_props.require_srgb_to_linear = properties.has_upg; m_shader_props.require_linear_to_srgb = properties.has_pkg; m_shader_props.require_fog_read = properties.in_register_mask & in_fogc; - m_shader_props.emulate_shadow_compare = device_props.emulate_depth_compare; + m_shader_props.emulate_shadow_compare = device_props.emulate_depth_compare || device_props.emulated_depth_storage; m_shader_props.low_precision_tests = ::gl::get_driver_caps().vendor_NVIDIA && !(m_prog.ctrl & RSX_SHADER_CONTROL_ATTRIBUTE_INTERPOLATION); m_shader_props.disable_early_discard = !::gl::get_driver_caps().vendor_NVIDIA; @@ -249,6 +255,9 @@ void GLFragmentDecompilerThread::insertGlobalFunctions(std::stringstream &OS) m_shader_props.require_color_format_convert = !!(m_prog.ctrl & RSX_SHADER_CONTROL_TEXTURE_FORMAT_CONVERT); m_shader_props.emulate_depth_compare = !!(m_prog.ctrl & RSX_SHADER_CONTROL_EMULATE_DEPTH_COMPARE); m_shader_props.ROP_output_multisampled = !!(m_prog.ctrl & RSX_SHADER_CONTROL_ROP_MULTISAMPLED); + m_shader_props.ROP_emulate_depth_range = !!(m_prog.ctrl & RSX_SHADER_CONTROL_EMULATE_DEPTH_RANGE); + m_shader_props.ROP_depth_export = !!(m_prog.ctrl & CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT) && m_shader_props.ROP_emulate_depth_range; + m_shader_props.emulated_depth_storage = device_props.emulated_depth_storage; glsl::insert_glsl_legacy_function(OS, m_shader_props); } @@ -372,6 +381,8 @@ void GLFragmentDecompilerThread::insertMainEnd(std::stringstream & OS) } } + glsl::insert_fragment_epilogue(OS, m_shader_props); + OS << "}\n"; } @@ -400,6 +411,7 @@ void GLFragmentProgram::Decompile(const RSXFragmentProgram& prog) decompiler.device_props.has_low_precision_rounding = driver_caps.vendor_NVIDIA; } + decompiler.device_props.emulated_depth_storage = gl::emulate_extended_depth_range(); decompiler.Task(); constant_offsets = std::move(decompiler.properties.constant_offsets); diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.cpp b/rpcs3/Emu/RSX/GL/GLGSRender.cpp index 7375c1053b..abc80450fa 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.cpp +++ b/rpcs3/Emu/RSX/GL/GLGSRender.cpp @@ -52,6 +52,7 @@ GLGSRender::GLGSRender(utils::serial* ar) noexcept : GSRender(ar) backend_config.supports_hw_instanced_rendering = true; // OpenGL 3.2+ defaults to GL_LAST_VERTEX_CONVENTION. backend_config.supports_last_provoking_vertex = true; + backend_config.supports_extended_depth_range = gl::get_driver_caps().NV_depth_buffer_float_supported; if (g_cfg.video.antialiasing_level != msaa_level::none) { @@ -673,7 +674,12 @@ void GLGSRender::clear_surface(u32 arg) { const u32 clear_depth_bits = REGS(m_ctx)->z_clear_value(is_depth_stencil_format(surface_depth_format)); clear_cmd.clear_depth.value = rsx::get_depth_clear_value(surface_depth_format, clear_depth_bits); - clear_cmd.aspect_mask |= gl::image_aspect::depth; + clear_cmd.aspect_mask |= gl::image_aspect::depth; + + if (gl::emulate_extended_depth_range() && rsx::is_float_depth_format(surface_depth_format)) [[ unlikely ]] + { + clear_cmd.clear_depth.value = rsx::encode_emulated_depth(clear_cmd.clear_depth.value); + } } if (is_depth_stencil_format(surface_depth_format)) @@ -861,12 +867,16 @@ bool GLGSRender::load_program() } void* pipeline_properties = nullptr; + // The shader interpreter does not emulate the depth range; compile those programs synchronously instead + const bool allow_async = shadermode != shader_mode::recompiler && + !(current_fragment_program.ctrl & RSX_SHADER_CONTROL_EMULATE_DEPTH_RANGE); + std::tie(m_program, m_vertex_prog, m_fragment_prog) = m_prog_buffer.get_graphics_pipeline( &m_program_cache_hint, current_vertex_program, current_fragment_program, pipeline_properties, - shadermode != shader_mode::recompiler, true); + allow_async, true); if (m_prog_buffer.check_cache_missed()) { diff --git a/rpcs3/Emu/RSX/GL/GLHelpers.cpp b/rpcs3/Emu/RSX/GL/GLHelpers.cpp index da95960f40..a85fdd0cfd 100644 --- a/rpcs3/Emu/RSX/GL/GLHelpers.cpp +++ b/rpcs3/Emu/RSX/GL/GLHelpers.cpp @@ -1,4 +1,5 @@ #include "stdafx.h" +#include "Emu/system_config.h" #include "GLHelpers.h" #include "GLCompute.h" #include "util/logs.hpp" @@ -186,4 +187,9 @@ namespace gl fmt::throw_exception("unknown primitive type"); } } + + bool emulate_extended_depth_range() + { + return !get_driver_caps().NV_depth_buffer_float_supported && g_cfg.video.emulate_extended_depth_range; + } } diff --git a/rpcs3/Emu/RSX/GL/GLHelpers.h b/rpcs3/Emu/RSX/GL/GLHelpers.h index 368cd9e124..4305faa4ba 100644 --- a/rpcs3/Emu/RSX/GL/GLHelpers.h +++ b/rpcs3/Emu/RSX/GL/GLHelpers.h @@ -35,5 +35,6 @@ namespace gl { void enable_debugging(); bool is_primitive_native(rsx::primitive_type in); + bool emulate_extended_depth_range(); GLenum draw_mode(rsx::primitive_type in); } diff --git a/rpcs3/Emu/RSX/GL/GLOverlays.cpp b/rpcs3/Emu/RSX/GL/GLOverlays.cpp index 22ad6181a6..e654d1891d 100644 --- a/rpcs3/Emu/RSX/GL/GLOverlays.cpp +++ b/rpcs3/Emu/RSX/GL/GLOverlays.cpp @@ -560,7 +560,8 @@ namespace gl { "%loc", std::to_string(GL_COMPUTE_BUFFER_SLOT(0)) }, { "%push_block", fmt::format("binding=%d, std140", GL_COMPUTE_BUFFER_SLOT(1)) }, { "%stencil_export_supported", stencil_export_supported ? "1" : "0" }, - { "%legacy_format_support", legacy_format_support ? "1" : "0" } + { "%legacy_format_support", legacy_format_support ? "1" : "0" }, + { "%emulated_depth_storage", gl::emulate_extended_depth_range() ? "1" : "0" } }; fs_src = fmt::replace_all(fs_src, repl_list); @@ -594,6 +595,11 @@ namespace gl program_handle.uniforms["src_pitch"] = row_length; program_handle.uniforms["swap_bytes"] = layout.swap_bytes; program_handle.uniforms["format"] = static_cast(dst->image()->get_internal_format()); + + if (gl::emulate_extended_depth_range()) + { + program_handle.uniforms["depth_float"] = (dst->image()->format_class() & RSX_FORMAT_CLASS_DEPTH_FLOAT_MASK) ? 1u : 0u; + } src->bind_range(gl::buffer::target::ssbo, GL_COMPUTE_BUFFER_SLOT(0), src_offset, row_length * bpp * dst_region.height); cmd->stencil_mask(0xFF); diff --git a/rpcs3/Emu/RSX/GL/GLVertexProgram.cpp b/rpcs3/Emu/RSX/GL/GLVertexProgram.cpp index b02238a485..1990f84d69 100644 --- a/rpcs3/Emu/RSX/GL/GLVertexProgram.cpp +++ b/rpcs3/Emu/RSX/GL/GLVertexProgram.cpp @@ -164,6 +164,11 @@ void GLVertexDecompilerThread::insertOutputs(std::stringstream& OS, const std::v << (flat_color ? "flat " : "") << "vec4 " << i.name << ";\n"; } } + + if (m_prog.ctrl & RSX_SHADER_CONTROL_EMULATE_DEPTH_RANGE) + { + OS << "layout(location=" << gl::get_varying_register_location("depth_range") << ") out flat vec2 depth_range;\n"; + } } void GLVertexDecompilerThread::insertMainStart(std::stringstream& OS) @@ -179,6 +184,7 @@ void GLVertexDecompilerThread::insertMainStart(std::stringstream& OS) properties2.require_explicit_invariance = dev_caps.vendor_MESA || (dev_caps.vendor_NVIDIA && g_cfg.video.shader_precision != gpu_preset_level::low); properties2.require_instanced_render = !!(m_prog.ctrl & RSX_SHADER_CONTROL_INSTANCED_CONSTANTS); properties2.require_clip_plane_functions = true; + properties2.emulate_depth_range = !!(m_prog.ctrl & RSX_SHADER_CONTROL_EMULATE_DEPTH_RANGE); insert_glsl_legacy_function(OS, properties2); glsl::insert_vertex_input_fetch(OS, glsl::glsl_rules_opengl4, dev_caps.vendor_INTEL == false); @@ -286,7 +292,17 @@ void GLVertexDecompilerThread::insertMainEnd(std::stringstream& OS) } OS << " gl_Position = gl_Position * scale_offset_mat;\n"; - OS << " gl_Position = apply_zclip_xform(gl_Position, z_near, z_far);\n"; + + if (!(m_prog.ctrl & RSX_SHADER_CONTROL_EMULATE_DEPTH_RANGE)) [[ likely ]] + { + OS << " gl_Position = apply_zclip_xform(gl_Position, z_near, z_far);\n"; + } + else + { + OS << + " depth_range = vec2(min(z_near, z_far), max(z_near, z_far));\n" + " gl_Position = apply_depth_range_xform(gl_Position, depth_range);\n"; + } // Since our clip_space is symmetrical [-1, 1] we map it to linear space using the eqn: // ln = (clip * 2) - 1 to fully utilize the 0-1 range of the depth buffer diff --git a/rpcs3/Emu/RSX/NV47/HW/nv4097.cpp b/rpcs3/Emu/RSX/NV47/HW/nv4097.cpp index 9ef0dfd3a7..f908610aa1 100644 --- a/rpcs3/Emu/RSX/NV47/HW/nv4097.cpp +++ b/rpcs3/Emu/RSX/NV47/HW/nv4097.cpp @@ -272,6 +272,22 @@ namespace rsx RSX(ctx)->m_graphics_state.clear(rtt_config_contested); } + void set_zmin_max_control(context* ctx, u32 /*reg*/, u32 arg) + { + if (arg == REGS(ctx)->latch) + { + return; + } + + RSX(ctx)->m_graphics_state |= rsx::pipeline_state::pipeline_config_dirty; + + // Depth clip and clamp are read by the fragment epilogue when the depth range is emulated + if (!RSX(ctx)->get_backend_config().supports_extended_depth_range && g_cfg.video.emulate_extended_depth_range) + { + RSX(ctx)->m_graphics_state |= rsx::pipeline_state::fragment_state_dirty; + } + } + void set_surface_format(context* ctx, u32 reg, u32 arg) { // The high bits of this register are just log2(dimension), ignore them diff --git a/rpcs3/Emu/RSX/NV47/HW/nv4097.h b/rpcs3/Emu/RSX/NV47/HW/nv4097.h index 70951a885b..110f456641 100644 --- a/rpcs3/Emu/RSX/NV47/HW/nv4097.h +++ b/rpcs3/Emu/RSX/NV47/HW/nv4097.h @@ -69,6 +69,8 @@ namespace rsx void set_surface_dirty_bit(context* ctx, u32 reg, u32 arg); + void set_zmin_max_control(context* ctx, u32 reg, u32 arg); + void set_surface_format(context* ctx, u32 reg, u32 arg); void set_surface_options_dirty_bit(context* ctx, u32 reg, u32 arg); diff --git a/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.h b/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.h index 8032abd6f2..8044044da8 100644 --- a/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.h +++ b/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.h @@ -195,6 +195,7 @@ public: bool has_native_half_support = false; bool emulate_depth_compare = false; bool has_low_precision_rounding = false; + bool emulated_depth_storage = false; } device_props; diff --git a/rpcs3/Emu/RSX/Program/GLSLCommon.cpp b/rpcs3/Emu/RSX/Program/GLSLCommon.cpp index 61e2836811..37bbff0642 100644 --- a/rpcs3/Emu/RSX/Program/GLSLCommon.cpp +++ b/rpcs3/Emu/RSX/Program/GLSLCommon.cpp @@ -170,6 +170,13 @@ namespace glsl ; } + void insert_fragment_epilogue(std::ostream& OS, const shader_properties& /*props*/) + { + OS << + #include "GLSLSnippets/RSXProg/RSXFragmentEpilogue.glsl" + ; + } + void insert_glsl_legacy_function(std::ostream& OS, const shader_properties& props) { std::vector enabled_options; @@ -208,6 +215,7 @@ namespace glsl { "MSAA_SAMPLE_CTRL_LENGTH ", rsx::ROP_control_bits::MSAA_SAMPLE_CTRL_NUM_BITS }, { "FRAG_DEPTH_24_BIT ", rsx::ROP_control_bits::FRAG_DEPTH_24_BIT }, { "FRAG_DEPTH_FLOAT_BIT ", rsx::ROP_control_bits::FRAG_DEPTH_FLOAT_BIT }, + { "DEPTH_CLAMP_ENABLE_BIT ", rsx::ROP_control_bits::DEPTH_CLAMP_ENABLE_BIT }, { "MRT_CHANNEL_REMAP_OFFSET ", rsx::ROP_control_bits::MRT_CHANNEL_REMAP_OFFSET }, { "MRT_CHANNEL_REMAP_LENGTH ", rsx::ROP_control_bits::MRT_CHANNEL_REMAP_NUM_BITS }, { "MRT_BLEND_TARGETS_OFFSET ", rsx::ROP_control_bits::MRT_BLEND_TARGETS_OFFSET }, @@ -274,6 +282,16 @@ namespace glsl { enabled_options.push_back("_ENABLE_ROP_OUTPUT_MULTISAMPLED"); } + + if (props.ROP_emulate_depth_range) + { + enabled_options.push_back("_EMULATE_DEPTH_RANGE"); + } + + if (props.ROP_depth_export) + { + enabled_options.push_back("_ENABLE_DEPTH_EXPORT"); + } } // Import common header @@ -308,6 +326,11 @@ namespace glsl enabled_options.push_back("_ENABLE_INSTANCED_CONSTANTS"); } + if (props.emulate_depth_range) + { + enabled_options.push_back("_EMULATE_DEPTH_RANGE"); + } + // Import vertex header program_common::define_glsl_switches(OS, enabled_options); @@ -373,6 +396,12 @@ namespace glsl enabled_options.push_back("_ENABLE_FOG_READ"); } + // Programs with the emulation bit always need the encoding, even if they were cached while the option was off + if (props.emulated_depth_storage || props.ROP_emulate_depth_range) + { + enabled_options.push_back("_EMULATED_DEPTH_STORAGE"); + } + // Import fragment header program_common::define_glsl_switches(OS, enabled_options); enabled_options.clear(); diff --git a/rpcs3/Emu/RSX/Program/GLSLCommon.h b/rpcs3/Emu/RSX/Program/GLSLCommon.h index 816bf2d7f6..5d929dca0b 100644 --- a/rpcs3/Emu/RSX/Program/GLSLCommon.h +++ b/rpcs3/Emu/RSX/Program/GLSLCommon.h @@ -23,6 +23,7 @@ namespace rsx MSAA_WRITE_ENABLE_BIT = 9, FRAG_DEPTH_24_BIT = 10, FRAG_DEPTH_FLOAT_BIT = 11, + DEPTH_CLAMP_ENABLE_BIT = 24, // Data ALPHA_FUNC_OFFSET = 12, @@ -51,6 +52,7 @@ namespace rsx void enable_framebuffer_INT() { value |= (1u << ROP_control_bits::INT_FRAMEBUFFER_BIT); } void enable_MSAA_writes() { value |= (1u << ROP_control_bits::MSAA_WRITE_ENABLE_BIT); } + void enable_depth_clamp() { value |= (1u << ROP_control_bits::DEPTH_CLAMP_ENABLE_BIT); } void set_alpha_test_func(uint func) { value |= (func << ROP_control_bits::ALPHA_FUNC_OFFSET); } void set_msaa_control(uint ctrl) { value |= (ctrl << ROP_control_bits::MSAA_SAMPLE_CTRL_OFFSET); } @@ -82,6 +84,7 @@ namespace glsl void insert_vertex_input_fetch(std::stringstream& OS, glsl_rules rules, bool glsl4_compliant=true); void insert_rop_init(std::ostream& OS, u32 mrt_buffers_count); void insert_rop(std::ostream& OS, const shader_properties& props); + void insert_fragment_epilogue(std::ostream& OS, const shader_properties& props); void insert_glsl_legacy_function(std::ostream& OS, const shader_properties& props); std::string getFunctionImpl(FUNCTION f); void insert_subheader_block(std::ostream& OS); diff --git a/rpcs3/Emu/RSX/Program/GLSLSnippets/CopyBufferToGenericImage.glsl b/rpcs3/Emu/RSX/Program/GLSLSnippets/CopyBufferToGenericImage.glsl index 021be6c6eb..9cc00a8b02 100644 --- a/rpcs3/Emu/RSX/Program/GLSLSnippets/CopyBufferToGenericImage.glsl +++ b/rpcs3/Emu/RSX/Program/GLSLSnippets/CopyBufferToGenericImage.glsl @@ -4,6 +4,7 @@ R"( #define ENABLE_DEPTH_STENCIL_LOAD %stencil_export_supported #define LEGACY_FORMAT_SUPPORT %legacy_format_support +#define EMULATED_DEPTH_STORAGE %emulated_depth_storage #define FMT_GL_DEPTH_COMPONENT16 0x81A5 #define FMT_GL_DEPTH_COMPONENT32F 0x8CAC @@ -45,11 +46,13 @@ layout(%push_block) uniform UnpackConfiguration uint swap_bytes; uint src_pitch; uint format; + uint depth_float; }; #else uniform uint swap_bytes; uniform uint src_pitch; uniform uint format; + uniform uint depth_float; #endif uint getTexelOffset() @@ -130,6 +133,11 @@ float readE4M12(const in uint address) } #define readFixed16(address) readUint16(uint(address)) / 65535.f + +#if EMULATED_DEPTH_STORAGE +// Float depth surfaces hold half the IEEE bit pattern, see encode_emulated_depth +#define encodeDepth(value) uintBitsToFloat((floatBitsToUint(max(value, 0.)) & 0x7fffffffu) >> 1) +#endif #define readFixed16x2(address) vec2(readFixed16(address * 2 + 0), readFixed16(address * 2 + 1)) #define readFixed16x4(address) vec4(readFixed16(address * 4 + 0), readFixed16(address * 4 + 1), readFixed16(address * 4 + 2), readFixed16(address * 4 + 3)) @@ -153,7 +161,11 @@ void main() gl_FragDepth = readFixed16(texel_address); break; case FMT_GL_DEPTH_COMPONENT32F: +#if EMULATED_DEPTH_STORAGE + gl_FragDepth = encodeDepth(readE4M12(texel_address)); +#else gl_FragDepth = readE4M12(texel_address); +#endif break; #if ENABLE_DEPTH_STENCIL_LOAD @@ -162,6 +174,13 @@ void main() case FMT_GL_DEPTH24_STENCIL8: case FMT_GL_DEPTH32F_STENCIL8: utmp2 = readUint24_8(texel_address); +#if EMULATED_DEPTH_STORAGE + if (depth_float != 0) + { + gl_FragDepth = encodeDepth(uintBitsToFloat(utmp2.x << 7)); + } + else +#endif gl_FragDepth = float(utmp2.x) / 0xffffff; gl_FragStencilRefARB = int(utmp2.y); break; diff --git a/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentEpilogue.glsl b/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentEpilogue.glsl new file mode 100644 index 0000000000..7dd9dec5ed --- /dev/null +++ b/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentEpilogue.glsl @@ -0,0 +1,27 @@ +R"( +#ifdef _EMULATE_DEPTH_RANGE + // The vertex stage mapped the clip range onto the [0, 1] viewport and hardware clipping is disabled. + // Clip or clamp against the real range here, then store the window Z encoded. + if (!_test_bit(rop_control, DEPTH_CLAMP_ENABLE_BIT) && (gl_FragCoord.z < 0. || gl_FragCoord.z > 1.)) + { + discard; + } + +#ifdef _ENABLE_DEPTH_EXPORT + // Hardware tests show exported depth is not clamped to 1 on float depth surfaces + float emulated_depth = encode_emulated_depth(r1.z); +#else + float emulated_depth = encode_emulated_depth(depth_range.x + _saturate(gl_FragCoord.z) * (depth_range.y - depth_range.x)); +#endif + +#ifdef _ENABLE_DEPTH_COMPARE + // dstDepth is the stored value, already encoded. One step of the 24-bit float format is 64 steps of the encoding. + if (abs(int(floatBitsToUint(emulated_depth)) - int(floatBitsToUint(dstDepth))) < 64) + { + emulated_depth = dstDepth; + } +#endif + + gl_FragDepth = emulated_depth; +#endif +)" diff --git a/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentPrologue.glsl b/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentPrologue.glsl index 500e77684a..8e1587e05d 100644 --- a/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentPrologue.glsl +++ b/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentPrologue.glsl @@ -1,5 +1,19 @@ R"( +#ifdef _EMULATED_DEPTH_STORAGE +// Halving the IEEE bit pattern of a non-negative float is monotonic and maps every finite value below 1. +// The host depth test keeps working on the encoded values. +float encode_emulated_depth(const in float depth) +{ + return uintBitsToFloat((floatBitsToUint(max(depth, 0.)) & 0x7fffffffu) >> 1); +} + +float decode_emulated_depth(const in float stored) +{ + return uintBitsToFloat(floatBitsToUint(stored) << 1); +} +#endif + #ifdef _32_BIT_OUTPUT // Everything is fp32 on ouput channels #define _mrt_color_t(expr) expr diff --git a/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentTextureDepthConversion.glsl b/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentTextureDepthConversion.glsl index 6333c1e6ac..2483cd63de 100644 --- a/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentTextureDepthConversion.glsl +++ b/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentTextureDepthConversion.glsl @@ -17,7 +17,11 @@ vec4 decode_depth24(const in float depth_value, const in bool depth_float) } else { +#ifdef _EMULATED_DEPTH_STORAGE + value = _get_bits(floatBitsToUint(decode_emulated_depth(depth_value)), 7, 24); +#else value = _get_bits(floatBitsToUint(depth_value), 7, 24); +#endif } uint b = _get_bits(value, 0, 8); diff --git a/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentTextureOps.glsl b/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentTextureOps.glsl index 19f3ff3f9c..0cabcf401c 100644 --- a/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentTextureOps.glsl +++ b/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentTextureOps.glsl @@ -55,6 +55,25 @@ R"( #define TEX_NAME(index) tex##index #define TEX_NAME_STENCIL(index) tex##index##_stencil +#ifdef _EMULATED_DEPTH_STORAGE +// Float depth surfaces hold the emulated encoding; recover the depth value when they are sampled directly +vec4 _decode_depth_texel(const in vec4 texel, const in uint flags, const in uint remap) +{ + if (!_test_bit(flags, DEPTH_FLOAT)) + { + return texel; + } + + // Only channels sourced from the texture (remap select == 2) hold encoded depth + const uvec4 remap_select = (uvec4(remap) >> uvec4(10, 12, 14, 8)) & 3u; + const vec4 decoded = uintBitsToFloat(floatBitsToUint(texel) << 1u); + return _select(texel, decoded, equal(remap_select, uvec4(2u))); +} +#define _DEPTH_DECODE(index, texel) _decode_depth_texel(texel, TEX_PARAM(index).flags, TEX_PARAM(index).remap) +#else +#define _DEPTH_DECODE(index, texel) texel +#endif + #define COORD_SCALE1(index, coord1) _texcoord_xform(coord1, TEX_PARAM(index)) #define COORD_SCALE2(index, coord2) _texcoord_xform(coord2, TEX_PARAM(index)) #define COORD_SCALE3(index, coord3) _texcoord_xform(coord3, TEX_PARAM(index)) @@ -63,19 +82,19 @@ R"( #define COORD_PROJ3(index, coord4) COORD_SCALE3(index, coord4.xyz / coord4.w) #ifdef _ENABLE_TEX1D -#define TEX1D(index, coord1) _process_texel(texture(TEX_NAME(index), COORD_SCALE1(index, coord1)), TEX_FLAGS(index)) -#define TEX1D_BIAS(index, coord1, bias) _process_texel(texture(TEX_NAME(index), COORD_SCALE1(index, coord1), bias), TEX_FLAGS(index)) -#define TEX1D_LOD(index, coord1, lod) _process_texel(textureLod(TEX_NAME(index), COORD_SCALE1(index, coord1), lod), TEX_FLAGS(index)) -#define TEX1D_GRAD(index, coord1, dpdx, dpdy) _process_texel(textureGrad(TEX_NAME(index), COORD_SCALE1(index, coord1), dpdx, dpdy), TEX_FLAGS(index)) -#define TEX1D_PROJ(index, coord4) _process_texel(texture(TEX_NAME(index), COORD_PROJ1(index, coord4.xw)), TEX_FLAGS(index)) +#define TEX1D(index, coord1) _process_texel(_DEPTH_DECODE(index, texture(TEX_NAME(index), COORD_SCALE1(index, coord1))), TEX_FLAGS(index)) +#define TEX1D_BIAS(index, coord1, bias) _process_texel(_DEPTH_DECODE(index, texture(TEX_NAME(index), COORD_SCALE1(index, coord1), bias)), TEX_FLAGS(index)) +#define TEX1D_LOD(index, coord1, lod) _process_texel(_DEPTH_DECODE(index, textureLod(TEX_NAME(index), COORD_SCALE1(index, coord1), lod)), TEX_FLAGS(index)) +#define TEX1D_GRAD(index, coord1, dpdx, dpdy) _process_texel(_DEPTH_DECODE(index, textureGrad(TEX_NAME(index), COORD_SCALE1(index, coord1), dpdx, dpdy)), TEX_FLAGS(index)) +#define TEX1D_PROJ(index, coord4) _process_texel(_DEPTH_DECODE(index, texture(TEX_NAME(index), COORD_PROJ1(index, coord4.xw))), TEX_FLAGS(index)) #endif #ifdef _ENABLE_TEX2D -#define TEX2D(index, coord2) _process_texel(texture(TEX_NAME(index), COORD_SCALE2(index, coord2)), TEX_FLAGS(index)) -#define TEX2D_BIAS(index, coord2, bias) _process_texel(texture(TEX_NAME(index), COORD_SCALE2(index, coord2), bias), TEX_FLAGS(index)) -#define TEX2D_LOD(index, coord2, lod) _process_texel(textureLod(TEX_NAME(index), COORD_SCALE2(index, coord2), lod), TEX_FLAGS(index)) -#define TEX2D_GRAD(index, coord2, dpdx, dpdy) _process_texel(textureGrad(TEX_NAME(index), COORD_SCALE2(index, coord2), dpdx, dpdy), TEX_FLAGS(index)) -#define TEX2D_PROJ(index, coord4) _process_texel(texture(TEX_NAME(index), COORD_PROJ2(index, coord4.xyw)), TEX_FLAGS(index)) +#define TEX2D(index, coord2) _process_texel(_DEPTH_DECODE(index, texture(TEX_NAME(index), COORD_SCALE2(index, coord2))), TEX_FLAGS(index)) +#define TEX2D_BIAS(index, coord2, bias) _process_texel(_DEPTH_DECODE(index, texture(TEX_NAME(index), COORD_SCALE2(index, coord2), bias)), TEX_FLAGS(index)) +#define TEX2D_LOD(index, coord2, lod) _process_texel(_DEPTH_DECODE(index, textureLod(TEX_NAME(index), COORD_SCALE2(index, coord2), lod)), TEX_FLAGS(index)) +#define TEX2D_GRAD(index, coord2, dpdx, dpdy) _process_texel(_DEPTH_DECODE(index, textureGrad(TEX_NAME(index), COORD_SCALE2(index, coord2), dpdx, dpdy)), TEX_FLAGS(index)) +#define TEX2D_PROJ(index, coord4) _process_texel(_DEPTH_DECODE(index, texture(TEX_NAME(index), COORD_PROJ2(index, coord4.xyw))), TEX_FLAGS(index)) #endif #ifdef _ENABLE_SHADOW @@ -96,11 +115,11 @@ R"( #endif #ifdef _ENABLE_TEX3D -#define TEX3D(index, coord3) _process_texel(texture(TEX_NAME(index), COORD_SCALE3(index, coord3)), TEX_FLAGS(index)) -#define TEX3D_BIAS(index, coord3, bias) _process_texel(texture(TEX_NAME(index), COORD_SCALE3(index, coord3), bias), TEX_FLAGS(index)) -#define TEX3D_LOD(index, coord3, lod) _process_texel(textureLod(TEX_NAME(index), COORD_SCALE3(index, coord3), lod), TEX_FLAGS(index)) -#define TEX3D_GRAD(index, coord3, dpdx, dpdy) _process_texel(textureGrad(TEX_NAME(index), COORD_SCALE3(index, coord3), dpdx, dpdy), TEX_FLAGS(index)) -#define TEX3D_PROJ(index, coord4) _process_texel(texture(TEX_NAME(index), COORD_PROJ3(index, coord4).xyz), TEX_FLAGS(index)) +#define TEX3D(index, coord3) _process_texel(_DEPTH_DECODE(index, texture(TEX_NAME(index), COORD_SCALE3(index, coord3))), TEX_FLAGS(index)) +#define TEX3D_BIAS(index, coord3, bias) _process_texel(_DEPTH_DECODE(index, texture(TEX_NAME(index), COORD_SCALE3(index, coord3), bias)), TEX_FLAGS(index)) +#define TEX3D_LOD(index, coord3, lod) _process_texel(_DEPTH_DECODE(index, textureLod(TEX_NAME(index), COORD_SCALE3(index, coord3), lod)), TEX_FLAGS(index)) +#define TEX3D_GRAD(index, coord3, dpdx, dpdy) _process_texel(_DEPTH_DECODE(index, textureGrad(TEX_NAME(index), COORD_SCALE3(index, coord3), dpdx, dpdy)), TEX_FLAGS(index)) +#define TEX3D_PROJ(index, coord4) _process_texel(_DEPTH_DECODE(index, texture(TEX_NAME(index), COORD_PROJ3(index, coord4).xyz)), TEX_FLAGS(index)) #endif #ifdef _ENABLE_TEX1D @@ -160,7 +179,12 @@ vec3 _texcoord_xform_shadow(const in vec3 coord3, const in sampler_info params) if (_test_bit(params.flags, DEPTH_FLOAT)) { // Depth-float buffer, extended range supported +#ifdef _EMULATED_DEPTH_STORAGE + // The stored values are encoded; the encoding is monotonic so the hardware compare still holds + result.z = encode_emulated_depth(coord3.z); +#else result.z = coord3.z; +#endif } else { @@ -180,7 +204,11 @@ vec4 _texcoord_xform_shadow(const in vec4 coord4, const in sampler_info params) if (_test_bit(params.flags, DEPTH_FLOAT)) { // Depth-float buffer, extended range supported +#ifdef _EMULATED_DEPTH_STORAGE + result.w = encode_emulated_depth(coord4.w); +#else result.w = coord4.w; +#endif } else { diff --git a/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXVertexPrologue.glsl b/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXVertexPrologue.glsl index 5971e4ab10..d5d9431359 100644 --- a/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXVertexPrologue.glsl +++ b/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXVertexPrologue.glsl @@ -55,6 +55,20 @@ vec4 apply_zclip_xform( } #endif +#ifdef _EMULATE_DEPTH_RANGE +// The host viewport only holds [0, 1] while float depth targets store window Z far above 1. +// Map the clip range linearly onto the viewport instead; the fragment stage recovers the window Z. +vec4 apply_depth_range_xform(const in vec4 pos, const in vec2 range) +{ + if (range.y <= range.x) + { + return pos; + } + + return vec4(pos.xy, (pos.z - range.x * pos.w) / (range.y - range.x), pos.w); +} +#endif + #if defined(_ENABLE_INSTANCED_CONSTANTS) // Workaround for GL vs VK builtin variable naming #ifdef VULKAN diff --git a/rpcs3/Emu/RSX/Program/GLSLTypes.h b/rpcs3/Emu/RSX/Program/GLSLTypes.h index cade46757d..d7adfc4a3b 100644 --- a/rpcs3/Emu/RSX/Program/GLSLTypes.h +++ b/rpcs3/Emu/RSX/Program/GLSLTypes.h @@ -29,6 +29,7 @@ namespace glsl bool require_clip_plane_functions : 1; bool emulate_zclip_transform : 1; bool emulate_depth_clip_only : 1; + bool emulate_depth_range : 1; // Only relevant for fragment programs bool fp32_outputs : 1; @@ -41,6 +42,7 @@ namespace glsl bool low_precision_tests : 1; bool disable_early_discard : 1; bool supports_native_fp16 : 1; + bool emulated_depth_storage : 1; // Float depth surfaces hold encode_emulated_depth(z) // ROP control flags bool ROP_output_multisampled : 1; @@ -52,6 +54,8 @@ namespace glsl bool ROP_discard : 1; bool ROP_channel_remap : 1; bool ROP_programmable_blend : 1; + bool ROP_emulate_depth_range : 1; + bool ROP_depth_export : 1; // Texturing spec bool require_texture_ops : 1; // Global switch to enable/disable all texture code diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index cae6685717..d1a45dfc40 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -127,7 +127,8 @@ namespace rsx constexpr u32 fs_export_config_mask = RSX_SHADER_CONTROL_EMULATE_DEPTH_COMPARE | RSX_SHADER_CONTROL_ROP_MULTISAMPLED | - RSX_SHADER_CONTROL_PROGRAMMABLE_BLENDING; + RSX_SHADER_CONTROL_PROGRAMMABLE_BLENDING | + RSX_SHADER_CONTROL_EMULATE_DEPTH_RANGE; rsx_iomap_table::rsx_iomap_table() noexcept : ea(fill_array(-1)) @@ -2137,6 +2138,11 @@ namespace rsx expected_ctrl |= RSX_SHADER_CONTROL_ROP_MULTISAMPLED; } + if (requires_depth_range_emulation()) [[ unlikely ]] + { + expected_ctrl |= RSX_SHADER_CONTROL_EMULATE_DEPTH_RANGE; + } + // Depth compare if (!g_cfg.video.emulate_depth_compare) [[ likely ]] { @@ -2166,6 +2172,16 @@ namespace rsx return expected_ctrl; } + bool thread::requires_depth_range_emulation() const + { + if (backend_config.supports_extended_depth_range || !g_cfg.video.emulate_extended_depth_range) [[ likely ]] + { + return false; + } + + return m_framebuffer_layout.zeta_address && rsx::is_float_depth_format(REGS(m_ctx)->surface_depth_fmt()); + } + void thread::prefetch_fragment_program() { if (!m_graphics_state.test(rsx::pipeline_state::fragment_program_ucode_dirty)) @@ -2291,6 +2307,13 @@ namespace rsx m_program_cache_hint.invalidate_vertex_program(current_vertex_program); } + if (const bool emulate_depth_range = requires_depth_range_emulation(); + emulate_depth_range != !!(current_vertex_program.ctrl & RSX_SHADER_CONTROL_EMULATE_DEPTH_RANGE)) + { + current_vertex_program.ctrl ^= RSX_SHADER_CONTROL_EMULATE_DEPTH_RANGE; + m_program_cache_hint.invalidate_vertex_program(current_vertex_program); + } + if (!m_graphics_state.test(rsx::pipeline_state::vertex_program_dirty)) { return; diff --git a/rpcs3/Emu/RSX/RSXThread.h b/rpcs3/Emu/RSX/RSXThread.h index ea0742bbdf..d09a1bc802 100644 --- a/rpcs3/Emu/RSX/RSXThread.h +++ b/rpcs3/Emu/RSX/RSXThread.h @@ -92,6 +92,7 @@ namespace rsx bool supports_host_gpu_labels; // Advanced host synchronization bool supports_normalized_barycentrics; // Basically all GPUs except NVIDIA have properly normalized barycentrics bool supports_last_provoking_vertex; // Flat shading using RSX's last-vertex convention + bool supports_extended_depth_range; // Depth values above 1 can be stored and tested. Float depth targets are emulated otherwise. bool supports_programmable_blending; // Can handle programmable blending requests }; @@ -265,6 +266,7 @@ namespace rsx protected: void get_framebuffer_layout(rsx::framebuffer_creation_context context, framebuffer_layout &layout); bool get_scissor(areau& region, bool clip_viewport); + bool requires_depth_range_emulation() const; // Notify framebuffer layout has been committed. // FIXME: This should not be here diff --git a/rpcs3/Emu/RSX/VK/VKCommonDecompiler.cpp b/rpcs3/Emu/RSX/VK/VKCommonDecompiler.cpp index 8424036739..dd6d597957 100644 --- a/rpcs3/Emu/RSX/VK/VKCommonDecompiler.cpp +++ b/rpcs3/Emu/RSX/VK/VKCommonDecompiler.cpp @@ -23,6 +23,7 @@ namespace vk { "fog_c", 14 }, { "fogc", 14 }, { "usr", 15 }, // custom injected stuff + { "depth_range", 17 }, // clip range for depth range emulation; 16 is used by the shader interpreter } }; int get_varying_register_location(std::string_view varying_register_name) diff --git a/rpcs3/Emu/RSX/VK/VKCompute.h b/rpcs3/Emu/RSX/VK/VKCompute.h index a45716d520..44cc1c6ab4 100644 --- a/rpcs3/Emu/RSX/VK/VKCompute.h +++ b/rpcs3/Emu/RSX/VK/VKCompute.h @@ -2,6 +2,7 @@ #include "VKProgramPipeline.h" #include "vkutils/descriptors.h" #include "vkutils/buffer_object.h" +#include "VKHelpers.h" #include "Emu/IdManager.h" #include "Emu/RSX/Utils/algorithm.hpp" @@ -182,8 +183,10 @@ namespace vk } else { - work_kernel += - " depth = f32_to_d24f(data[index + z_offset]);\n"; + // With depth range emulation, float depth surfaces hold the emulated encoding (half the bit pattern) + work_kernel += !vk::emulate_extended_depth_range() + ? " depth = f32_to_d24f(data[index + z_offset]);\n" + : " depth = f32_to_d24f(data[index + z_offset] << 1);\n"; } work_kernel += @@ -231,8 +234,9 @@ namespace vk } else { - work_kernel += - " data[index + z_offset] = d24f_to_f32(value >> 8);\n"; + work_kernel += !vk::emulate_extended_depth_range() + ? " data[index + z_offset] = d24f_to_f32(value >> 8);\n" + : " data[index + z_offset] = d24f_to_f32(value >> 8) >> 1;\n"; } work_kernel += @@ -283,6 +287,12 @@ namespace vk " tmp = bswap_u32(tmp);\n"; } + if (vk::emulate_extended_depth_range()) + { + // Float depth surfaces hold the emulated encoding (half the bit pattern) + work_kernel += " tmp.xy <<= 1;\n"; + } + // Convert work_kernel += " tmp.z = pack_e4m12_pack16(tmp.xy);\n"; @@ -314,6 +324,11 @@ namespace vk // Convert work_kernel += " tmp.yz = unpack_e4m12_pack16(tmp.x);\n"; + if (vk::emulate_extended_depth_range()) + { + work_kernel += " tmp.yz >>= 1;\n"; + } + if constexpr (_SwapDst) { work_kernel += " tmp.yz = bswap_u32(tmp.yz);\n"; diff --git a/rpcs3/Emu/RSX/VK/VKDraw.cpp b/rpcs3/Emu/RSX/VK/VKDraw.cpp index 957948b930..5bc90963f1 100644 --- a/rpcs3/Emu/RSX/VK/VKDraw.cpp +++ b/rpcs3/Emu/RSX/VK/VKDraw.cpp @@ -253,8 +253,14 @@ void VKGSRender::update_draw_state() bounds_max = std::max(1.f, rsx::method_registers.clip_max()); } - if (!m_device->get_unrestricted_depth_range_support()) + if (!backend_config.supports_extended_depth_range) { + if (rsx::method_registers.depth_bounds_test_enabled() && requires_depth_range_emulation()) [[ unlikely ]] + { + bounds_min = rsx::encode_emulated_depth(bounds_min); + bounds_max = rsx::encode_emulated_depth(bounds_max); + } + bounds_min = std::clamp(bounds_min, 0.f, 1.f); bounds_max = std::clamp(bounds_max, 0.f, 1.f); } diff --git a/rpcs3/Emu/RSX/VK/VKFragmentProgram.cpp b/rpcs3/Emu/RSX/VK/VKFragmentProgram.cpp index 790f062b4e..439cd3774b 100644 --- a/rpcs3/Emu/RSX/VK/VKFragmentProgram.cpp +++ b/rpcs3/Emu/RSX/VK/VKFragmentProgram.cpp @@ -298,6 +298,12 @@ void VKFragmentDecompilerThread::insertConstants(std::stringstream & OS) "layout(location=" << vk::get_varying_register_location("usr") << ") in flat uvec4 draw_params_payload;\n\n"; } + if (m_prog.ctrl & RSX_SHADER_CONTROL_EMULATE_DEPTH_RANGE) + { + OS << + "layout(location=" << vk::get_varying_register_location("depth_range") << ") in flat vec2 depth_range;\n\n"; + } + OS << "#define _fs_constants_offset draw_params_payload.x\n" "#define _fs_context_offset draw_params_payload.y\n" @@ -396,7 +402,7 @@ void VKFragmentDecompilerThread::insertGlobalFunctions(std::stringstream &OS) m_shader_props.require_srgb_to_linear = properties.has_upg; m_shader_props.require_linear_to_srgb = properties.has_pkg; m_shader_props.require_fog_read = properties.in_register_mask & in_fogc; - m_shader_props.emulate_shadow_compare = device_props.emulate_depth_compare; + m_shader_props.emulate_shadow_compare = device_props.emulate_depth_compare || device_props.emulated_depth_storage; m_shader_props.low_precision_tests = device_props.has_low_precision_rounding && !(m_prog.ctrl & RSX_SHADER_CONTROL_ATTRIBUTE_INTERPOLATION); m_shader_props.disable_early_discard = !vk::is_NVIDIA(vk::get_driver_vendor()); @@ -419,6 +425,9 @@ void VKFragmentDecompilerThread::insertGlobalFunctions(std::stringstream &OS) m_shader_props.require_color_format_convert = !!(m_prog.ctrl & RSX_SHADER_CONTROL_TEXTURE_FORMAT_CONVERT); m_shader_props.emulate_depth_compare = !!(m_prog.ctrl & RSX_SHADER_CONTROL_EMULATE_DEPTH_COMPARE); m_shader_props.ROP_output_multisampled = !!(m_prog.ctrl & RSX_SHADER_CONTROL_ROP_MULTISAMPLED); + m_shader_props.ROP_emulate_depth_range = !!(m_prog.ctrl & RSX_SHADER_CONTROL_EMULATE_DEPTH_RANGE); + m_shader_props.ROP_depth_export = !!(m_prog.ctrl & CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT) && m_shader_props.ROP_emulate_depth_range; + m_shader_props.emulated_depth_storage = device_props.emulated_depth_storage; // Declare global constants if (m_shader_props.require_fog_read) @@ -547,7 +556,8 @@ void VKFragmentDecompilerThread::insertMainEnd(std::stringstream & OS) RSX_SHADER_CONTROL_ALPHA_TEST | RSX_SHADER_CONTROL_EMULATE_DEPTH_COMPARE | RSX_SHADER_CONTROL_ROP_OUTPUT_REMAP | - RSX_SHADER_CONTROL_PROGRAMMABLE_BLENDING; + RSX_SHADER_CONTROL_PROGRAMMABLE_BLENDING | + RSX_SHADER_CONTROL_EMULATE_DEPTH_RANGE; if (m_prog.ctrl & ROP_control_access_options) { @@ -593,6 +603,8 @@ void VKFragmentDecompilerThread::insertMainEnd(std::stringstream & OS) } } + glsl::insert_fragment_epilogue(OS, m_shader_props); + OS << "}\n"; } @@ -622,6 +634,7 @@ void VKFragmentProgram::Decompile(const RSXFragmentProgram& prog) } decompiler.device_props.emulate_depth_compare = !pdev->get_formats_support().d24_unorm_s8; + decompiler.device_props.emulated_depth_storage = vk::emulate_extended_depth_range(); decompiler.device_props.has_low_precision_rounding = vk::is_NVIDIA(vk::get_driver_vendor()); decompiler.Task(); diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.cpp b/rpcs3/Emu/RSX/VK/VKGSRender.cpp index c959122cec..8c046ecaf7 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.cpp +++ b/rpcs3/Emu/RSX/VK/VKGSRender.cpp @@ -251,7 +251,8 @@ namespace vk u8 num_draw_buffers, u8 num_rasterization_samples, bool depth_bounds_support, - bool force_disable_blending) + bool force_disable_blending, + bool force_depth_clamp) { vk::pipeline_props properties{}; @@ -262,7 +263,15 @@ namespace vk // Rasterizer state properties.state.set_attachment_count(num_draw_buffers); properties.state.set_front_face(vk::get_front_face(REGS(ctx)->front_face_mode())); - properties.state.enable_depth_clamp(REGS(ctx)->depth_clamp_enabled() || !REGS(ctx)->depth_clip_enabled()); + if (!force_depth_clamp) [[ likely ]] + { + properties.state.enable_depth_clamp(REGS(ctx)->depth_clamp_enabled() || !REGS(ctx)->depth_clip_enabled()); + } + else + { + // Depth clip and clamp are emulated in the fragment shader + properties.state.enable_depth_clamp(true); + } properties.state.enable_depth_bias(true); properties.state.enable_depth_bounds_test(depth_bounds_support); @@ -645,6 +654,7 @@ VKGSRender::VKGSRender(utils::serial* ar) noexcept : GSRender(ar) backend_config.supports_hw_instanced_rendering = true; backend_config.supports_programmable_blending = true; + backend_config.supports_extended_depth_range = m_device->get_unrestricted_depth_range_support(); backend_config.supports_last_provoking_vertex = m_device->get_provoking_vertex_last_support(); if (!backend_config.supports_last_provoking_vertex) { @@ -1335,7 +1345,13 @@ void VKGSRender::clear_surface(u32 mask) if (mask & RSX_GCM_CLEAR_DEPTH_BIT) { const u32 clear_depth_bits = REGS(m_ctx)->z_clear_value(is_depth_stencil_format(surface_depth_format)); - depth_stencil_clear_values.depthStencil.depth = rsx::get_depth_clear_value(surface_depth_format, clear_depth_bits); + f32 depth_clear = rsx::get_depth_clear_value(surface_depth_format, clear_depth_bits); + if (vk::emulate_extended_depth_range() && rsx::is_float_depth_format(surface_depth_format)) [[ unlikely ]] + { + depth_clear = rsx::encode_emulated_depth(depth_clear); + } + + depth_stencil_clear_values.depthStencil.depth = depth_clear; depth_stencil_clear_values.depthStencil.stencil = stencil_clear; depth_stencil_mask |= VK_IMAGE_ASPECT_DEPTH_BIT; @@ -1856,7 +1872,8 @@ bool VKGSRender::load_program() static_cast(m_draw_buffers.size()), u8((m_current_renderpass_key >> 16) & 0xF), m_device->get_depth_bounds_support(), - !!(current_fragment_program.ctrl & RSX_SHADER_CONTROL_PROGRAMMABLE_BLENDING) + !!(current_fragment_program.ctrl & RSX_SHADER_CONTROL_PROGRAMMABLE_BLENDING), + !!(current_fragment_program.ctrl & RSX_SHADER_CONTROL_EMULATE_DEPTH_RANGE) ); properties.renderpass_key = m_current_renderpass_key; @@ -1901,12 +1918,16 @@ bool VKGSRender::load_program() } // Load current program from cache + // The shader interpreter does not emulate the depth range; compile those programs synchronously instead + const bool allow_async = shadermode != shader_mode::recompiler && + !(fragment_program.ctrl & RSX_SHADER_CONTROL_EMULATE_DEPTH_RANGE); + std::tie(m_program, m_vertex_prog, m_fragment_prog) = m_prog_buffer->get_graphics_pipeline( &m_program_cache_hint, vertex_program, fragment_program, m_pipeline_properties, - shadermode != shader_mode::recompiler, true); + allow_async, true); vk::leave_uninterruptible(); diff --git a/rpcs3/Emu/RSX/VK/VKHelpers.cpp b/rpcs3/Emu/RSX/VK/VKHelpers.cpp index 4a3bfc1c99..cfa8391c68 100644 --- a/rpcs3/Emu/RSX/VK/VKHelpers.cpp +++ b/rpcs3/Emu/RSX/VK/VKHelpers.cpp @@ -1,4 +1,5 @@ #include "stdafx.h" +#include "Emu/system_config.h" #include "VKHelpers.h" #include "VKGSRender.h" #include "VKCompute.h" @@ -245,6 +246,11 @@ namespace vk return g_drv_emulate_cond_render; } + bool emulate_extended_depth_range() + { + return !g_render_device->get_unrestricted_depth_range_support() && g_cfg.video.emulate_extended_depth_range; + } + bool use_strict_query_scopes() { return g_drv_strict_query_scopes; diff --git a/rpcs3/Emu/RSX/VK/VKHelpers.h b/rpcs3/Emu/RSX/VK/VKHelpers.h index 6cbb33c39e..7d5058b085 100644 --- a/rpcs3/Emu/RSX/VK/VKHelpers.h +++ b/rpcs3/Emu/RSX/VK/VKHelpers.h @@ -54,6 +54,7 @@ namespace vk bool sanitize_fp_values(); bool fence_reset_disabled(); bool emulate_conditional_rendering(); + bool emulate_extended_depth_range(); bool use_strict_query_scopes(); bool force_reuse_query_pools(); VkFlags get_heap_compatible_buffer_types(); diff --git a/rpcs3/Emu/RSX/VK/VKVertexProgram.cpp b/rpcs3/Emu/RSX/VK/VKVertexProgram.cpp index fef536e61d..a9893cddd8 100644 --- a/rpcs3/Emu/RSX/VK/VKVertexProgram.cpp +++ b/rpcs3/Emu/RSX/VK/VKVertexProgram.cpp @@ -321,6 +321,11 @@ void VKVertexDecompilerThread::insertOutputs(std::stringstream& OS, const std::v { OS << "layout(location=" << vk::get_varying_register_location("usr") << ") out flat uvec4 draw_params_payload;\n"; } + + if (m_prog.ctrl & RSX_SHADER_CONTROL_EMULATE_DEPTH_RANGE) + { + OS << "layout(location=" << vk::get_varying_register_location("depth_range") << ") out flat vec2 depth_range;\n"; + } } void VKVertexDecompilerThread::insertFSExport(std::stringstream& OS) @@ -346,6 +351,7 @@ void VKVertexDecompilerThread::insertMainStart(std::stringstream& OS) properties2.low_precision_tests = vk::is_NVIDIA(vk::get_driver_vendor()); properties2.require_explicit_invariance = (vk::is_NVIDIA(vk::get_driver_vendor()) && g_cfg.video.shader_precision != gpu_preset_level::low); properties2.require_instanced_render = !!(m_prog.ctrl & RSX_SHADER_CONTROL_INSTANCED_CONSTANTS); + properties2.emulate_depth_range = !!(m_prog.ctrl & RSX_SHADER_CONTROL_EMULATE_DEPTH_RANGE); glsl::insert_glsl_legacy_function(OS, properties2); glsl::insert_vertex_input_fetch(OS, glsl::glsl_rules_vulkan); @@ -477,7 +483,18 @@ void VKVertexDecompilerThread::insertMainEnd(std::stringstream& OS) } OS << " gl_Position = gl_Position * scale_offset_mat;\n"; - OS << " gl_Position = apply_zclip_xform(gl_Position, z_near, z_far);\n"; + + if (!(m_prog.ctrl & RSX_SHADER_CONTROL_EMULATE_DEPTH_RANGE)) [[ likely ]] + { + OS << " gl_Position = apply_zclip_xform(gl_Position, z_near, z_far);\n"; + } + else + { + OS << + " depth_range = vec2(min(z_near, z_far), max(z_near, z_far));\n" + " gl_Position = apply_depth_range_xform(gl_Position, depth_range);\n"; + } + OS << "}\n"; } diff --git a/rpcs3/Emu/RSX/gcm_enums.h b/rpcs3/Emu/RSX/gcm_enums.h index fe5cf89eb3..e5c6947e81 100644 --- a/rpcs3/Emu/RSX/gcm_enums.h +++ b/rpcs3/Emu/RSX/gcm_enums.h @@ -476,6 +476,7 @@ namespace gcm RSX_SHADER_CONTROL_ROP_MULTISAMPLED = 0x10000000, // ROP outputs are multisampled RSX_SHADER_CONTROL_ROP_OUTPUT_REMAP = 0x20000000, // ROP outputs need channel swizzles. RSX_SHADER_CONTROL_PROGRAMMABLE_BLENDING = 0x40000000, // Enable programmable blending. + RSX_SHADER_CONTROL_EMULATE_DEPTH_RANGE = 0x80000000, // Float depth target without host support for depth values above 1 // Meta RSX_SHADER_CONTROL_META_USES_DISCARD = (RSX_SHADER_CONTROL_USES_KIL | RSX_SHADER_CONTROL_TEXTURE_ALPHA_KILL | RSX_SHADER_CONTROL_ALPHA_TEST | RSX_SHADER_CONTROL_POLYGON_STIPPLE | RSX_SHADER_CONTROL_ALPHA_TO_COVERAGE), diff --git a/rpcs3/Emu/RSX/rsx_methods.cpp b/rpcs3/Emu/RSX/rsx_methods.cpp index 0a5068b39c..b93769bdec 100644 --- a/rpcs3/Emu/RSX/rsx_methods.cpp +++ b/rpcs3/Emu/RSX/rsx_methods.cpp @@ -775,7 +775,6 @@ namespace rsx state_signals[NV4097_SET_DEPTH_BOUNDS_MIN] = rsx::depth_bounds_state_dirty; state_signals[NV4097_SET_DEPTH_BOUNDS_MAX] = rsx::depth_bounds_state_dirty; state_signals[NV4097_SET_CULL_FACE_ENABLE] = rsx::pipeline_config_dirty; - state_signals[NV4097_SET_ZMIN_MAX_CONTROL] = rsx::pipeline_config_dirty; state_signals[NV4097_SET_LOGIC_OP_ENABLE] = rsx::pipeline_config_dirty; state_signals[NV4097_SET_LOGIC_OP] = rsx::pipeline_config_dirty; state_signals[NV4097_SET_BLEND_ENABLE] = rsx::pipeline_config_dirty | rsx::blend_config_dirty; @@ -1621,6 +1620,7 @@ namespace rsx // NV4097 bind(NV4097_SET_CULL_FACE, nv4097::set_face_property); + bind(NV4097_SET_ZMIN_MAX_CONTROL, nv4097::set_zmin_max_control); bind(NV4097_SET_FRONT_FACE, nv4097::set_face_property); bind(NV4097_TEXTURE_READ_SEMAPHORE_RELEASE, nv4097::texture_read_semaphore_release); bind(NV4097_BACK_END_WRITE_SEMAPHORE_RELEASE, nv4097::back_end_write_semaphore_release); diff --git a/rpcs3/Emu/system_config.h b/rpcs3/Emu/system_config.h index 2d53c68ed5..df4bdc6343 100644 --- a/rpcs3/Emu/system_config.h +++ b/rpcs3/Emu/system_config.h @@ -149,6 +149,7 @@ struct cfg_root : cfg::node cfg::_bool disable_vertex_cache{ this, "Disable Vertex Cache", false }; cfg::_bool disable_FIFO_reordering{ this, "Disable FIFO Reordering", false }; cfg::_bool emulate_depth_compare{ this, "Emulate Special Depth Comparison", false }; + cfg::_bool emulate_extended_depth_range{ this, "Emulate Extended Depth Range", false }; cfg::_bool frame_skip_enabled{ this, "Enable Frame Skip", false, true }; cfg::_bool force_cpu_blit_processing{ this, "Force CPU Blit", false, true }; // Debugging option cfg::_bool disable_on_disk_shader_cache{ this, "Disable On-Disk Shader Cache", false }; diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index 0ad8b3edd3..682662d8bb 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -1131,6 +1131,7 @@ + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index b8d3022f78..328b77b247 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -3063,6 +3063,9 @@ Emu\GPU\RSX\Program\Snippets + + Emu\GPU\RSX\Program\Snippets\RSXProg + Emu\GPU\RSX\Program\Snippets\RSXProg