diff --git a/rpcs3/Emu/CMakeLists.txt b/rpcs3/Emu/CMakeLists.txt index 743b6c88d3..d68ab8c795 100644 --- a/rpcs3/Emu/CMakeLists.txt +++ b/rpcs3/Emu/CMakeLists.txt @@ -546,6 +546,7 @@ target_sources(rpcs3_emu PRIVATE RSX/Overlays/overlay_trophy_notification.cpp RSX/Overlays/overlay_user_list_dialog.cpp RSX/Overlays/overlay_utils.cpp + RSX/Overlays/overlay_video.cpp RSX/Overlays/Shaders/shader_loading_dialog.cpp RSX/Overlays/Shaders/shader_loading_dialog_native.cpp RSX/Program/CgBinaryFragmentProgram.cpp diff --git a/rpcs3/Emu/RSX/GL/GLOverlays.cpp b/rpcs3/Emu/RSX/GL/GLOverlays.cpp index c5fac83404..983bed6f85 100644 --- a/rpcs3/Emu/RSX/GL/GLOverlays.cpp +++ b/rpcs3/Emu/RSX/GL/GLOverlays.cpp @@ -218,12 +218,12 @@ namespace gl m_input_filter = gl::filter::linear; } - gl::texture_view* ui_overlay_renderer::load_simple_image(rsx::overlays::image_info* desc, bool temp_resource, u32 owner_uid) + gl::texture_view* ui_overlay_renderer::load_simple_image(rsx::overlays::image_info_base* desc, bool temp_resource, u32 owner_uid) { auto tex = std::make_unique(GL_TEXTURE_2D, desc->w, desc->h, 1, 1, 1, GL_RGBA8, RSX_FORMAT_CLASS_COLOR); tex->copy_from(desc->get_data(), gl::texture::format::rgba, gl::texture::type::uint_8_8_8_8, {}); - GLenum remap[] = { GL_RED, GL_ALPHA, GL_BLUE, GL_GREEN }; + const GLenum remap[] = { GL_RED, GL_ALPHA, GL_BLUE, GL_GREEN }; auto view = std::make_unique(tex.get(), remap); auto result = view.get(); @@ -234,7 +234,7 @@ namespace gl } else { - u64 key = reinterpret_cast(desc); + const u64 key = reinterpret_cast(desc); temp_image_cache[key] = std::make_pair(owner_uid, std::move(tex)); temp_view_cache[key] = std::move(view); } @@ -249,7 +249,7 @@ namespace gl rsx::overlays::resource_config configuration; configuration.load_files(); - for (const auto &res : configuration.texture_raw_data) + for (const auto& res : configuration.texture_raw_data) { load_simple_image(res.get(), false, -1); } @@ -318,13 +318,22 @@ namespace gl return result; } - gl::texture_view* ui_overlay_renderer::find_temp_image(rsx::overlays::image_info* desc, u32 owner_uid) + gl::texture_view* ui_overlay_renderer::find_temp_image(rsx::overlays::image_info_base* desc, u32 owner_uid) { - auto key = reinterpret_cast(desc); + const bool dirty = std::exchange(desc->dirty, false); + const u64 key = reinterpret_cast(desc); + auto cached = temp_view_cache.find(key); if (cached != temp_view_cache.end()) { - return cached->second.get(); + gl::texture_view* view = cached->second.get(); + + if (dirty) + { + view->image()->copy_from(desc->get_data(), gl::texture::format::rgba, gl::texture::type::uint_8_8_8_8, {}); + } + + return view; } return load_simple_image(desc, true, owner_uid); @@ -420,7 +429,7 @@ namespace gl } case rsx::overlays::image_resource_id::raw_image: { - cmd_->bind_texture(31, GL_TEXTURE_2D, find_temp_image(static_cast(cmd.config.external_data_ref), ui.uid)->id()); + cmd_->bind_texture(31, GL_TEXTURE_2D, find_temp_image(static_cast(cmd.config.external_data_ref), ui.uid)->id()); break; } case rsx::overlays::image_resource_id::font_file: diff --git a/rpcs3/Emu/RSX/GL/GLOverlays.h b/rpcs3/Emu/RSX/GL/GLOverlays.h index f70e885d2e..bd2be2d0ed 100644 --- a/rpcs3/Emu/RSX/GL/GLOverlays.h +++ b/rpcs3/Emu/RSX/GL/GLOverlays.h @@ -75,7 +75,7 @@ namespace gl ui_overlay_renderer(); - gl::texture_view* load_simple_image(rsx::overlays::image_info* desc, bool temp_resource, u32 owner_uid); + gl::texture_view* load_simple_image(rsx::overlays::image_info_base* desc, bool temp_resource, u32 owner_uid); void create(); void destroy(); @@ -84,7 +84,7 @@ namespace gl gl::texture_view* find_font(rsx::overlays::font* font); - gl::texture_view* find_temp_image(rsx::overlays::image_info* desc, u32 owner_uid); + gl::texture_view* find_temp_image(rsx::overlays::image_info_base* desc, u32 owner_uid); void set_primitive_type(rsx::overlays::primitive_type type); diff --git a/rpcs3/Emu/RSX/Overlays/overlay_controls.cpp b/rpcs3/Emu/RSX/Overlays/overlay_controls.cpp index fcccaa4645..23e7fccb55 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_controls.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_controls.cpp @@ -913,7 +913,7 @@ namespace rsx external_ref = nullptr; } - void image_view::set_raw_image(image_info* raw_image) + void image_view::set_raw_image(image_info_base* raw_image) { image_resource_ref = image_resource_id::raw_image; external_ref = raw_image; diff --git a/rpcs3/Emu/RSX/Overlays/overlay_controls.h b/rpcs3/Emu/RSX/Overlays/overlay_controls.h index 07f1a8c5f0..b6dd2b84da 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_controls.h +++ b/rpcs3/Emu/RSX/Overlays/overlay_controls.h @@ -30,23 +30,29 @@ namespace rsx triangle_fan = 4 }; - struct image_info + struct image_info_base + { + int w = 0, h = 0, channels = 0; + int bpp = 0; + bool dirty = false; + + virtual const u8* get_data() const = 0; + }; + + struct image_info : public image_info_base { private: u8* data = nullptr; std::vector data_grey; public: - int w = 0, h = 0, channels = 0; - int bpp = 0; - image_info(image_info&) = delete; image_info(const std::string& filename, bool grayscaled = false); image_info(const std::vector& bytes, bool grayscaled = false); ~image_info(); void load_data(const std::vector& bytes, bool grayscaled = false); - const u8* get_data() const { return channels == 4 ? data : data_grey.empty() ? nullptr : data_grey.data(); } + const u8* get_data() const override { return channels == 4 ? data : data_grey.empty() ? nullptr : data_grey.data(); } }; struct resource_config @@ -204,6 +210,7 @@ namespace rsx virtual std::vector render_text(const char32_t* string, f32 x, f32 y); virtual compiled_resource& get_compiled(); void measure_text(u16& width, u16& height, bool ignore_word_wrap = false) const; + virtual void set_selected(bool selected) { static_cast(selected); } protected: bool m_is_compiled = false; // Only use m_is_compiled as a getter in is_compiled() if possible @@ -269,7 +276,7 @@ namespace rsx struct image_view : public overlay_element { - private: + protected: u8 image_resource_ref = image_resource_id::none; void* external_ref = nullptr; @@ -282,7 +289,7 @@ namespace rsx compiled_resource& get_compiled() override; void set_image_resource(u8 resource_id); - void set_raw_image(image_info* raw_image); + void set_raw_image(image_info_base* raw_image); void clear_image(); void set_blur_strength(u8 strength); }; diff --git a/rpcs3/Emu/RSX/Overlays/overlay_video.cpp b/rpcs3/Emu/RSX/Overlays/overlay_video.cpp new file mode 100644 index 0000000000..ac6017d35f --- /dev/null +++ b/rpcs3/Emu/RSX/Overlays/overlay_video.cpp @@ -0,0 +1,116 @@ +#include "stdafx.h" +#include "overlay_video.h" +#include "Emu/System.h" + +namespace rsx +{ + namespace overlays + { + video_view::video_view(const std::string& video_path, const std::string& thumbnail_path) + { + init_video(video_path); + + if (!thumbnail_path.empty()) + { + m_thumbnail_info = std::make_unique(thumbnail_path); + set_raw_image(m_thumbnail_info.get()); + } + } + + video_view::video_view(const std::string& video_path, const std::vector& thumbnail_buf) + { + init_video(video_path); + + if (!thumbnail_buf.empty()) + { + m_thumbnail_info = std::make_unique(thumbnail_buf); + set_raw_image(m_thumbnail_info.get()); + } + } + + video_view::video_view(const std::string& video_path, u8 thumbnail_id) + : m_thumbnail_id(thumbnail_id) + { + init_video(video_path); + set_image_resource(thumbnail_id); + } + + video_view::~video_view() + { + } + + void video_view::init_video(const std::string& video_path) + { + if (video_path.empty()) return; + + m_video_source = Emu.GetCallbacks().make_video_source(); + ensure(!!m_video_source); + + m_video_source->set_update_callback([this]() + { + if (m_video_active) + { + m_is_compiled = false; + } + }); + m_video_source->set_video_path(video_path); + } + + void video_view::set_active(bool active) + { + if (m_video_source) + { + m_video_source->set_active(active); + m_video_active = active; + m_is_compiled = false; + } + } + + void video_view::update() + { + if (m_video_active && m_video_source && m_video_source->get_active()) + { + if (!m_video_source->has_new()) + { + return; + } + + m_buffer_index = (m_buffer_index + 1) % m_video_info.size(); + + auto& info = m_video_info.at(m_buffer_index); + if (!info) + { + info = std::make_unique(); + } + + m_video_source->get_image(info->data, info->w, info->h, info->channels, info->bpp); + info->dirty = true; + + set_raw_image(info.get()); + m_is_compiled = false; + return; + } + + if (m_thumbnail_info && m_thumbnail_info.get() != external_ref) + { + set_raw_image(m_thumbnail_info.get()); + m_is_compiled = false; + return; + } + + if (m_thumbnail_id != image_resource_id::none && m_thumbnail_id != image_resource_ref) + { + set_image_resource(m_thumbnail_id); + m_is_compiled = false; + return; + } + } + + compiled_resource& video_view::get_compiled() + { + update(); + + return external_ref ? image_view::get_compiled() : overlay_element::get_compiled(); + } + } +} diff --git a/rpcs3/Emu/RSX/Overlays/overlay_video.h b/rpcs3/Emu/RSX/Overlays/overlay_video.h new file mode 100644 index 0000000000..bdff580f0e --- /dev/null +++ b/rpcs3/Emu/RSX/Overlays/overlay_video.h @@ -0,0 +1,40 @@ +#pragma once + +#include "overlay_controls.h" +#include "util/video_source.h" + +namespace rsx +{ + namespace overlays + { + struct video_info : public image_info_base + { + std::vector data; + const u8* get_data() const override { return data.empty() ? nullptr : data.data(); } + }; + + class video_view final : public image_view + { + public: + video_view(const std::string& video_path, const std::string& thumbnail_path); + video_view(const std::string& video_path, const std::vector& thumbnail_buf); + video_view(const std::string& video_path, u8 thumbnail_id); + virtual ~video_view(); + + void set_active(bool active); + + void update(); + compiled_resource& get_compiled() override; + + private: + void init_video(const std::string& video_path); + + usz m_buffer_index = 0; + std::array, 2> m_video_info; // double buffer + std::unique_ptr m_video_source; + std::unique_ptr m_thumbnail_info; + u8 m_thumbnail_id = image_resource_id::none; + bool m_video_active = false; // This is the expected state. The actual state is found in the video source. + }; + } +} diff --git a/rpcs3/Emu/RSX/VK/VKOverlays.cpp b/rpcs3/Emu/RSX/VK/VKOverlays.cpp index 2976d90c76..f195ee6c15 100644 --- a/rpcs3/Emu/RSX/VK/VKOverlays.cpp +++ b/rpcs3/Emu/RSX/VK/VKOverlays.cpp @@ -386,22 +386,14 @@ namespace vk VK_BLEND_OP_ADD, VK_BLEND_OP_ADD); } - vk::image_view* ui_overlay_renderer::upload_simple_texture(vk::render_device& dev, vk::command_buffer& cmd, - vk::data_heap& upload_heap, u64 key, u32 w, u32 h, u32 layers, bool font, bool temp, const void* pixel_src, u32 owner_uid) + void ui_overlay_renderer::upload_simple_texture(vk::image* tex, vk::command_buffer& cmd, + vk::data_heap& upload_heap, u32 w, u32 h, u32 layers, bool font, const void* pixel_src) { - const VkFormat format = (font) ? VK_FORMAT_R8_UNORM : VK_FORMAT_B8G8R8A8_UNORM; const u32 pitch = (font) ? w : w * 4; const u32 data_size = pitch * h * layers; const auto offset = upload_heap.alloc<512>(data_size); const auto addr = upload_heap.map(offset, data_size); - const VkImageSubresourceRange range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, layers }; - - auto tex = std::make_unique(dev, dev.get_memory_mapping().device_local, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, - VK_IMAGE_TYPE_2D, format, std::max(w, 1u), std::max(h, 1u), 1, 1, layers, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, - 0, VMM_ALLOCATION_POOL_UNDEFINED); - if (pixel_src && data_size) std::memcpy(addr, pixel_src, data_size); else if (data_size) @@ -409,17 +401,31 @@ namespace vk upload_heap.unmap(); - VkBufferImageCopy region; - region.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, layers }; - region.bufferOffset = offset; - region.bufferRowLength = w; - region.bufferImageHeight = h; - region.imageOffset = {}; - region.imageExtent = { static_cast(w), static_cast(h), 1u }; - - change_image_layout(cmd, tex.get(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, range); + const VkImageSubresourceRange range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, layers }; + VkBufferImageCopy region { + .bufferOffset = offset, + .bufferRowLength = w, + .bufferImageHeight = h, + .imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, layers }, + .imageOffset = {}, + .imageExtent = { static_cast(w), static_cast(h), 1u } + }; + change_image_layout(cmd, tex, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, range); vkCmdCopyBufferToImage(cmd, upload_heap.heap->value, tex->value, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); - change_image_layout(cmd, tex.get(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, range); + change_image_layout(cmd, tex, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, range); + } + + vk::image_view* ui_overlay_renderer::upload_simple_texture(vk::render_device& dev, vk::command_buffer& cmd, + vk::data_heap& upload_heap, u64 key, u32 w, u32 h, u32 layers, bool font, bool temp, const void* pixel_src, u32 owner_uid) + { + const VkFormat format = (font) ? VK_FORMAT_R8_UNORM : VK_FORMAT_B8G8R8A8_UNORM; + + auto tex = std::make_unique(dev, dev.get_memory_mapping().device_local, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + VK_IMAGE_TYPE_2D, format, std::max(w, 1u), std::max(h, 1u), 1, 1, layers, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + 0, VMM_ALLOCATION_POOL_UNDEFINED); + + upload_simple_texture(tex.get(), cmd, upload_heap, w, h, layers, font, pixel_src); auto view = std::make_unique(dev, tex.get()); @@ -521,12 +527,23 @@ namespace vk true, false, bytes.data(), -1); } - vk::image_view* ui_overlay_renderer::find_temp_image(rsx::overlays::image_info* desc, vk::command_buffer& cmd, vk::data_heap& upload_heap, u32 owner_uid) + vk::image_view* ui_overlay_renderer::find_temp_image(rsx::overlays::image_info_base* desc, vk::command_buffer& cmd, vk::data_heap& upload_heap, u32 owner_uid) { - u64 key = reinterpret_cast(desc); - auto found = temp_view_cache.find(key); - if (found != temp_view_cache.end()) - return found->second.get(); + const bool dirty = std::exchange(desc->dirty, false); + const u64 key = reinterpret_cast(desc); + + auto cached = temp_view_cache.find(key); + if (cached != temp_view_cache.end()) + { + vk::image_view* view = cached->second.get(); + + if (dirty) + { + upload_simple_texture(view->image(), cmd, upload_heap, desc->w, desc->h, 1, false, desc->get_data()); + } + + return view; + } return upload_simple_texture(cmd.get_command_pool().get_owner(), cmd, upload_heap, key, desc->w, desc->h, 1, false, true, desc->get_data(), owner_uid); @@ -693,7 +710,7 @@ namespace vk : rsx::overlays::texture_sampling_mode::font3D; break; case rsx::overlays::image_resource_id::raw_image: - src = find_temp_image(static_cast(command.config.external_data_ref), cmd, upload_heap, ui.uid); + src = find_temp_image(static_cast(command.config.external_data_ref), cmd, upload_heap, ui.uid); break; default: src = view_cache[command.config.texture_ref].get(); diff --git a/rpcs3/Emu/RSX/VK/VKOverlays.h b/rpcs3/Emu/RSX/VK/VKOverlays.h index 2b11285653..7308a5c894 100644 --- a/rpcs3/Emu/RSX/VK/VKOverlays.h +++ b/rpcs3/Emu/RSX/VK/VKOverlays.h @@ -154,6 +154,9 @@ namespace vk ui_overlay_renderer(); + void upload_simple_texture(vk::image* tex, vk::command_buffer& cmd, + vk::data_heap& upload_heap, u32 w, u32 h, u32 layers, bool font, const void* pixel_src); + vk::image_view* upload_simple_texture(vk::render_device& dev, vk::command_buffer& cmd, vk::data_heap& upload_heap, u64 key, u32 w, u32 h, u32 layers, bool font, bool temp, const void* pixel_src, u32 owner_uid); @@ -164,7 +167,7 @@ namespace vk void remove_temp_resources(u32 key); vk::image_view* find_font(rsx::overlays::font* font, vk::command_buffer& cmd, vk::data_heap& upload_heap); - vk::image_view* find_temp_image(rsx::overlays::image_info* desc, vk::command_buffer& cmd, vk::data_heap& upload_heap, u32 owner_uid); + vk::image_view* find_temp_image(rsx::overlays::image_info_base* desc, vk::command_buffer& cmd, vk::data_heap& upload_heap, u32 owner_uid); std::vector get_push_constants() override; diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index 7a5234d01a..07414051ed 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -147,6 +147,7 @@ + @@ -682,6 +683,7 @@ + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index 50e3e666a7..c0ef133d1a 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -1354,6 +1354,9 @@ Emu\GPU\RSX\Common + + Emu\GPU\RSX\Overlays + @@ -2719,6 +2722,9 @@ Utilities + + Emu\GPU\RSX\Overlays +