diff --git a/3rdparty/libpng/libpng b/3rdparty/libpng/libpng index f5e92d7697..51f5bd68b9 160000 --- a/3rdparty/libpng/libpng +++ b/3rdparty/libpng/libpng @@ -1 +1 @@ -Subproject commit f5e92d76973a7a53f517579bc95d61483bf108c0 +Subproject commit 51f5bd68b9b806d2c92b4318164d28b49357da31 diff --git a/3rdparty/pugixml b/3rdparty/pugixml index db78afc2b7..ee86beb30e 160000 --- a/3rdparty/pugixml +++ b/3rdparty/pugixml @@ -1 +1 @@ -Subproject commit db78afc2b7d8f043b4bc6b185635d949ea2ed2a8 +Subproject commit ee86beb30e4973f5feffe3ce63bfa4fbadf72f38 diff --git a/Utilities/File.cpp b/Utilities/File.cpp index 3c4a8c769d..e2dd842ae6 100644 --- a/Utilities/File.cpp +++ b/Utilities/File.cpp @@ -21,7 +21,7 @@ using namespace std::literals::string_literals; #include #include -static std::unique_ptr to_wchar(const std::string& source) +static std::unique_ptr to_wchar(std::string_view source) { // String size + null terminator const usz buf_size = source.size() + 1; @@ -44,7 +44,7 @@ static std::unique_ptr to_wchar(const std::string& source) std::memcpy(buffer.get() + 32768 + 4, L"UNC\\", 4 * sizeof(wchar_t)); } - ensure(MultiByteToWideChar(CP_UTF8, 0, source.c_str(), size, buffer.get() + 32768 + (unc ? 8 : 4), size)); // "to_wchar" + ensure(MultiByteToWideChar(CP_UTF8, 0, source.data(), size, buffer.get() + 32768 + (unc ? 8 : 4), size)); // "to_wchar" // Canonicalize wide path (replace '/', ".", "..", \\ repetitions, etc) ensure(GetFullPathNameW(buffer.get() + 32768, 32768, buffer.get(), nullptr) - 1 < 32768 - 1); // "to_wchar" diff --git a/Utilities/rXml.cpp b/Utilities/rXml.cpp index db52fb38e3..14ac0659f6 100644 --- a/Utilities/rXml.cpp +++ b/Utilities/rXml.cpp @@ -49,12 +49,11 @@ std::string rXmlNode::GetName() return {}; } -std::string rXmlNode::GetAttribute(const std::string& name) +std::string rXmlNode::GetAttribute(std::string_view name) { if (handle) { - const auto pred = [&name](const pugi::xml_attribute& attr) { return (name == attr.name()); }; - if (const pugi::xml_attribute attr = handle.find_attribute(pred)) + if (const pugi::xml_attribute attr = handle.attribute(name)) { if (const pugi::char_t* value = attr.value()) { @@ -86,7 +85,7 @@ rXmlDocument::rXmlDocument() { } -pugi::xml_parse_result rXmlDocument::Read(const std::string& data) +pugi::xml_parse_result rXmlDocument::Read(std::string_view data) { if (handle) { diff --git a/Utilities/rXml.h b/Utilities/rXml.h index 71ca257902..8b70d06ee4 100644 --- a/Utilities/rXml.h +++ b/Utilities/rXml.h @@ -23,7 +23,7 @@ struct rXmlNode std::shared_ptr GetChildren(); std::shared_ptr GetNext(); std::string GetName(); - std::string GetAttribute(const std::string& name); + std::string GetAttribute(std::string_view name); std::string GetNodeContent(); pugi::xml_node handle{}; @@ -34,7 +34,7 @@ struct rXmlDocument rXmlDocument(); rXmlDocument(const rXmlDocument& other) = delete; rXmlDocument &operator=(const rXmlDocument& other) = delete; - pugi::xml_parse_result Read(const std::string& data); + pugi::xml_parse_result Read(std::string_view data); virtual std::shared_ptr GetRoot(); pugi::xml_document handle{}; diff --git a/rpcs3/Emu/RSX/Core/RSXDrawCommands.cpp b/rpcs3/Emu/RSX/Core/RSXDrawCommands.cpp index 095903832d..389efe0a2e 100644 --- a/rpcs3/Emu/RSX/Core/RSXDrawCommands.cpp +++ b/rpcs3/Emu/RSX/Core/RSXDrawCommands.cpp @@ -735,7 +735,7 @@ namespace rsx utils::stream_vector(dst + 4, 0u, fog_mode, std::bit_cast(wpos_scale), std::bit_cast(wpos_bias)); } - void draw_command_processor::fill_constants_instancing_buffer(rsx::io_buffer& indirection_table_buf, rsx::io_buffer& constants_data_array_buffer, const VertexProgramBase& prog) const + void draw_command_processor::fill_constants_instancing_buffer(rsx::io_buffer& indirection_table_buf, rsx::io_buffer& constants_data_array_buffer, const VertexProgramBase* prog) const { auto& draw_call = REGS(m_ctx)->current_draw_clause; @@ -745,8 +745,9 @@ namespace rsx // Temp indirection table. Used to track "running" updates. rsx::simple_array instancing_indirection_table; // indirection table size - const auto reloc_table = prog.has_indexed_constants ? decltype(prog.constant_ids){} : prog.constant_ids; - const auto redirection_table_size = prog.has_indexed_constants ? 468u : ::size32(prog.constant_ids); + const auto full_reupload = !prog || prog->has_indexed_constants; + const auto reloc_table = full_reupload ? decltype(prog->constant_ids){} : prog->constant_ids; + const auto redirection_table_size = full_reupload ? 468u : ::size32(prog->constant_ids); instancing_indirection_table.resize(redirection_table_size); // Temp constants data @@ -787,9 +788,9 @@ namespace rsx continue; } - const int translated_offset = prog.has_indexed_constants + const int translated_offset = full_reupload ? instance_config.patch_load_offset - : prog.TranslateConstantsRange(instance_config.patch_load_offset, instance_config.patch_load_count); + : prog->translate_constants_range(instance_config.patch_load_offset, instance_config.patch_load_count); if (translated_offset >= 0) { @@ -809,14 +810,14 @@ namespace rsx continue; } - ensure(!prog.has_indexed_constants); + ensure(!full_reupload); // Sparse update. Update records individually instead of bulk // FIXME: Range batching optimization const auto load_end = instance_config.patch_load_offset + instance_config.patch_load_count; for (u32 i = 0; i < redirection_table_size; ++i) { - const auto read_index = prog.constant_ids[i]; + const auto read_index = prog->constant_ids[i]; if (read_index < instance_config.patch_load_offset || read_index >= load_end) { // Reading outside "hot" range. diff --git a/rpcs3/Emu/RSX/Core/RSXDrawCommands.h b/rpcs3/Emu/RSX/Core/RSXDrawCommands.h index b69a918401..5bc5991a18 100644 --- a/rpcs3/Emu/RSX/Core/RSXDrawCommands.h +++ b/rpcs3/Emu/RSX/Core/RSXDrawCommands.h @@ -105,6 +105,6 @@ namespace rsx // Fill instancing buffers. A single iobuf is used for both. 256byte alignment enforced to allow global bind // Returns offsets to the index redirection lookup table and constants field array - void fill_constants_instancing_buffer(rsx::io_buffer& indirection_table_buf, rsx::io_buffer& constants_data_array_buffer, const VertexProgramBase& prog) const; + void fill_constants_instancing_buffer(rsx::io_buffer& indirection_table_buf, rsx::io_buffer& constants_data_array_buffer, const VertexProgramBase* prog) const; }; } diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.cpp b/rpcs3/Emu/RSX/GL/GLGSRender.cpp index 90bc7cacbd..c37228afe9 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.cpp +++ b/rpcs3/Emu/RSX/GL/GLGSRender.cpp @@ -878,7 +878,7 @@ void GLGSRender::load_program_env() } } - if (update_fragment_constants && !update_instruction_buffers) + if (update_fragment_constants && !m_shader_interpreter.is_interpreter(m_program)) { // Fragment constants auto mapping = m_fragment_constants_buffer->alloc_from_heap(fragment_constants_size, m_uniform_buffer_offset_align); @@ -978,12 +978,18 @@ void GLGSRender::load_program_env() } } - m_graphics_state.clear( + rsx::flags32_t handled_flags = rsx::pipeline_state::fragment_state_dirty | rsx::pipeline_state::vertex_state_dirty | rsx::pipeline_state::transform_constants_dirty | - rsx::pipeline_state::fragment_constants_dirty | - rsx::pipeline_state::fragment_texture_state_dirty); + rsx::pipeline_state::fragment_texture_state_dirty; + + if (update_fragment_constants && !m_shader_interpreter.is_interpreter(m_program)) + { + handled_flags |= rsx::pipeline_state::fragment_constants_dirty; + } + + m_graphics_state.clear(handled_flags); } bool GLGSRender::is_current_program_interpreted() const @@ -1039,13 +1045,19 @@ void GLGSRender::update_vertex_env(const gl::vertex_upload_info& upload_info) void GLGSRender::patch_transform_constants(rsx::context* ctx, u32 index, u32 count) { - if (!m_vertex_prog) + if (!m_program || !m_vertex_prog) { // Shouldn't be reachable, but handle it correctly anyway m_graphics_state |= rsx::pipeline_state::transform_constants_dirty; return; } + if (!m_vertex_prog->overlaps_constants_range(index, count)) + { + // Nothing meaningful to us + return; + } + std::pair data_range {}; void* data_source = nullptr; const auto bound_range = m_transform_constants_buffer->bound_range(); @@ -1059,7 +1071,7 @@ void GLGSRender::patch_transform_constants(rsx::context* ctx, u32 index, u32 cou data_range = { bound_range.first + byte_offset, byte_count}; data_source = ®S(ctx)->transform_constants[index]; } - else if (auto xform_id = m_vertex_prog->TranslateConstantsRange(index, count); xform_id >= 0) + else if (auto xform_id = m_vertex_prog->translate_constants_range(index, count); xform_id >= 0) { const auto write_offset = xform_id * 16; const auto byte_count = count * 16; diff --git a/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentTextureMSAAOps.glsl b/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentTextureMSAAOps.glsl index d250e2efb4..8575508f54 100644 --- a/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentTextureMSAAOps.glsl +++ b/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentTextureMSAAOps.glsl @@ -34,7 +34,8 @@ vec2 texture2DMSCoord(const in vec2 coords, const in uint flags) return coords; } - const vec2 wrapped_coords = mod(coords, vec2(1.0)); + const vec2 wrapped_coords_raw = mod(coords, vec2(1.0)); + const vec2 wrapped_coords = mod(wrapped_coords_raw + vec2(1.0), vec2(1.0)); const bvec2 wrap_control_mask = bvec2(uvec2(flags) & uvec2(WRAP_S_MASK, WRAP_T_MASK)); return _select(coords, wrapped_coords, wrap_control_mask); } diff --git a/rpcs3/Emu/RSX/Program/program_util.cpp b/rpcs3/Emu/RSX/Program/program_util.cpp index 9f44f05505..e5daf319fc 100644 --- a/rpcs3/Emu/RSX/Program/program_util.cpp +++ b/rpcs3/Emu/RSX/Program/program_util.cpp @@ -110,7 +110,7 @@ namespace rsx multisampled_textures == other.multisampled_textures; } - int VertexProgramBase::TranslateConstantsRange(int first_index, int count) const + int VertexProgramBase::translate_constants_range(int first_index, int count) const { // The constant ids should be sorted, so just find the first one and check for continuity int index = -1; @@ -157,4 +157,31 @@ namespace rsx // OOB or partial match return -1; } + + bool VertexProgramBase::overlaps_constants_range(int first_index, int count) const + { + if (has_indexed_constants) + { + return true; + } + + const int last_index = first_index + count - 1; + + // Early rejection test + if (constant_ids.empty() || first_index > constant_ids.back() || last_index < first_index) + { + return false; + } + + // Check for any hits + for (auto& idx : constant_ids) + { + if (idx >= first_index && idx <= last_index) + { + return true; + } + } + + return false; + } } diff --git a/rpcs3/Emu/RSX/Program/program_util.h b/rpcs3/Emu/RSX/Program/program_util.h index c8011a3db2..8159b27845 100644 --- a/rpcs3/Emu/RSX/Program/program_util.h +++ b/rpcs3/Emu/RSX/Program/program_util.h @@ -67,6 +67,9 @@ namespace rsx // Translates an incoming range of constants against our mapping. // If there is no linear mapping available, return -1, otherwise returns the translated index of the first slot // TODO: Move this somewhere else during refactor - int TranslateConstantsRange(int first_index, int count) const; + int translate_constants_range(int first_index, int count) const; + + // Returns true if this program consumes any constants in the range [first, first + count - 1] + bool overlaps_constants_range(int first_index, int count) const; }; } diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.cpp b/rpcs3/Emu/RSX/VK/VKGSRender.cpp index 6e53891fbf..8ebb41c099 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.cpp +++ b/rpcs3/Emu/RSX/VK/VKGSRender.cpp @@ -688,10 +688,10 @@ VKGSRender::VKGSRender(utils::serial* ar) noexcept : GSRender(ar) } // Initialize optional allocation information with placeholders - m_vertex_env_buffer_info = { m_vertex_env_ring_info.heap->value, 0, 32 }; - m_vertex_constants_buffer_info = { m_transform_constants_ring_info.heap->value, 0, 32 }; - m_fragment_env_buffer_info = { m_fragment_env_ring_info.heap->value, 0, 32 }; - m_fragment_texture_params_buffer_info = { m_fragment_texture_params_ring_info.heap->value, 0, 32 }; + m_vertex_env_buffer_info = { m_vertex_env_ring_info.heap->value, 0, 16 }; + m_vertex_constants_buffer_info = { m_transform_constants_ring_info.heap->value, 0, 16 }; + m_fragment_env_buffer_info = { m_fragment_env_ring_info.heap->value, 0, 16 }; + m_fragment_texture_params_buffer_info = { m_fragment_texture_params_ring_info.heap->value, 0, 16 }; m_raster_env_buffer_info = { m_raster_env_ring_info.heap->value, 0, 128 }; const auto limits = m_device->gpu().get_limits(); @@ -2192,7 +2192,7 @@ void VKGSRender::load_program_env() return std::make_pair(m_instancing_buffer_ring_info.map(constants_data_table_offset, size), size); }); - m_draw_processor.fill_constants_instancing_buffer(indirection_table_buf, constants_array_buf, *m_vertex_prog); + m_draw_processor.fill_constants_instancing_buffer(indirection_table_buf, constants_array_buf, m_vertex_prog); m_instancing_buffer_ring_info.unmap(); m_instancing_indirection_buffer_info = { m_instancing_buffer_ring_info.heap->value, indirection_table_offset, indirection_table_buf.size() }; @@ -2219,7 +2219,7 @@ void VKGSRender::load_program_env() } } - if (update_fragment_constants && !update_instruction_buffers) + if (update_fragment_constants && !m_shader_interpreter.is_interpreter(m_program)) { check_heap_status(VK_HEAP_CHECK_FRAGMENT_CONSTANTS_STORAGE); @@ -2350,9 +2350,9 @@ void VKGSRender::load_program_env() } // Clear flags - u32 handled_flags = rsx::pipeline_state::fragment_state_dirty | + rsx::flags32_t handled_flags = + rsx::pipeline_state::fragment_state_dirty | rsx::pipeline_state::vertex_state_dirty | - rsx::pipeline_state::fragment_constants_dirty | rsx::pipeline_state::fragment_texture_state_dirty; if (!update_instancing_data) @@ -2360,6 +2360,11 @@ void VKGSRender::load_program_env() handled_flags |= rsx::pipeline_state::transform_constants_dirty; } + if (update_fragment_constants && !m_shader_interpreter.is_interpreter(m_program)) + { + handled_flags |= rsx::pipeline_state::fragment_constants_dirty; + } + m_graphics_state.clear(handled_flags); } @@ -2438,13 +2443,19 @@ void VKGSRender::update_vertex_env(u32 id, const vk::vertex_upload_info& vertex_ void VKGSRender::patch_transform_constants(rsx::context* ctx, u32 index, u32 count) { - if (!m_vertex_prog) + if (!m_program || !m_vertex_prog) { // Shouldn't be reachable, but handle it correctly anyway m_graphics_state |= rsx::pipeline_state::transform_constants_dirty; return; } + if (!m_vertex_prog->overlaps_constants_range(index, count)) + { + // Nothing meaningful to us + return; + } + // Hot-patching transform constants mid-draw (instanced draw) std::pair data_range; void* data_source = nullptr; @@ -2458,7 +2469,7 @@ void VKGSRender::patch_transform_constants(rsx::context* ctx, u32 index, u32 cou data_range = { m_vertex_constants_buffer_info.offset + byte_offset, byte_count }; data_source = ®S(ctx)->transform_constants[index]; } - else if (auto xform_id = m_vertex_prog->TranslateConstantsRange(index, count); xform_id >= 0) + else if (auto xform_id = m_vertex_prog->translate_constants_range(index, count); xform_id >= 0) { const auto write_offset = xform_id * 16; const auto byte_count = count * 16; diff --git a/rpcs3/Emu/RSX/VK/VKResolveHelper.h b/rpcs3/Emu/RSX/VK/VKResolveHelper.h index ab9e827db9..6b83a5af9c 100644 --- a/rpcs3/Emu/RSX/VK/VKResolveHelper.h +++ b/rpcs3/Emu/RSX/VK/VKResolveHelper.h @@ -387,7 +387,7 @@ namespace vk struct stencilonly_unresolve : depth_resolve_base { - VkClearRect region{}; + VkClearRect clear_region{}; VkClearAttachment clear_info{}; stencilonly_unresolve() @@ -402,8 +402,8 @@ namespace vk renderpass_config.set_depth_mask(false); clear_info.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT; - region.baseArrayLayer = 0; - region.layerCount = 1; + clear_region.baseArrayLayer = 0; + clear_region.layerCount = 1; static_parameters_width = 3; @@ -425,7 +425,7 @@ namespace vk void emit_geometry(vk::command_buffer& cmd) override { - vkCmdClearAttachments(cmd, 1, &clear_info, 1, ®ion); + vkCmdClearAttachments(cmd, 1, &clear_info, 1, &clear_region); for (s32 write_mask = 0x1; write_mask <= 0x80; write_mask <<= 1) { @@ -444,8 +444,8 @@ namespace vk auto stencil_view = resolve_image->get_view(rsx::default_remap_vector.with_encoding(VK_REMAP_IDENTITY), VK_IMAGE_ASPECT_STENCIL_BIT); - region.rect.extent.width = resolve_image->width(); - region.rect.extent.height = resolve_image->height(); + clear_region.rect.extent.width = msaa_image->width(); + clear_region.rect.extent.height = msaa_image->height(); overlay_pass::run( cmd, diff --git a/rpcs3/Emu/RSX/rsx_cache.h b/rpcs3/Emu/RSX/rsx_cache.h index 7f82c210d9..f5eca2c85b 100644 --- a/rpcs3/Emu/RSX/rsx_cache.h +++ b/rpcs3/Emu/RSX/rsx_cache.h @@ -400,6 +400,7 @@ namespace rsx fp.texture_state.multisampled_textures = data.fp_multisampled_textures; fp.texcoord_control_mask = data.fp_texcoord_control; fp.two_sided_lighting = !!(data.fp_lighting_flags & 0x1); + fp.mrt_buffers_count = data.fp_mrt_count; return result; } diff --git a/rpcs3/main.cpp b/rpcs3/main.cpp index 404fa66eb9..f67670ccc9 100644 --- a/rpcs3/main.cpp +++ b/rpcs3/main.cpp @@ -111,6 +111,65 @@ extern char **environ; LOG_CHANNEL(sys_log, "SYS"); LOG_CHANNEL(q_debug, "QDEBUG"); +#ifdef _WIN32 +std::set get_one_drive_paths() +{ + std::set paths; + for (const char* key : { "OneDrive", "OneDriveConsumer", "OneDriveCommercial" }) + { + if (const char* env_path = std::getenv(key)) + { + sys_log.notice("get_one_drive_paths: Found OneDrive env path: '%s' (key='%s')", env_path, key); + paths.insert(env_path); + } + } + + for (const wchar_t* key : { L"Software\\Microsoft\\OneDrive\\Accounts\\Personal" }) + { + HKEY hkey = NULL; + LSTATUS status = RegOpenKeyW(HKEY_CURRENT_USER, key, &hkey); + if (status != ERROR_SUCCESS) + { + sys_log.trace("get_one_drive_paths: RegOpenKeyW failed: %s (key='%s')", fmt::win_error{static_cast(status), nullptr}, wchar_to_utf8(key)); + continue; + } + + std::wstring path_buffer; + static_cast(path_buffer.size() - 1); + DWORD type = 0U; + + do + { + path_buffer.resize(path_buffer.size() + MAX_PATH); + DWORD buffer_size = static_cast(path_buffer.size() - 1); + status = RegQueryValueExW(hkey, L"UserFolder", NULL, &type, reinterpret_cast(path_buffer.data()), &buffer_size); + } + while (status == ERROR_MORE_DATA); + + const LSTATUS close_status = RegCloseKey(hkey); + if (close_status != ERROR_SUCCESS) + { + sys_log.error("get_one_drive_paths: RegCloseKey failed: %s", fmt::win_error{static_cast(close_status), nullptr}); + } + + if (status != ERROR_SUCCESS) + { + sys_log.error("get_one_drive_paths: RegQueryValueExW failed: %s", fmt::win_error{static_cast(status), nullptr}); + continue; + } + + if ((type == REG_SZ) || (type == REG_EXPAND_SZ) || (type == REG_MULTI_SZ)) + { + const std::string path = wchar_to_utf8(path_buffer.data()); + sys_log.notice("get_one_drive_paths: Found OneDrive registry path: '%s' (key='%s')", path, wchar_to_utf8(key)); + paths.insert(path); + } + } + + return paths; +} +#endif + [[noreturn]] extern void report_fatal_error(std::string_view _text, bool is_html = false, bool include_help_text = true) { #ifdef __linux__ @@ -1135,6 +1194,21 @@ int main(int argc, char** argv) return 1; } } + +#ifdef _WIN32 + // Check OneDrive locations + for (const std::string& one_drive_path : get_one_drive_paths()) + { + if (Emu.IsPathInsideDir(emu_dir, one_drive_path)) + { + report_fatal_error(QObject::tr( + "RPCS3 should never be run from a OneDrive path!\n" + "Please move RPCS3 to a location not synced by OneDrive.\n" + "Current location:\n%0").arg(QString::fromStdString(emu_dir)).toStdString()); + return 1; + } + } +#endif } // Set timerslack value for Linux. The default value is 50,000ns. Change this to just 1 since we value precise timers.