diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.h b/rpcs3/Emu/RSX/GL/GLGSRender.h index a4e5b6dda1..0d626debc7 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.h +++ b/rpcs3/Emu/RSX/GL/GLGSRender.h @@ -154,7 +154,7 @@ private: void update_draw_state(); - GLuint get_present_source(gl::present_surface_info* info, const rsx::avconf* avconfig); + gl::texture* get_present_source(gl::present_surface_info* info, const rsx::avconf* avconfig); public: void set_viewport(); diff --git a/rpcs3/Emu/RSX/GL/GLOverlays.h b/rpcs3/Emu/RSX/GL/GLOverlays.h index 5c889f9956..8145cf3424 100644 --- a/rpcs3/Emu/RSX/GL/GLOverlays.h +++ b/rpcs3/Emu/RSX/GL/GLOverlays.h @@ -740,15 +740,39 @@ namespace gl fs_src = "#version 420\n\n" "layout(binding=31) uniform sampler2D fs0;\n" + "layout(binding=30) uniform sampler2D fs1;\n" "layout(location=0) in vec2 tc0;\n" "layout(location=0) out vec4 ocol;\n" "\n" "uniform float gamma;\n" "uniform int limit_range;\n" + "uniform int stereo;\n" + "uniform int stereo_image_count;\n" + "\n" + "vec4 read_source()\n" + "{\n" + " if (stereo == 0) return texture(fs0, tc0);\n" + "\n" + " vec4 left, right;\n" + " if (stereo_image_count == 2)\n" + " {\n" + " left = texture(fs0, tc0);\n" + " right = texture(fs1, tc0);\n" + " }\n" + " else\n" + " {\n" + " vec2 coord_left = tc0 * vec2(1.f, 0.4898f);\n" + " vec2 coord_right = coord_left + vec2(0.f, 0.510204f);\n" + " left = texture(fs0, coord_left);\n" + " right = texture(fs0, coord_right);\n" + " }\n" + "\n" + " return vec4(left.r, right.g, right.b, 1.);\n" + "}\n" "\n" "void main()\n" "{\n" - " vec4 color = texture(fs0, tc0);\n" + " vec4 color = read_source();\n" " color.rgb = pow(color.rgb, vec3(gamma));\n" " if (limit_range > 0)\n" " ocol = ((color * 220.) + 16.) / 255.;\n" @@ -759,13 +783,19 @@ namespace gl input_filter = GL_LINEAR; } - void run(const areau& viewport, GLuint source, f32 gamma, bool limited_rgb) + void run(const areau& viewport, const rsx::simple_array& source, f32 gamma, bool limited_rgb, bool _3d) { program_handle.uniforms["gamma"] = gamma; program_handle.uniforms["limit_range"] = limited_rgb + 0; + program_handle.uniforms["stereo"] = _3d + 0; + program_handle.uniforms["stereo_image_count"] = (source[1] == GL_NONE? 1 : 2); saved_sampler_state saved(31, m_sampler); - glBindTexture(GL_TEXTURE_2D, source); + glBindTexture(GL_TEXTURE_2D, source[0]); + + saved_sampler_state saved2(30, m_sampler); + glBindTexture(GL_TEXTURE_2D, source[1]); + overlay_pass::run(viewport, GL_NONE, false, false); } }; diff --git a/rpcs3/Emu/RSX/GL/GLPresent.cpp b/rpcs3/Emu/RSX/GL/GLPresent.cpp index 22d49e2a17..f67436ac98 100644 --- a/rpcs3/Emu/RSX/GL/GLPresent.cpp +++ b/rpcs3/Emu/RSX/GL/GLPresent.cpp @@ -3,9 +3,9 @@ LOG_CHANNEL(screenshot); -GLuint GLGSRender::get_present_source(gl::present_surface_info* info, const rsx::avconf* avconfig) +gl::texture* GLGSRender::get_present_source(gl::present_surface_info* info, const rsx::avconf* avconfig) { - GLuint image = GL_NONE; + gl::texture* image = nullptr; // Check the surface store first gl::command_context cmd = { gl_state }; @@ -45,7 +45,7 @@ GLuint GLGSRender::get_present_source(gl::present_surface_info* info, const rsx: if (viable) { surface->read_barrier(cmd); - image = section.surface->get_surface(rsx::surface_access::read)->id(); + image = section.surface->get_surface(rsx::surface_access::read); info->width = rsx::apply_resolution_scale(std::min(surface_width, static_cast(info->width)), true); info->height = rsx::apply_resolution_scale(std::min(surface_height, static_cast(info->height)), true); @@ -56,7 +56,7 @@ GLuint GLGSRender::get_present_source(gl::present_surface_info* info, const rsx: { // Hack - this should be the first location to check for output // The render might have been done offscreen or in software and a blit used to display - if (const auto tex = surface->get_raw_texture(); tex) image = tex->id(); + if (const auto tex = surface->get_raw_texture(); tex) image = tex; } if (!image) @@ -76,7 +76,7 @@ GLuint GLGSRender::get_present_source(gl::present_surface_info* info, const rsx: m_gl_texture_cache.invalidate_range(cmd, range, rsx::invalidation_cause::read); m_flip_tex_color->copy_from(vm::base(info->address), gl::texture::format::bgra, gl::texture::type::uint_8_8_8_8, unpack_settings); - image = m_flip_tex_color->id(); + image = m_flip_tex_color.get(); } return image; @@ -121,7 +121,7 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info) // Enable drawing to window backbuffer gl::screen.bind(); - GLuint image_to_flip = GL_NONE; + GLuint image_to_flip = GL_NONE, image_to_flip2 = GL_NONE; if (info.buffer < display_buffers_count && buffer_width && buffer_height) { @@ -133,7 +133,29 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info) present_info.format = av_format; present_info.address = rsx::get_address(display_buffers[info.buffer].offset, CELL_GCM_LOCATION_LOCAL, HERE); - image_to_flip = get_present_source(&present_info, avconfig); + const auto image_to_flip_ = get_present_source(&present_info, avconfig); + image_to_flip = image_to_flip_->id(); + + if (avconfig->_3d) [[unlikely]] + { + const auto min_expected_height = rsx::apply_resolution_scale(buffer_height + 30, true); + if (image_to_flip_->height() < min_expected_height) + { + // Get image for second eye + const u32 image_offset = (buffer_height + 30) * buffer_pitch + display_buffers[info.buffer].offset; + present_info.width = buffer_width; + present_info.height = buffer_height; + present_info.address = rsx::get_address(image_offset, CELL_GCM_LOCATION_LOCAL, HERE); + + image_to_flip2 = get_present_source(&present_info, avconfig)->id(); + } + else + { + // Account for possible insets + buffer_height = std::min(image_to_flip_->height() - min_expected_height, rsx::apply_resolution_scale(buffer_height, true)); + } + } + buffer_width = present_info.width; buffer_height = present_info.height; } @@ -194,7 +216,7 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info) areai screen_area = coordi({}, { static_cast(buffer_width), static_cast(buffer_height) }); - if (g_cfg.video.full_rgb_range_output && rsx::fcmp(avconfig->gamma, 1.f)) + if (g_cfg.video.full_rgb_range_output && rsx::fcmp(avconfig->gamma, 1.f) && !avconfig->_3d) { // Blit source image to the screen m_flip_fbo.recreate(); @@ -208,9 +230,10 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info) { const f32 gamma = avconfig->gamma; const bool limited_range = !g_cfg.video.full_rgb_range_output; + const rsx::simple_array images{ image_to_flip, image_to_flip2 }; gl::screen.bind(); - m_video_output_pass.run(areau(aspect_ratio), image_to_flip, gamma, limited_range); + m_video_output_pass.run(areau(aspect_ratio), images, gamma, limited_range, avconfig->_3d); } } diff --git a/rpcs3/Emu/RSX/VK/VKOverlays.h b/rpcs3/Emu/RSX/VK/VKOverlays.h index 669172816d..8e6a592c92 100644 --- a/rpcs3/Emu/RSX/VK/VKOverlays.h +++ b/rpcs3/Emu/RSX/VK/VKOverlays.h @@ -1048,9 +1048,11 @@ namespace vk { float gamma; int limit_range; + int stereo; + int stereo_image_count; }; - float data[2]; + float data[4]; } config; @@ -1072,6 +1074,7 @@ namespace vk fs_src = "#version 420\n\n" "layout(set=0, binding=1) uniform sampler2D fs0;\n" + "layout(set=0, binding=2) uniform sampler2D fs1;\n" "layout(location=0) in vec2 tc0;\n" "layout(location=0) out vec4 ocol;\n" "\n" @@ -1079,11 +1082,34 @@ namespace vk "{\n" " float gamma;\n" " int limit_range;\n" + " int stereo;\n" + " int stereo_image_count;\n" "};\n" "\n" + "vec4 read_source()\n" + "{\n" + " if (stereo == 0) return texture(fs0, tc0);\n" + "\n" + " vec4 left, right;\n" + " if (stereo_image_count == 2)\n" + " {\n" + " left = texture(fs0, tc0);\n" + " right = texture(fs1, tc0);\n" + " }\n" + " else\n" + " {\n" + " vec2 coord_left = tc0 * vec2(1.f, 0.4898f);\n" + " vec2 coord_right = coord_left + vec2(0.f, 0.510204f);\n" + " left = texture(fs0, coord_left);\n" + " right = texture(fs0, coord_right);\n" + " }\n" + "\n" + " return vec4(left.r, right.g, right.b, 1.);\n" + "}\n" + "\n" "void main()\n" "{\n" - " vec4 color = texture(fs0, tc0);\n" + " vec4 color = read_source();\n" " color.rgb = pow(color.rgb, vec3(gamma));\n" " if (limit_range > 0)\n" " ocol = ((color * 220.) + 16.) / 255.;\n" @@ -1094,6 +1120,8 @@ namespace vk renderpass_config.set_depth_mask(false); renderpass_config.set_color_mask(0, true, true, true, true); renderpass_config.set_attachment_count(1); + + m_num_usable_samplers = 2; } std::vector get_push_constants() override @@ -1101,23 +1129,39 @@ namespace vk VkPushConstantRange constant; constant.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; constant.offset = 0; - constant.size = 8; + constant.size = 16; return { constant }; } void update_uniforms(vk::command_buffer& cmd, vk::glsl::program* /*program*/) override { - vkCmdPushConstants(cmd, m_pipeline_layout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, 8, config.data); + vkCmdPushConstants(cmd, m_pipeline_layout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, 16, config.data); } - void run(vk::command_buffer &cmd, const areau& viewport, vk::framebuffer* target, vk::viewable_image* src, - f32 gamma, bool limited_rgb, VkRenderPass render_pass) + void run(vk::command_buffer &cmd, const areau& viewport, vk::framebuffer* target, + const rsx::simple_array& src, + f32 gamma, bool limited_rgb, bool _3d, VkRenderPass render_pass) { config.gamma = gamma; config.limit_range = limited_rgb? 1 : 0; + config.stereo = _3d? 1 : 0; + config.stereo_image_count = std::min(::size32(src), 2u); - overlay_pass::run(cmd, viewport, target, { src->get_view(VK_REMAP_IDENTITY, rsx::default_remap_vector) }, render_pass); + std::vector views; + views.reserve(2); + + for (auto& img : src) + { + views.push_back(img->get_view(VK_REMAP_IDENTITY, rsx::default_remap_vector)); + } + + if (views.size() < 2) + { + views.push_back(vk::null_image_view(cmd, VK_IMAGE_VIEW_TYPE_2D)); + } + + overlay_pass::run(cmd, viewport, target, views, render_pass); } }; } diff --git a/rpcs3/Emu/RSX/VK/VKPresent.cpp b/rpcs3/Emu/RSX/VK/VKPresent.cpp index 7d98b063b5..62242ce0f1 100644 --- a/rpcs3/Emu/RSX/VK/VKPresent.cpp +++ b/rpcs3/Emu/RSX/VK/VKPresent.cpp @@ -431,7 +431,7 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info) } // Scan memory for required data. This is done early to optimize waiting for the driver image acquire below. - vk::image* image_to_flip = nullptr; + vk::image *image_to_flip = nullptr, *image_to_flip2 = nullptr; if (info.buffer < display_buffers_count && buffer_width && buffer_height) { vk::present_surface_info present_info; @@ -442,6 +442,27 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info) present_info.address = rsx::get_address(display_buffers[info.buffer].offset, CELL_GCM_LOCATION_LOCAL, HERE); image_to_flip = get_present_source(&present_info, avconfig); + + if (avconfig->_3d) [[unlikely]] + { + const auto min_expected_height = rsx::apply_resolution_scale(buffer_height + 30, true); + if (image_to_flip->height() < min_expected_height) + { + // Get image for second eye + const u32 image_offset = (buffer_height + 30) * buffer_pitch + display_buffers[info.buffer].offset; + present_info.width = buffer_width; + present_info.height = buffer_height; + present_info.address = rsx::get_address(image_offset, CELL_GCM_LOCATION_LOCAL, HERE); + + image_to_flip2 = get_present_source(&present_info, avconfig); + } + else + { + // Account for possible insets + buffer_height = std::min(image_to_flip->height() - min_expected_height, rsx::apply_resolution_scale(buffer_height, true)); + } + } + buffer_width = present_info.width; buffer_height = present_info.height; } @@ -526,7 +547,7 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info) VkRenderPass single_target_pass = VK_NULL_HANDLE; vk::framebuffer_holder* direct_fbo = nullptr; - vk::viewable_image* calibration_src = nullptr; + rsx::simple_array calibration_src; if (!image_to_flip || aspect_ratio.width < csize.width || aspect_ratio.height < csize.height) { @@ -540,13 +561,19 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info) if (image_to_flip) { - if (!g_cfg.video.full_rgb_range_output || !rsx::fcmp(avconfig->gamma, 1.f)) [[unlikely]] + if (!g_cfg.video.full_rgb_range_output || !rsx::fcmp(avconfig->gamma, 1.f) || avconfig->_3d) [[unlikely]] { - calibration_src = dynamic_cast(image_to_flip); - verify("Image handle not viewable!" HERE), calibration_src; + calibration_src.push_back(dynamic_cast(image_to_flip)); + verify("Image not viewable" HERE), calibration_src.front(); + + if (image_to_flip2) + { + calibration_src.push_back(dynamic_cast(image_to_flip2)); + verify("Image not viewable" HERE), calibration_src.back(); + } } - if (!calibration_src) [[likely]] + if (calibration_src.empty()) [[likely]] { vk::copy_scaled_image(*m_current_command_buffer, image_to_flip->value, target_image, image_to_flip->current_layout, target_layout, { 0, 0, static_cast(buffer_width), static_cast(buffer_height) }, aspect_ratio, 1, VK_IMAGE_ASPECT_COLOR_BIT, false); @@ -564,7 +591,7 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info) direct_fbo->add_ref(); image_to_flip->push_layout(*m_current_command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - m_video_output_pass->run(*m_current_command_buffer, areau(aspect_ratio), direct_fbo, calibration_src, avconfig->gamma, !g_cfg.video.full_rgb_range_output, single_target_pass); + m_video_output_pass->run(*m_current_command_buffer, areau(aspect_ratio), direct_fbo, calibration_src, avconfig->gamma, !g_cfg.video.full_rgb_range_output, avconfig->_3d, single_target_pass); image_to_flip->pop_layout(*m_current_command_buffer); direct_fbo->release();