2020-12-05 13:08:24 +01:00
# include "stdafx.h"
2020-01-17 17:24:33 +01:00
# include "GLGSRender.h"
2020-08-11 19:25:04 +02:00
# include "Emu/Cell/Modules/cellVideoOut.h"
2020-01-17 17:24:33 +01:00
2021-03-01 00:00:49 +01:00
LOG_CHANNEL ( screenshot_log , " SCREENSHOT " ) ;
2020-01-31 14:43:59 +01:00
2021-06-05 16:27:03 +02:00
namespace gl
{
namespace debug
{
std : : unique_ptr < texture > g_vis_texture ;
void set_vis_texture ( texture * visual )
{
const auto target = static_cast < GLenum > ( visual - > get_target ( ) ) ;
const auto ifmt = static_cast < GLenum > ( visual - > get_internal_format ( ) ) ;
g_vis_texture . reset ( new texture ( target , visual - > width ( ) , visual - > height ( ) , 1 , 1 , ifmt , visual - > format_class ( ) ) ) ;
glCopyImageSubData ( visual - > id ( ) , target , 0 , 0 , 0 , 0 , g_vis_texture - > id ( ) , target , 0 , 0 , 0 , 0 , visual - > width ( ) , visual - > height ( ) , 1 ) ;
}
}
}
2021-03-02 12:59:19 +01:00
gl : : texture * GLGSRender : : get_present_source ( gl : : present_surface_info * info , const rsx : : avconf & avconfig )
2020-01-17 17:24:33 +01:00
{
2020-01-07 19:29:37 +01:00
gl : : texture * image = nullptr ;
2020-01-17 21:17:14 +01:00
// Check the surface store first
gl : : command_context cmd = { gl_state } ;
2020-08-15 23:33:34 +02:00
const auto format_bpp = rsx : : get_format_block_size_in_bytes ( info - > format ) ;
2020-01-17 21:17:14 +01:00
const auto overlap_info = m_rtts . get_merged_texture_memory_region ( cmd ,
2022-03-13 09:32:04 +01:00
info - > address , info - > width , info - > height , info - > pitch , format_bpp , rsx : : surface_access : : transfer_read ) ;
2020-01-17 21:17:14 +01:00
if ( ! overlap_info . empty ( ) )
{
const auto & section = overlap_info . back ( ) ;
auto surface = gl : : as_rtt ( section . surface ) ;
2020-01-18 10:21:31 +01:00
bool viable = false ;
2020-01-17 21:17:14 +01:00
if ( section . base_address > = info - > address )
{
2022-03-05 16:42:53 +01:00
const auto surface_width = surface - > get_surface_width < rsx : : surface_metrics : : samples > ( ) ;
const auto surface_height = surface - > get_surface_height < rsx : : surface_metrics : : samples > ( ) ;
2020-01-17 21:17:14 +01:00
2020-01-18 10:21:31 +01:00
if ( section . base_address = = info - > address )
{
// Check for fit or crop
viable = ( surface_width > = info - > width & & surface_height > = info - > height ) ;
}
else
{
// Check for borders and letterboxing
const u32 inset_offset = section . base_address - info - > address ;
const u32 inset_y = inset_offset / info - > pitch ;
const u32 inset_x = ( inset_offset % info - > pitch ) / format_bpp ;
const u32 full_width = surface_width + inset_x + inset_x ;
const u32 full_height = surface_height + inset_y + inset_y ;
viable = ( full_width = = info - > width & & full_height = = info - > height ) ;
}
2020-01-17 21:17:14 +01:00
2020-01-18 10:21:31 +01:00
if ( viable )
2020-01-17 21:17:14 +01:00
{
2022-03-13 09:32:04 +01:00
image = section . surface - > get_surface ( rsx : : surface_access : : transfer_read ) ;
2020-01-17 21:17:14 +01:00
2020-11-17 21:56:33 +01:00
std : : tie ( info - > width , info - > height ) = rsx : : apply_resolution_scale < true > (
2022-01-09 21:07:18 +01:00
std : : min ( surface_width , info - > width ) ,
std : : min ( surface_height , info - > height ) ) ;
2020-01-17 21:17:14 +01:00
}
}
}
2020-03-04 20:25:31 +01:00
else if ( auto surface = m_gl_texture_cache . find_texture_from_dimensions < true > ( info - > address , info - > format ) ;
surface & & surface - > get_width ( ) > = info - > width & & surface - > get_height ( ) > = info - > height )
2020-01-17 21:17:14 +01:00
{
// 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
2020-01-07 19:29:37 +01:00
if ( const auto tex = surface - > get_raw_texture ( ) ; tex ) image = tex ;
2020-01-17 21:17:14 +01:00
}
if ( ! image )
{
2020-02-01 09:07:25 +01:00
rsx_log . warning ( " Flip texture was not found in cache. Uploading surface from CPU " ) ;
2020-01-17 21:17:14 +01:00
gl : : pixel_unpack_settings unpack_settings ;
unpack_settings . alignment ( 1 ) . row_length ( info - > pitch / 4 ) ;
2020-02-10 14:57:56 +01:00
if ( ! m_flip_tex_color | | m_flip_tex_color - > size2D ( ) ! = sizeu { info - > width , info - > height } )
2020-01-17 21:17:14 +01:00
{
m_flip_tex_color = std : : make_unique < gl : : texture > ( GL_TEXTURE_2D , info - > width , info - > height , 1 , 1 , GL_RGBA8 ) ;
}
gl : : command_context cmd { gl_state } ;
const auto range = utils : : address_range : : start_length ( info - > address , info - > pitch * info - > height ) ;
m_gl_texture_cache . invalidate_range ( cmd , range , rsx : : invalidation_cause : : read ) ;
2020-08-11 19:25:04 +02:00
gl : : texture : : format fmt ;
2021-03-02 12:59:19 +01:00
switch ( avconfig . format )
2020-08-11 19:25:04 +02:00
{
default :
2021-03-02 12:59:19 +01:00
rsx_log . error ( " Unhandled video output format 0x%x " , avconfig . format ) ;
2020-08-11 19:25:04 +02:00
[[fallthrough]] ;
case CELL_VIDEO_OUT_BUFFER_COLOR_FORMAT_X8R8G8B8 :
fmt = gl : : texture : : format : : bgra ;
break ;
case CELL_VIDEO_OUT_BUFFER_COLOR_FORMAT_X8B8G8R8 :
fmt = gl : : texture : : format : : rgba ;
break ;
}
m_flip_tex_color - > copy_from ( vm : : base ( info - > address ) , fmt , gl : : texture : : type : : uint_8_8_8_8 , unpack_settings ) ;
2020-01-07 19:29:37 +01:00
image = m_flip_tex_color . get ( ) ;
2020-01-17 21:17:14 +01:00
}
return image ;
2020-01-17 17:24:33 +01:00
}
void GLGSRender : : flip ( const rsx : : display_flip_info_t & info )
{
if ( info . skip_frame )
{
m_frame - > flip ( m_context , true ) ;
rsx : : thread : : flip ( info ) ;
return ;
}
u32 buffer_width = display_buffers [ info . buffer ] . width ;
u32 buffer_height = display_buffers [ info . buffer ] . height ;
u32 buffer_pitch = display_buffers [ info . buffer ] . pitch ;
u32 av_format ;
2021-11-10 22:18:46 +01:00
const auto & avconfig = g_fxo - > get < rsx : : avconf > ( ) ;
2020-01-17 17:24:33 +01:00
2021-12-02 14:02:08 +01:00
if ( ! buffer_width )
{
buffer_width = avconfig . resolution_x ;
buffer_height = avconfig . resolution_y ;
}
2021-03-02 12:59:19 +01:00
if ( avconfig . state )
2020-01-17 17:24:33 +01:00
{
2021-03-02 12:59:19 +01:00
av_format = avconfig . get_compatible_gcm_format ( ) ;
2020-01-17 17:24:33 +01:00
if ( ! buffer_pitch )
2021-03-02 12:59:19 +01:00
buffer_pitch = buffer_width * avconfig . get_bpp ( ) ;
2020-01-17 17:24:33 +01:00
2021-11-02 21:57:02 +01:00
const u32 video_frame_height = ( ! avconfig . _3d ? avconfig . resolution_y : ( avconfig . resolution_y - 30 ) / 2 ) ;
2021-03-02 12:59:19 +01:00
buffer_width = std : : min ( buffer_width , avconfig . resolution_x ) ;
2020-01-07 17:07:09 +01:00
buffer_height = std : : min ( buffer_height , video_frame_height ) ;
2020-01-17 17:24:33 +01:00
}
else
{
av_format = CELL_GCM_TEXTURE_A8R8G8B8 ;
if ( ! buffer_pitch )
buffer_pitch = buffer_width * 4 ;
}
// Disable scissor test (affects blit, clear, etc)
gl_state . enable ( GL_FALSE , GL_SCISSOR_TEST ) ;
2020-01-18 15:30:39 +01:00
// Enable drawing to window backbuffer
2020-01-17 17:24:33 +01:00
gl : : screen . bind ( ) ;
2020-01-07 19:29:37 +01:00
GLuint image_to_flip = GL_NONE , image_to_flip2 = GL_NONE ;
2020-01-17 20:44:59 +01:00
if ( info . buffer < display_buffers_count & & buffer_width & & buffer_height )
{
// Find the source image
2020-01-17 21:17:14 +01:00
gl : : present_surface_info present_info ;
present_info . width = buffer_width ;
present_info . height = buffer_height ;
present_info . pitch = buffer_pitch ;
present_info . format = av_format ;
2020-12-09 16:04:52 +01:00
present_info . address = rsx : : get_address ( display_buffers [ info . buffer ] . offset , CELL_GCM_LOCATION_LOCAL ) ;
2020-01-17 21:17:14 +01:00
2020-01-07 19:29:37 +01:00
const auto image_to_flip_ = get_present_source ( & present_info , avconfig ) ;
image_to_flip = image_to_flip_ - > id ( ) ;
2021-03-02 12:59:19 +01:00
if ( avconfig . _3d ) [[unlikely]]
2020-01-07 19:29:37 +01:00
{
2020-11-17 21:56:33 +01:00
const auto [ unused , min_expected_height ] = rsx : : apply_resolution_scale < true > ( RSX_SURFACE_DIMENSION_IGNORED , buffer_height + 30 ) ;
2020-01-07 19:29:37 +01:00
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 ;
2020-12-09 16:04:52 +01:00
present_info . address = rsx : : get_address ( image_offset , CELL_GCM_LOCATION_LOCAL ) ;
2020-01-07 19:29:37 +01:00
image_to_flip2 = get_present_source ( & present_info , avconfig ) - > id ( ) ;
}
else
{
// Account for possible insets
2020-11-17 21:56:33 +01:00
const auto [ unused2 , scaled_buffer_height ] = rsx : : apply_resolution_scale < true > ( RSX_SURFACE_DIMENSION_IGNORED , buffer_height ) ;
buffer_height = std : : min < u32 > ( image_to_flip_ - > height ( ) - min_expected_height , scaled_buffer_height ) ;
2020-01-07 19:29:37 +01:00
}
}
2020-01-17 21:17:14 +01:00
buffer_width = present_info . width ;
buffer_height = present_info . height ;
}
2021-05-26 00:16:24 +02:00
// Get window state
const int width = m_frame - > client_width ( ) ;
const int height = m_frame - > client_height ( ) ;
2020-01-17 21:17:14 +01:00
// Calculate blit coordinates
2021-11-09 21:06:52 +01:00
areai aspect_ratio ;
2020-01-17 17:24:33 +01:00
if ( ! g_cfg . video . stretch_to_display_area )
{
2021-11-09 21:06:52 +01:00
const sizeu csize ( width , height ) ;
const auto converted = avconfig . aspect_convert_region ( size2u { buffer_width , buffer_height } , csize ) ;
aspect_ratio = static_cast < areai > ( converted ) ;
}
else
{
aspect_ratio = { 0 , 0 , width , height } ;
2020-01-17 17:24:33 +01:00
}
2021-11-09 21:06:52 +01:00
if ( ! image_to_flip | | aspect_ratio . x1 | | aspect_ratio . y1 )
2020-01-18 15:30:39 +01:00
{
// Clear the window background to black
gl_state . clear_color ( 0 , 0 , 0 , 0 ) ;
gl : : screen . clear ( gl : : buffers : : color ) ;
}
2020-01-17 21:17:14 +01:00
if ( image_to_flip )
{
2020-12-06 14:32:56 +01:00
if ( m_frame - > screenshot_toggle )
2020-01-17 17:24:33 +01:00
{
m_frame - > screenshot_toggle = false ;
std : : vector < u8 > sshot_frame ( buffer_height * buffer_width * 4 ) ;
gl : : pixel_pack_settings pack_settings { } ;
pack_settings . apply ( ) ;
if ( gl : : get_driver_caps ( ) . ARB_dsa_supported )
2020-08-11 19:35:50 +02:00
glGetTextureImage ( image_to_flip , 0 , GL_RGBA , GL_UNSIGNED_BYTE , buffer_height * buffer_width * 4 , sshot_frame . data ( ) ) ;
2020-01-17 17:24:33 +01:00
else
2020-08-11 19:35:50 +02:00
glGetTextureImageEXT ( image_to_flip , GL_TEXTURE_2D , 0 , GL_RGBA , GL_UNSIGNED_BYTE , sshot_frame . data ( ) ) ;
2020-01-17 17:24:33 +01:00
if ( GLenum err ; ( err = glGetError ( ) ) ! = GL_NO_ERROR )
2021-03-01 00:00:49 +01:00
screenshot_log . error ( " Failed to capture image: 0x%x " , err ) ;
2020-01-17 17:24:33 +01:00
else
2020-08-11 19:35:50 +02:00
m_frame - > take_screenshot ( std : : move ( sshot_frame ) , buffer_width , buffer_height , false ) ;
2020-01-17 17:24:33 +01:00
}
2020-04-14 19:13:52 +02:00
const areai screen_area = coordi ( { } , { static_cast < int > ( buffer_width ) , static_cast < int > ( buffer_height ) } ) ;
2020-01-17 17:24:33 +01:00
2020-04-14 19:13:52 +02:00
const bool use_full_rgb_range_output = g_cfg . video . full_rgb_range_output . get ( ) ;
2021-03-02 12:59:19 +01:00
if ( use_full_rgb_range_output & & rsx : : fcmp ( avconfig . gamma , 1.f ) & & ! avconfig . _3d )
2020-01-17 17:24:33 +01:00
{
// Blit source image to the screen
m_flip_fbo . recreate ( ) ;
m_flip_fbo . bind ( ) ;
2020-01-17 20:44:59 +01:00
m_flip_fbo . color = image_to_flip ;
2020-01-17 17:24:33 +01:00
m_flip_fbo . read_buffer ( m_flip_fbo . color ) ;
m_flip_fbo . draw_buffer ( m_flip_fbo . color ) ;
2021-11-09 21:06:52 +01:00
m_flip_fbo . blit ( gl : : screen , screen_area , aspect_ratio . flipped_vertical ( ) , gl : : buffers : : color , gl : : filter : : linear ) ;
2020-01-17 17:24:33 +01:00
}
else
{
2021-03-02 12:59:19 +01:00
const f32 gamma = avconfig . gamma ;
2020-04-14 19:13:52 +02:00
const bool limited_range = ! use_full_rgb_range_output ;
2020-01-07 19:29:37 +01:00
const rsx : : simple_array < GLuint > images { image_to_flip , image_to_flip2 } ;
2020-01-17 17:24:33 +01:00
gl : : screen . bind ( ) ;
2021-03-02 12:59:19 +01:00
m_video_output_pass . run ( areau ( aspect_ratio ) , images , gamma , limited_range , avconfig . _3d ) ;
2020-01-17 17:24:33 +01:00
}
}
if ( m_overlay_manager )
{
if ( m_overlay_manager - > has_dirty ( ) )
{
m_overlay_manager - > lock ( ) ;
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 ( ) ;
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 ( areau ( aspect_ratio ) , 0 , * view . get ( ) ) ;
}
}
}
2020-12-14 18:26:20 +01:00
if ( g_cfg . video . overlay & & gl : : get_driver_caps ( ) . ARB_shader_draw_parameters_supported )
2020-01-17 17:24:33 +01:00
{
2020-12-14 18:26:20 +01:00
if ( ! m_text_printer . is_enabled ( ) )
{
m_text_printer . init ( ) ;
m_text_printer . set_enabled ( true ) ;
}
2020-11-23 17:19:14 +01:00
// Disable depth test
gl_state . depth_func ( GL_ALWAYS ) ;
2020-01-17 17:24:33 +01:00
gl : : screen . bind ( ) ;
2021-05-26 00:16:24 +02:00
glViewport ( 0 , 0 , width , height ) ;
m_text_printer . set_scale ( m_frame - > client_device_pixel_ratio ( ) ) ;
2020-01-17 17:24:33 +01:00
2021-05-26 00:16:24 +02:00
m_text_printer . print_text ( 4 , 0 , width , height , fmt : : format ( " RSX Load: %3d%% " , get_load ( ) ) ) ;
m_text_printer . print_text ( 4 , 18 , width , height , fmt : : format ( " draw calls: %16d " , info . stats . draw_calls ) ) ;
m_text_printer . print_text ( 4 , 36 , width , height , fmt : : format ( " draw call setup: %11dus " , info . stats . setup_time ) ) ;
m_text_printer . print_text ( 4 , 54 , width , height , fmt : : format ( " vertex upload time: %8dus " , info . stats . vertex_upload_time ) ) ;
m_text_printer . print_text ( 4 , 72 , width , height , fmt : : format ( " textures upload time: %6dus " , info . stats . textures_upload_time ) ) ;
m_text_printer . print_text ( 4 , 90 , width , height , fmt : : format ( " draw call execution: %7dus " , info . stats . draw_exec_time ) ) ;
2020-01-17 17:24:33 +01:00
const auto num_dirty_textures = m_gl_texture_cache . get_unreleased_textures_count ( ) ;
const auto texture_memory_size = m_gl_texture_cache . get_texture_memory_in_use ( ) / ( 1024 * 1024 ) ;
const auto num_flushes = m_gl_texture_cache . get_num_flush_requests ( ) ;
const auto num_mispredict = m_gl_texture_cache . get_num_cache_mispredictions ( ) ;
const auto num_speculate = m_gl_texture_cache . get_num_cache_speculative_writes ( ) ;
const auto num_misses = m_gl_texture_cache . get_num_cache_misses ( ) ;
const auto num_unavoidable = m_gl_texture_cache . get_num_unavoidable_hard_faults ( ) ;
const auto cache_miss_ratio = static_cast < u32 > ( ceil ( m_gl_texture_cache . get_cache_miss_ratio ( ) * 100 ) ) ;
2020-12-14 19:29:07 +01:00
const auto num_texture_upload = m_gl_texture_cache . get_texture_upload_calls_this_frame ( ) ;
const auto num_texture_upload_miss = m_gl_texture_cache . get_texture_upload_misses_this_frame ( ) ;
const auto texture_upload_miss_ratio = m_gl_texture_cache . get_texture_upload_miss_percentage ( ) ;
2021-05-26 00:16:24 +02:00
m_text_printer . print_text ( 4 , 126 , width , height , fmt : : format ( " Unreleased textures: %7d " , num_dirty_textures ) ) ;
m_text_printer . print_text ( 4 , 144 , width , height , fmt : : format ( " Texture memory: %12dM " , texture_memory_size ) ) ;
m_text_printer . print_text ( 4 , 162 , width , height , fmt : : format ( " Flush requests: %12d = %2d (%3d%%) hard faults, %2d unavoidable, %2d misprediction(s), %2d speculation(s) " , num_flushes , num_misses , cache_miss_ratio , num_unavoidable , num_mispredict , num_speculate ) ) ;
m_text_printer . print_text ( 4 , 180 , width , height , fmt : : format ( " Texture uploads: %15u (%u from CPU - %02u%%) " , num_texture_upload , num_texture_upload_miss , texture_upload_miss_ratio ) ) ;
2020-01-17 17:24:33 +01:00
}
2021-06-05 16:27:03 +02:00
if ( gl : : debug : : g_vis_texture )
{
// Optionally renders a single debug texture to framebuffer.
// Only programmatic access provided at the moment.
// TODO: Migrate to use overlay system. (kd-11)
gl : : fbo m_vis_buffer ;
m_vis_buffer . create ( ) ;
m_vis_buffer . bind ( ) ;
m_vis_buffer . color = gl : : debug : : g_vis_texture - > id ( ) ;
m_vis_buffer . read_buffer ( m_vis_buffer . color ) ;
m_vis_buffer . draw_buffer ( m_vis_buffer . color ) ;
const u32 vis_width = 320 ;
const u32 vis_height = 240 ;
areai display_view = areai ( aspect_ratio ) . flipped_vertical ( ) ;
display_view . x1 = display_view . x2 - vis_width ;
display_view . y1 = vis_height ;
// Blit
const auto src_region = areau { 0u , 0u , gl : : debug : : g_vis_texture - > width ( ) , gl : : debug : : g_vis_texture - > height ( ) } ;
m_vis_buffer . blit ( gl : : screen , static_cast < areai > ( src_region ) , display_view , gl : : buffers : : color , gl : : filter : : linear ) ;
m_vis_buffer . remove ( ) ;
}
2020-01-17 17:24:33 +01:00
m_frame - > flip ( m_context ) ;
rsx : : thread : : flip ( info ) ;
// Cleanup
m_gl_texture_cache . on_frame_end ( ) ;
m_vertex_cache - > purge ( ) ;
2020-06-01 20:11:33 +02:00
gl : : command_context cmd { gl_state } ;
auto removed_textures = m_rtts . free_invalidated ( cmd ) ;
2020-01-17 17:24:33 +01:00
m_framebuffer_cache . remove_if ( [ & ] ( auto & fbo )
{
if ( fbo . unused_check_count ( ) > = 2 ) return true ; // Remove if stale
if ( fbo . references_any ( removed_textures ) ) return true ; // Remove if any of the attachments is invalid
return false ;
} ) ;
if ( m_draw_fbo & & ! m_rtts_dirty )
{
// Always restore the active framebuffer
m_draw_fbo - > bind ( ) ;
set_viewport ( ) ;
set_scissor ( ! ! ( m_graphics_state & rsx : : pipeline_state : : scissor_setup_clipped ) ) ;
}
}