Skip to content

Add ability to record/screenshot with overlays #16803

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion rpcs3/Emu/RSX/GL/GLDraw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,7 @@ void GLGSRender::bind_texture_env()
}
else
{
auto target = gl::get_target(current_fragment_program.get_texture_dimension(i));
const auto target = gl::get_target(current_fragment_program.get_texture_dimension(i));
cmd->bind_texture(GL_FRAGMENT_TEXTURES_START + i, target, m_null_textures[target]->id());

if (current_fragment_program.texture_state.redirected_textures & (1 << i))
Expand Down
2 changes: 2 additions & 0 deletions rpcs3/Emu/RSX/GL/GLGSRender.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ class GLGSRender : public GSRender, public ::rsx::reports::ZCULL_control
std::unique_ptr<gl::texture> m_flip_tex_color[2];

// Present
gl::fbo m_sshot_fbo;
std::unique_ptr<gl::texture> m_sshot_tex;
std::unique_ptr<gl::upscaler> m_upscaler;
output_scaling_mode m_output_scaling = output_scaling_mode::bilinear;

Expand Down
113 changes: 77 additions & 36 deletions rpcs3/Emu/RSX/GL/GLPresent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,8 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info)
// Enable drawing to window backbuffer
gl::screen.bind();

gl::texture *image_to_flip = nullptr, *image_to_flip2 = nullptr;
gl::texture* image_to_flip = nullptr;
gl::texture* image_to_flip2 = nullptr;

if (info.buffer < display_buffers_count && buffer_width && buffer_height)
{
Expand Down Expand Up @@ -276,15 +277,86 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info)
gl::screen.clear(gl::buffers::color);
}

if (m_overlay_manager && m_overlay_manager->has_dirty())
{
m_overlay_manager->lock_shared();

std::vector<u32> uids_to_dispose;
uids_to_dispose.reserve(m_overlay_manager->get_dirty().size());

for (const auto& view : m_overlay_manager->get_dirty())
{
m_ui_renderer.remove_temp_resources(view->uid);
uids_to_dispose.push_back(view->uid);
}

m_overlay_manager->unlock_shared();
m_overlay_manager->dispose(uids_to_dispose);
}

const auto render_overlays = [this, &cmd](gl::texture* dst, const areau& aspect_ratio)
{
if (m_overlay_manager && m_overlay_manager->has_visible())
{
GLuint target = 0;

if (dst)
{
m_sshot_fbo.bind();
m_sshot_fbo.color = dst->id();
target = dst->id();
}
else
{
gl::screen.bind();
}

// Lock to avoid modification during run-update chain
std::lock_guard lock(*m_overlay_manager);

for (const auto& view : m_overlay_manager->get_views())
{
m_ui_renderer.run(cmd, aspect_ratio, target, *view.get());
}
}
};

if (image_to_flip)
{
if (g_user_asked_for_screenshot || (g_recording_mode != recording_mode::stopped && m_frame->can_consume_frame()))
{
static const gl::pixel_pack_settings pack_settings{};

gl::texture* tex = image_to_flip;

if (g_cfg.video.record_with_overlays)
{
m_sshot_fbo.create();
m_sshot_tex = std::make_unique<gl::texture>(
GLenum(image_to_flip->get_target()),
image_to_flip->width(),
image_to_flip->height(),
image_to_flip->depth(),
image_to_flip->levels(),
image_to_flip->samples(),
GLenum(image_to_flip->get_internal_format()),
image_to_flip->format_class());

tex = m_sshot_tex.get();

static const position3u offset{};
gl::g_hw_blitter->copy_image(cmd, image_to_flip, tex, 0, 0, offset, offset, { tex->width(), tex->height(), 1 });

render_overlays(tex, areau(0, 0, image_to_flip->width(), image_to_flip->height()));
m_sshot_fbo.remove();
}

std::vector<u8> sshot_frame(buffer_height * buffer_width * 4);
glGetError();

gl::pixel_pack_settings pack_settings{};
image_to_flip->copy_to(sshot_frame.data(), gl::texture::format::rgba, gl::texture::type::ubyte, pack_settings);
tex->copy_to(sshot_frame.data(), gl::texture::format::rgba, gl::texture::type::ubyte, pack_settings);

m_sshot_tex.reset();

if (GLenum err = glGetError(); err != GL_NO_ERROR)
{
Expand All @@ -296,7 +368,7 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info)
}
else
{
m_frame->present_frame(sshot_frame, buffer_width * 4, buffer_width, buffer_height, false);
m_frame->present_frame(std::move(sshot_frame), buffer_width * 4, buffer_width, buffer_height, false);
}
}

Expand Down Expand Up @@ -349,38 +421,7 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info)
}
}

if (m_overlay_manager)
{
if (m_overlay_manager->has_dirty())
{
m_overlay_manager->lock_shared();

std::vector<u32> uids_to_dispose;
uids_to_dispose.reserve(m_overlay_manager->get_dirty().size());

for (const auto& view : m_overlay_manager->get_dirty())
{
m_ui_renderer.remove_temp_resources(view->uid);
uids_to_dispose.push_back(view->uid);
}

m_overlay_manager->unlock_shared();
m_overlay_manager->dispose(uids_to_dispose);
}

if (m_overlay_manager->has_visible())
{
gl::screen.bind();

// Lock to avoid modification during run-update chain
std::lock_guard lock(*m_overlay_manager);

for (const auto& view : m_overlay_manager->get_views())
{
m_ui_renderer.run(cmd, areau(aspect_ratio), 0, *view.get());
}
}
}
render_overlays(nullptr, areau(aspect_ratio));

if (g_cfg.video.debug_overlay)
{
Expand Down
2 changes: 1 addition & 1 deletion rpcs3/Emu/RSX/GL/glutils/image.h
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ namespace gl
GLenum m_aspect_flags = 0;
texture* m_image_data = nullptr;

GLenum component_swizzle[4];
GLenum component_swizzle[4] {};

texture_view() = default;

Expand Down
2 changes: 1 addition & 1 deletion rpcs3/Emu/RSX/GSFrameBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,6 @@ class GSFrameBase
virtual display_handle_t handle() const = 0;

virtual bool can_consume_frame() const = 0;
virtual void present_frame(std::vector<u8>& data, u32 pitch, u32 width, u32 height, bool is_bgra) const = 0;
virtual void present_frame(std::vector<u8>&& data, u32 pitch, u32 width, u32 height, bool is_bgra) const = 0;
virtual void take_screenshot(std::vector<u8>&& sshot_data, u32 sshot_width, u32 sshot_height, bool is_bgra) = 0;
};
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ namespace rsx
add_checkbox(&g_cfg.misc.show_pressure_intensity_toggle_hint, localized_string_id::HOME_MENU_SETTINGS_OVERLAYS_SHOW_PRESSURE_INTENSITY_TOGGLE_HINT);
add_checkbox(&g_cfg.misc.show_analog_limiter_toggle_hint, localized_string_id::HOME_MENU_SETTINGS_OVERLAYS_SHOW_ANALOG_LIMITER_TOGGLE_HINT);
add_checkbox(&g_cfg.misc.show_mouse_and_keyboard_toggle_hint, localized_string_id::HOME_MENU_SETTINGS_OVERLAYS_SHOW_MOUSE_AND_KB_TOGGLE_HINT);
add_checkbox(&g_cfg.video.record_with_overlays, localized_string_id::HOME_MENU_SETTINGS_OVERLAYS_RECORD_WITH_OVERLAYS);

apply_layout();
}
Expand Down
10 changes: 5 additions & 5 deletions rpcs3/Emu/RSX/VK/VKFramebuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ namespace vk
vk::framebuffer_holder* get_framebuffer(VkDevice dev, u16 width, u16 height, VkBool32 has_input_attachments, VkRenderPass renderpass, const std::vector<vk::image*>& image_list)
{
framebuffer_storage_key key(width, height, has_input_attachments);
auto &queue = g_framebuffers_cache[key.encoded];
auto& queue = g_framebuffers_cache[key.encoded];

for (auto &fbo : queue)
for (const auto& fbo : queue)
{
if (fbo->matches(image_list, width, height))
{
Expand All @@ -42,7 +42,7 @@ namespace vk
std::vector<std::unique_ptr<vk::image_view>> image_views;
image_views.reserve(image_list.size());

for (auto &e : image_list)
for (const auto& e : image_list)
{
const VkImageSubresourceRange subres = { e->aspect(), 0, 1, 0, 1 };
image_views.push_back(std::make_unique<vk::image_view>(dev, e, VK_IMAGE_VIEW_TYPE_2D, vk::default_component_map, subres));
Expand All @@ -58,9 +58,9 @@ namespace vk
vk::framebuffer_holder* get_framebuffer(VkDevice dev, u16 width, u16 height, VkBool32 has_input_attachments, VkRenderPass renderpass, VkFormat format, VkImage attachment)
{
framebuffer_storage_key key(width, height, has_input_attachments);
auto &queue = g_framebuffers_cache[key.encoded];
auto& queue = g_framebuffers_cache[key.encoded];

for (const auto &e : queue)
for (const auto& e : queue)
{
if (e->attachments[0]->info.image == attachment)
{
Expand Down
71 changes: 53 additions & 18 deletions rpcs3/Emu/RSX/VK/VKPresent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,21 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info)
}
}

const bool has_overlay = (m_overlay_manager && m_overlay_manager->has_visible());
const auto render_overlays = [&](vk::framebuffer_holder* fbo, const areau& area)
{
if (!has_overlay) return;

// Lock to avoid modification during run-update chain
auto ui_renderer = vk::get_overlay_pass<vk::ui_overlay_renderer>();
std::lock_guard lock(*m_overlay_manager);

for (const auto& view : m_overlay_manager->get_views())
{
ui_renderer->run(*m_current_command_buffer, area, fbo, single_target_pass, m_texture_upload_buffer_ring_info, *view.get());
}
};

if (image_to_flip)
{
const bool use_full_rgb_range_output = g_cfg.video.full_rgb_range_output.get();
Expand Down Expand Up @@ -699,30 +714,60 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info)
copy_info.imageExtent.height = buffer_height;
copy_info.imageExtent.depth = 1;

image_to_flip->push_layout(*m_current_command_buffer, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
vk::copy_image_to_buffer(*m_current_command_buffer, image_to_flip, &sshot_vkbuf, copy_info);
image_to_flip->pop_layout(*m_current_command_buffer);
std::unique_ptr<vk::image> tmp_tex;
vk::image* image_to_copy = image_to_flip;

if (g_cfg.video.record_with_overlays && has_overlay)
{
const auto key = vk::get_renderpass_key(m_swapchain->get_surface_format());
single_target_pass = vk::get_renderpass(*m_device, key);
ensure(single_target_pass != VK_NULL_HANDLE);

tmp_tex = std::make_unique<vk::image>(*m_device, m_device->get_memory_mapping().device_local, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
image_to_flip->type(), image_to_flip->format(), image_to_flip->width(), image_to_flip->height(), 1, 1, image_to_flip->layers(), VK_SAMPLE_COUNT_1_BIT, image_to_flip->current_layout,
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);

tmp_tex->change_layout(*m_current_command_buffer, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
image_to_flip->push_layout(*m_current_command_buffer, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);

const areai rect = areai(0, 0, buffer_width, buffer_height);
vk::copy_image(*m_current_command_buffer, image_to_flip, tmp_tex.get(), rect, rect, 1);

image_to_flip->pop_layout(*m_current_command_buffer);
tmp_tex->change_layout(*m_current_command_buffer, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);

vk::framebuffer_holder* sshot_fbo = vk::get_framebuffer(*m_device, buffer_width, buffer_height, VK_FALSE, single_target_pass, { tmp_tex.get() });
sshot_fbo->add_ref();
render_overlays(sshot_fbo, areau(rect));
sshot_fbo->release();

image_to_copy = tmp_tex.get();
}

image_to_copy->push_layout(*m_current_command_buffer, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
vk::copy_image_to_buffer(*m_current_command_buffer, image_to_copy, &sshot_vkbuf, copy_info);
image_to_copy->pop_layout(*m_current_command_buffer);

flush_command_queue(true);
auto src = sshot_vkbuf.map(0, sshot_size);
const auto src = sshot_vkbuf.map(0, sshot_size);
std::vector<u8> sshot_frame(sshot_size);
memcpy(sshot_frame.data(), src, sshot_size);
sshot_vkbuf.unmap();

const bool is_bgra = image_to_flip->format() == VK_FORMAT_B8G8R8A8_UNORM;
const bool is_bgra = image_to_copy->format() == VK_FORMAT_B8G8R8A8_UNORM;

if (g_user_asked_for_screenshot.exchange(false))
{
m_frame->take_screenshot(std::move(sshot_frame), buffer_width, buffer_height, is_bgra);
}
else
{
m_frame->present_frame(sshot_frame, buffer_width * 4, buffer_width, buffer_height, is_bgra);
m_frame->present_frame(std::move(sshot_frame), buffer_width * 4, buffer_width, buffer_height, is_bgra);
}
}
}

const bool has_overlay = (m_overlay_manager && m_overlay_manager->has_visible());
if (g_cfg.video.debug_overlay || has_overlay)
{
if (target_layout != VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL)
Expand Down Expand Up @@ -754,17 +799,7 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info)

direct_fbo->add_ref();

if (has_overlay)
{
// Lock to avoid modification during run-update chain
auto ui_renderer = vk::get_overlay_pass<vk::ui_overlay_renderer>();
std::lock_guard lock(*m_overlay_manager);

for (const auto& view : m_overlay_manager->get_views())
{
ui_renderer->run(*m_current_command_buffer, areau(aspect_ratio), direct_fbo, single_target_pass, m_texture_upload_buffer_ring_info, *view.get());
}
}
render_overlays(direct_fbo, areau(aspect_ratio));

if (g_cfg.video.debug_overlay)
{
Expand Down
12 changes: 6 additions & 6 deletions rpcs3/Emu/RSX/VK/vkutils/framebuffer_object.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,35 +47,35 @@ namespace vk
vkDestroyFramebuffer(m_device, value, nullptr);
}

u32 width()
u32 width() const
{
return m_width;
}

u32 height()
u32 height() const
{
return m_height;
}

u8 samples()
u8 samples() const
{
ensure(!attachments.empty());
return attachments[0]->image()->samples();
}

VkFormat format()
VkFormat format() const
{
ensure(!attachments.empty());
return attachments[0]->image()->format();
}

VkFormat depth_format()
VkFormat depth_format() const
{
ensure(!attachments.empty());
return attachments.back()->image()->format();
}

bool matches(const std::vector<vk::image*>& fbo_images, u32 width, u32 height)
bool matches(const std::vector<vk::image*>& fbo_images, u32 width, u32 height) const
{
if (m_width != width || m_height != height)
return false;
Expand Down
1 change: 1 addition & 0 deletions rpcs3/Emu/localized_string_id.h
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ enum class localized_string_id
HOME_MENU_SETTINGS_OVERLAYS_SHOW_PRESSURE_INTENSITY_TOGGLE_HINT,
HOME_MENU_SETTINGS_OVERLAYS_SHOW_ANALOG_LIMITER_TOGGLE_HINT,
HOME_MENU_SETTINGS_OVERLAYS_SHOW_MOUSE_AND_KB_TOGGLE_HINT,
HOME_MENU_SETTINGS_OVERLAYS_RECORD_WITH_OVERLAYS,
HOME_MENU_SETTINGS_PERFORMANCE_OVERLAY,
HOME_MENU_SETTINGS_PERFORMANCE_OVERLAY_ENABLE,
HOME_MENU_SETTINGS_PERFORMANCE_OVERLAY_ENABLE_FRAMERATE_GRAPH,
Expand Down
1 change: 1 addition & 0 deletions rpcs3/Emu/system_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ struct cfg_root : cfg::node
cfg::_bool disable_msl_fast_math{ this, "Disable MSL Fast Math", false };
cfg::_bool disable_async_host_memory_manager{ this, "Disable Asynchronous Memory Manager", false, true };
cfg::_enum<output_scaling_mode> output_scaling{ this, "Output Scaling Mode", output_scaling_mode::bilinear, true };
cfg::_bool record_with_overlays{ this, "Record With Overlays", true, true };

struct node_vk : cfg::node
{
Expand Down
Loading