2016-02-15 10:50:14 +01:00
# pragma once
# include "stdafx.h"
# include <exception>
# include <string>
# include <functional>
# include <vector>
# include <memory>
2017-02-28 09:13:50 +01:00
# include <thread>
# include <condition_variable>
2017-03-29 21:27:29 +02:00
# include <chrono>
2016-02-15 10:50:14 +01:00
2017-07-27 15:29:08 +02:00
# include "Utilities/mutex.h"
2017-06-22 20:25:58 +02:00
# include "Emu/System.h"
2016-09-18 07:19:26 +02:00
# include "GLRenderTargets.h"
2016-02-15 10:50:14 +01:00
# include "../Common/TextureUtils.h"
2017-09-08 16:52:13 +02:00
# include "../Common/texture_cache.h"
2017-03-29 21:27:29 +02:00
# include "../../Memory/vm.h"
2017-04-23 11:32:37 +02:00
# include "../rsx_utils.h"
2017-02-16 19:29:56 +01:00
class GLGSRender ;
2016-02-15 10:50:14 +01:00
2017-02-13 15:22:25 +01:00
namespace gl
{
2017-09-28 20:32:00 +02:00
class blitter ;
2017-09-08 16:52:13 +02:00
extern GLenum get_sized_internal_format ( u32 ) ;
2017-09-28 20:32:00 +02:00
extern blitter * g_hw_blitter ;
class blitter
{
fbo blit_src ;
fbo blit_dst ;
public :
void init ( )
{
blit_src . create ( ) ;
blit_dst . create ( ) ;
}
void destroy ( )
{
blit_dst . remove ( ) ;
blit_src . remove ( ) ;
}
u32 scale_image ( u32 src , u32 dst , const areai src_rect , const areai dst_rect , bool linear_interpolation , bool is_depth_copy )
{
s32 old_fbo = 0 ;
glGetIntegerv ( GL_FRAMEBUFFER_BINDING , & old_fbo ) ;
u32 dst_tex = dst ;
filter interp = linear_interpolation ? filter : : linear : filter : : nearest ;
GLenum attachment = is_depth_copy ? GL_DEPTH_ATTACHMENT : GL_COLOR_ATTACHMENT0 ;
blit_src . bind ( ) ;
glFramebufferTexture2D ( GL_FRAMEBUFFER , attachment , GL_TEXTURE_2D , src , 0 ) ;
blit_src . check ( ) ;
blit_dst . bind ( ) ;
glFramebufferTexture2D ( GL_FRAMEBUFFER , attachment , GL_TEXTURE_2D , dst_tex , 0 ) ;
blit_dst . check ( ) ;
GLboolean scissor_test_enabled = glIsEnabled ( GL_SCISSOR_TEST ) ;
if ( scissor_test_enabled )
glDisable ( GL_SCISSOR_TEST ) ;
blit_src . blit ( blit_dst , src_rect , dst_rect , is_depth_copy ? buffers : : depth : buffers : : color , interp ) ;
2017-10-02 15:53:27 +02:00
blit_src . bind ( ) ;
glFramebufferTexture2D ( GL_FRAMEBUFFER , attachment , GL_TEXTURE_2D , GL_NONE , 0 ) ;
blit_dst . bind ( ) ;
glFramebufferTexture2D ( GL_FRAMEBUFFER , attachment , GL_TEXTURE_2D , GL_NONE , 0 ) ;
2017-09-28 20:32:00 +02:00
if ( scissor_test_enabled )
glEnable ( GL_SCISSOR_TEST ) ;
glBindFramebuffer ( GL_FRAMEBUFFER , old_fbo ) ;
return dst_tex ;
}
} ;
2017-09-08 16:52:13 +02:00
2017-09-18 19:22:34 +02:00
class cached_texture_section : public rsx : : cached_texture_section
2017-02-13 15:22:25 +01:00
{
2017-09-08 16:52:13 +02:00
private :
fence m_fence ;
u32 pbo_id = 0 ;
u32 pbo_size = 0 ;
2017-02-13 15:22:25 +01:00
2017-09-08 16:52:13 +02:00
u32 vram_texture = 0 ;
2017-09-28 20:32:00 +02:00
u32 scaled_texture = 0 ;
2017-02-16 19:29:56 +01:00
2017-09-08 16:52:13 +02:00
bool copied = false ;
bool flushed = false ;
bool is_depth = false ;
2016-02-15 10:50:14 +01:00
2017-09-08 16:52:13 +02:00
texture : : format format = texture : : format : : rgba ;
texture : : type type = texture : : type : : ubyte ;
bool pack_unpack_swap_bytes = false ;
2017-02-13 15:22:25 +01:00
2017-09-08 16:52:13 +02:00
u8 get_pixel_size ( texture : : format fmt_ , texture : : type type_ )
{
u8 size = 1 ;
switch ( type_ )
{
case texture : : type : : ubyte :
case texture : : type : : sbyte :
break ;
case texture : : type : : ushort :
case texture : : type : : sshort :
case texture : : type : : f16 :
size = 2 ;
break ;
case texture : : type : : ushort_5_6_5 :
case texture : : type : : ushort_5_6_5_rev :
case texture : : type : : ushort_4_4_4_4 :
case texture : : type : : ushort_4_4_4_4_rev :
case texture : : type : : ushort_5_5_5_1 :
case texture : : type : : ushort_1_5_5_5_rev :
return 2 ;
case texture : : type : : uint_8_8_8_8 :
case texture : : type : : uint_8_8_8_8_rev :
case texture : : type : : uint_10_10_10_2 :
case texture : : type : : uint_2_10_10_10_rev :
case texture : : type : : uint_24_8 :
return 4 ;
case texture : : type : : f32 :
case texture : : type : : sint :
case texture : : type : : uint :
size = 4 ;
break ;
2017-11-02 07:29:17 +01:00
default :
LOG_ERROR ( RSX , " Unsupported texture type " ) ;
2017-09-08 16:52:13 +02:00
}
switch ( fmt_ )
{
case texture : : format : : red :
case texture : : format : : r :
break ;
case texture : : format : : rg :
size * = 2 ;
break ;
case texture : : format : : rgb :
case texture : : format : : bgr :
size * = 3 ;
break ;
case texture : : format : : rgba :
case texture : : format : : bgra :
size * = 4 ;
break ;
//Depth formats..
case texture : : format : : depth :
size = 2 ;
break ;
case texture : : format : : depth_stencil :
size = 4 ;
break ;
default :
LOG_ERROR ( RSX , " Unsupported rtt format %d " , ( GLenum ) fmt_ ) ;
size = 4 ;
}
return size ;
}
2017-02-13 15:22:25 +01:00
2017-09-08 16:52:13 +02:00
void init_buffer ( )
{
if ( pbo_id )
{
glDeleteBuffers ( 1 , & pbo_id ) ;
pbo_id = 0 ;
pbo_size = 0 ;
2016-02-15 10:50:14 +01:00
}
2017-09-08 16:52:13 +02:00
glGenBuffers ( 1 , & pbo_id ) ;
2017-02-27 20:39:22 +01:00
2017-10-03 13:38:00 +02:00
const f32 resolution_scale = rsx : : get_resolution_scale ( ) ;
const u32 real_buffer_size = ( resolution_scale < 1.f ) ? cpu_address_range : ( u32 ) ( resolution_scale * resolution_scale * cpu_address_range ) ;
2017-10-02 15:53:27 +02:00
const u32 buffer_size = align ( real_buffer_size , 4096 ) ;
2017-10-03 13:38:00 +02:00
2017-09-08 16:52:13 +02:00
glBindBuffer ( GL_PIXEL_PACK_BUFFER , pbo_id ) ;
glBufferStorage ( GL_PIXEL_PACK_BUFFER , buffer_size , nullptr , GL_MAP_READ_BIT ) ;
2017-02-27 20:39:22 +01:00
2017-09-08 16:52:13 +02:00
pbo_size = buffer_size ;
}
2017-02-27 20:39:22 +01:00
2017-09-08 16:52:13 +02:00
public :
2017-02-27 20:39:22 +01:00
2017-09-08 16:52:13 +02:00
void reset ( const u32 base , const u32 size , const bool flushable = false )
{
2017-10-04 19:54:13 +02:00
rsx : : protection_policy policy = g_cfg . video . strict_rendering_mode ? rsx : : protection_policy : : protect_policy_full_range : rsx : : protection_policy : : protect_policy_conservative ;
2017-09-08 16:52:13 +02:00
rsx : : buffered_section : : reset ( base , size , policy ) ;
2016-02-15 10:50:14 +01:00
2017-09-08 16:52:13 +02:00
if ( flushable )
init_buffer ( ) ;
flushed = false ;
copied = false ;
is_depth = false ;
2017-02-16 19:29:56 +01:00
2017-09-08 16:52:13 +02:00
vram_texture = 0 ;
}
2017-09-22 15:12:10 +02:00
void create ( const u16 w , const u16 h , const u16 depth , const u16 mipmaps , void * ,
2017-09-18 19:22:34 +02:00
gl : : texture * image , const u32 rsx_pitch , bool read_only ,
2017-09-08 16:52:13 +02:00
gl : : texture : : format gl_format , gl : : texture : : type gl_type , bool swap_bytes )
{
if ( ! read_only & & pbo_id = = 0 )
init_buffer ( ) ;
2017-03-29 21:27:29 +02:00
2017-09-08 16:52:13 +02:00
flushed = false ;
copied = false ;
is_depth = false ;
2017-03-29 21:27:29 +02:00
2017-09-18 19:22:34 +02:00
this - > width = w ;
this - > height = h ;
this - > rsx_pitch = rsx_pitch ;
2017-09-22 15:12:10 +02:00
this - > depth = depth ;
this - > mipmaps = mipmaps ;
2017-03-29 21:27:29 +02:00
2017-09-08 16:52:13 +02:00
vram_texture = image - > id ( ) ;
set_format ( gl_format , gl_type , swap_bytes ) ;
}
2017-03-29 21:27:29 +02:00
2017-09-22 15:12:10 +02:00
void create_read_only ( const u32 id , const u32 width , const u32 height , const u32 depth , const u32 mipmaps )
2017-09-08 16:52:13 +02:00
{
//Only to be used for ro memory, we dont care about most members, just dimensions and the vram texture handle
2017-09-18 19:22:34 +02:00
this - > width = width ;
this - > height = height ;
2017-09-22 15:12:10 +02:00
this - > depth = depth ;
this - > mipmaps = mipmaps ;
2017-09-08 16:52:13 +02:00
vram_texture = id ;
2017-03-29 21:27:29 +02:00
2017-09-18 19:22:34 +02:00
rsx_pitch = 0 ;
2017-09-08 16:52:13 +02:00
real_pitch = 0 ;
}
2017-03-29 21:27:29 +02:00
2017-10-12 14:48:31 +02:00
void set_dimensions ( u32 width , u32 height , u32 /*depth*/ , u32 pitch )
2017-09-08 16:52:13 +02:00
{
2017-09-18 19:22:34 +02:00
this - > width = width ;
this - > height = height ;
rsx_pitch = pitch ;
2017-09-08 16:52:13 +02:00
real_pitch = width * get_pixel_size ( format , type ) ;
}
2017-09-04 12:05:02 +02:00
2017-09-08 16:52:13 +02:00
void set_format ( const texture : : format gl_format , const texture : : type gl_type , const bool swap_bytes )
{
format = gl_format ;
type = gl_type ;
pack_unpack_swap_bytes = swap_bytes ;
2017-09-18 19:22:34 +02:00
real_pitch = width * get_pixel_size ( format , type ) ;
2017-09-08 16:52:13 +02:00
}
void set_depth_flag ( bool is_depth_fmt )
{
is_depth = is_depth_fmt ;
}
void set_source ( gl : : texture & source )
{
vram_texture = source . id ( ) ;
}
2017-02-16 19:29:56 +01:00
2017-09-08 16:52:13 +02:00
void copy_texture ( bool = false )
{
if ( ! glIsTexture ( vram_texture ) )
2017-02-16 19:29:56 +01:00
{
2017-09-08 16:52:13 +02:00
LOG_ERROR ( RSX , " Attempted to download rtt texture, but texture handle was invalid! (0x%X) " , vram_texture ) ;
return ;
}
2017-02-16 19:29:56 +01:00
2017-10-02 15:53:27 +02:00
u32 target_texture = vram_texture ;
2017-10-03 13:38:00 +02:00
if ( real_pitch ! = rsx_pitch | | rsx : : get_resolution_scale_percent ( ) ! = 100 )
2017-09-28 20:32:00 +02:00
{
2017-10-02 15:53:27 +02:00
//Disabled - doesnt work properly yet
2017-09-28 20:32:00 +02:00
const u32 real_width = ( rsx_pitch * width ) / real_pitch ;
2017-10-02 15:53:27 +02:00
const u32 real_height = cpu_address_range / rsx_pitch ;
2017-09-28 20:32:00 +02:00
areai src_area = { 0 , 0 , 0 , 0 } ;
2017-10-02 15:53:27 +02:00
const areai dst_area = { 0 , 0 , ( s32 ) real_width , ( s32 ) real_height } ;
2017-09-28 20:32:00 +02:00
2017-10-02 15:53:27 +02:00
GLenum ifmt = 0 ;
2017-09-28 20:32:00 +02:00
glBindTexture ( GL_TEXTURE_2D , vram_texture ) ;
2017-10-02 15:53:27 +02:00
glGetTexLevelParameteriv ( GL_TEXTURE_2D , 0 , GL_TEXTURE_INTERNAL_FORMAT , ( GLint * ) & ifmt ) ;
2017-09-28 20:32:00 +02:00
glGetTexLevelParameteriv ( GL_TEXTURE_2D , 0 , GL_TEXTURE_WIDTH , & src_area . x2 ) ;
glGetTexLevelParameteriv ( GL_TEXTURE_2D , 0 , GL_TEXTURE_HEIGHT , & src_area . y2 ) ;
2017-10-02 15:53:27 +02:00
if ( src_area . x2 ! = dst_area . x2 | | src_area . y2 ! = dst_area . y2 )
{
if ( scaled_texture ! = 0 )
{
int sw , sh , fmt ;
glBindTexture ( GL_TEXTURE_2D , scaled_texture ) ;
glGetTexLevelParameteriv ( GL_TEXTURE_2D , 0 , GL_TEXTURE_WIDTH , & sw ) ;
glGetTexLevelParameteriv ( GL_TEXTURE_2D , 0 , GL_TEXTURE_HEIGHT , & sh ) ;
glGetTexLevelParameteriv ( GL_TEXTURE_2D , 0 , GL_TEXTURE_INTERNAL_FORMAT , & fmt ) ;
if ( ( u32 ) sw ! = real_width | | ( u32 ) sh ! = real_height | | ( GLenum ) fmt ! = ifmt )
{
glDeleteTextures ( 1 , & scaled_texture ) ;
scaled_texture = 0 ;
}
}
if ( scaled_texture = = 0 )
{
glGenTextures ( 1 , & scaled_texture ) ;
glBindTexture ( GL_TEXTURE_2D , scaled_texture ) ;
glTexStorage2D ( GL_TEXTURE_2D , 1 , ifmt , real_width , real_height ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_LINEAR ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_LINEAR ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_BASE_LEVEL , 0 ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAX_LEVEL , 0 ) ;
}
bool linear_interp = false ; //TODO: Make optional or detect full sized sources
g_hw_blitter - > scale_image ( vram_texture , scaled_texture , src_area , dst_area , linear_interp , is_depth ) ;
target_texture = scaled_texture ;
}
2017-09-28 20:32:00 +02:00
}
2017-09-08 16:52:13 +02:00
glPixelStorei ( GL_PACK_SWAP_BYTES , pack_unpack_swap_bytes ) ;
2017-10-02 15:53:27 +02:00
glPixelStorei ( GL_PACK_ALIGNMENT , 1 ) ;
2017-09-08 16:52:13 +02:00
glBindBuffer ( GL_PIXEL_PACK_BUFFER , pbo_id ) ;
2017-04-04 18:14:36 +02:00
2017-10-03 13:38:00 +02:00
glGetError ( ) ;
2017-09-08 16:52:13 +02:00
if ( get_driver_caps ( ) . EXT_dsa_supported )
2017-09-28 20:32:00 +02:00
glGetTextureImageEXT ( target_texture , GL_TEXTURE_2D , 0 , ( GLenum ) format , ( GLenum ) type , nullptr ) ;
2017-09-08 16:52:13 +02:00
else
2017-09-28 20:32:00 +02:00
glGetTextureImage ( target_texture , 0 , ( GLenum ) format , ( GLenum ) type , pbo_size , nullptr ) ;
2017-04-04 18:14:36 +02:00
2017-10-03 13:38:00 +02:00
if ( GLenum err = glGetError ( ) )
{
bool recovered = false ;
if ( target_texture = = scaled_texture )
{
if ( get_driver_caps ( ) . EXT_dsa_supported )
glGetTextureImageEXT ( vram_texture , GL_TEXTURE_2D , 0 , ( GLenum ) format , ( GLenum ) type , nullptr ) ;
else
glGetTextureImage ( vram_texture , 0 , ( GLenum ) format , ( GLenum ) type , pbo_size , nullptr ) ;
if ( ! glGetError ( ) )
{
recovered = true ;
const u32 min_dimension = cpu_address_range / rsx_pitch ;
LOG_WARNING ( RSX , " Failed to read back a scaled image, but the original texture can be read back. Consider setting min scalable dimension below or equal to %d " , min_dimension ) ;
}
}
if ( ! recovered & & rsx : : get_resolution_scale_percent ( ) ! = 100 )
{
LOG_ERROR ( RSX , " Texture readback failed. Disable resolution scaling to get the 'Write Color Buffers' option to work properly " ) ;
}
}
2017-09-08 16:52:13 +02:00
glBindBuffer ( GL_PIXEL_PACK_BUFFER , 0 ) ;
2017-02-13 15:22:25 +01:00
2017-09-08 16:52:13 +02:00
m_fence . reset ( ) ;
copied = true ;
}
2017-02-13 15:22:25 +01:00
2017-09-08 16:52:13 +02:00
void fill_texture ( gl : : texture * tex )
{
if ( ! copied )
2016-02-15 10:50:14 +01:00
{
2017-09-08 16:52:13 +02:00
//LOG_WARNING(RSX, "Request to fill texture rejected because contents were not read");
return ;
}
2017-02-16 19:29:56 +01:00
2017-09-18 19:22:34 +02:00
u32 min_width = std : : min ( ( u16 ) tex - > width ( ) , width ) ;
u32 min_height = std : : min ( ( u16 ) tex - > height ( ) , height ) ;
2016-02-15 10:50:14 +01:00
2017-09-08 16:52:13 +02:00
tex - > bind ( ) ;
glPixelStorei ( GL_UNPACK_SWAP_BYTES , pack_unpack_swap_bytes ) ;
glBindBuffer ( GL_PIXEL_UNPACK_BUFFER , pbo_id ) ;
glTexSubImage2D ( ( GLenum ) tex - > get_target ( ) , 0 , 0 , 0 , min_width , min_height , ( GLenum ) format , ( GLenum ) type , nullptr ) ;
glBindBuffer ( GL_PIXEL_UNPACK_BUFFER , 0 ) ;
}
2017-02-13 15:22:25 +01:00
2017-09-08 16:52:13 +02:00
bool flush ( )
{
2017-10-27 15:52:27 +02:00
if ( flushed ) return true ; //Already written, ignore
2017-09-08 16:52:13 +02:00
if ( ! copied )
2017-02-13 15:22:25 +01:00
{
2017-09-08 16:52:13 +02:00
LOG_WARNING ( RSX , " Cache miss at address 0x%X. This is gonna hurt... " , cpu_address_base ) ;
copy_texture ( ) ;
2017-02-16 19:29:56 +01:00
if ( ! copied )
2016-02-15 10:50:14 +01:00
{
2017-09-08 16:52:13 +02:00
LOG_WARNING ( RSX , " Nothing to copy; Setting section to readable and moving on... " ) ;
protect ( utils : : protection : : ro ) ;
return false ;
2017-02-13 15:22:25 +01:00
}
2017-09-08 16:52:13 +02:00
}
2017-02-13 15:22:25 +01:00
2017-09-08 16:52:13 +02:00
m_fence . wait_for_signal ( ) ;
flushed = true ;
2017-02-13 15:22:25 +01:00
2017-09-08 16:52:13 +02:00
glBindBuffer ( GL_PIXEL_PACK_BUFFER , pbo_id ) ;
void * data = glMapBufferRange ( GL_PIXEL_PACK_BUFFER , 0 , pbo_size , GL_MAP_READ_BIT ) ;
u8 * dst = vm : : ps3 : : _ptr < u8 > ( cpu_address_base ) ;
2016-02-15 10:50:14 +01:00
2017-09-08 16:52:13 +02:00
//throw if map failed since we'll segfault anyway
verify ( HERE ) , data ! = nullptr ;
2016-02-15 10:50:14 +01:00
2017-09-28 20:32:00 +02:00
if ( real_pitch > = rsx_pitch | | scaled_texture ! = 0 )
2017-09-08 16:52:13 +02:00
{
memcpy ( dst , data , cpu_address_range ) ;
2016-02-15 10:50:14 +01:00
}
2017-09-08 16:52:13 +02:00
else
2016-02-15 10:50:14 +01:00
{
2017-09-08 16:52:13 +02:00
//TODO: Use compression hint from the gcm tile information
//TODO: Fall back to bilinear filtering if samples > 2
2017-08-07 23:54:40 +02:00
2017-09-08 16:52:13 +02:00
const u8 pixel_size = get_pixel_size ( format , type ) ;
2017-09-18 19:22:34 +02:00
const u8 samples = rsx_pitch / real_pitch ;
rsx : : scale_image_nearest ( dst , const_cast < const void * > ( data ) , width , height , rsx_pitch , real_pitch , pixel_size , samples ) ;
2017-09-08 16:52:13 +02:00
}
2016-02-15 10:50:14 +01:00
2017-11-09 17:47:38 +01:00
/* switch (gcm_format)
{
case CELL_GCM_TEXTURE_W16_Z16_Y16_X16_FLOAT :
rsx : : shuffle_texel_data_wzyx < u16 > ( dst , rsx_pitch , width , height ) ;
break ;
case CELL_GCM_TEXTURE_W32_Z32_Y32_X32_FLOAT :
rsx : : shuffle_texel_data_wzyx < u32 > ( dst , rsx_pitch , width , height ) ;
break ;
} */
2017-09-08 16:52:13 +02:00
glUnmapBuffer ( GL_PIXEL_PACK_BUFFER ) ;
glBindBuffer ( GL_PIXEL_PACK_BUFFER , 0 ) ;
return true ;
}
2016-02-15 10:50:14 +01:00
2017-09-08 16:52:13 +02:00
void destroy ( )
{
if ( ! locked & & pbo_id = = 0 & & vram_texture = = 0 & & m_fence . is_empty ( ) )
//Already destroyed
return ;
2017-02-13 15:22:25 +01:00
2017-09-08 16:52:13 +02:00
if ( locked )
unprotect ( ) ;
2017-02-13 15:22:25 +01:00
2017-09-08 16:52:13 +02:00
if ( pbo_id = = 0 )
2017-02-13 15:22:25 +01:00
{
2017-09-08 16:52:13 +02:00
//Read-only texture, destroy texture memory
glDeleteTextures ( 1 , & vram_texture ) ;
2016-02-15 10:50:14 +01:00
}
2017-09-08 16:52:13 +02:00
else
2017-02-13 15:22:25 +01:00
{
2017-09-08 16:52:13 +02:00
//Destroy pbo cache since vram texture is managed elsewhere
glDeleteBuffers ( 1 , & pbo_id ) ;
2017-09-28 20:32:00 +02:00
if ( scaled_texture )
glDeleteTextures ( 1 , & scaled_texture ) ;
2017-02-13 15:22:25 +01:00
}
2017-03-29 21:27:29 +02:00
2017-10-23 14:39:24 +02:00
vram_texture = 0 ;
scaled_texture = 0 ;
pbo_id = 0 ;
pbo_size = 0 ;
2017-09-21 15:24:42 +02:00
if ( ! m_fence . is_empty ( ) )
m_fence . destroy ( ) ;
2017-09-08 16:52:13 +02:00
}
texture : : format get_format ( ) const
{
return format ;
}
bool exists ( ) const
{
return vram_texture ! = 0 ;
}
bool is_flushable ( ) const
{
2017-09-15 00:32:23 +02:00
return ( locked & & pbo_id ! = 0 ) ;
2017-09-08 16:52:13 +02:00
}
2017-03-29 21:27:29 +02:00
2017-09-08 16:52:13 +02:00
bool is_flushed ( ) const
{
return flushed ;
}
2017-03-29 21:27:29 +02:00
2017-09-08 16:52:13 +02:00
bool is_synchronized ( ) const
{
return copied ;
}
2017-09-04 12:05:02 +02:00
2017-09-08 16:52:13 +02:00
void set_flushed ( const bool state )
{
flushed = state ;
}
bool is_empty ( ) const
{
return vram_texture = = 0 ;
}
u32 get_raw_view ( ) const
{
return vram_texture ;
}
u32 get_raw_texture ( ) const
{
return vram_texture ;
}
bool is_depth_texture ( ) const
{
return is_depth ;
}
bool has_compatible_format ( gl : : texture * tex ) const
{
GLenum fmt ;
glBindTexture ( GL_TEXTURE_2D , vram_texture ) ;
glGetTexLevelParameteriv ( GL_TEXTURE_2D , 0 , GL_TEXTURE_INTERNAL_FORMAT , ( GLint * ) & fmt ) ;
if ( auto as_rtt = dynamic_cast < gl : : render_target * > ( tex ) )
2017-09-04 12:05:02 +02:00
{
2017-09-08 16:52:13 +02:00
return ( GLenum ) as_rtt - > get_compatible_internal_format ( ) = = fmt ;
2017-09-04 12:05:02 +02:00
}
2017-03-29 21:27:29 +02:00
2017-09-08 16:52:13 +02:00
return ( gl : : texture : : format ) fmt = = tex - > get_internal_format ( ) ;
}
} ;
class texture_cache : public rsx : : texture_cache < void * , cached_texture_section , u32 , u32 , gl : : texture , gl : : texture : : format >
{
2017-02-13 15:22:25 +01:00
private :
2017-03-29 21:27:29 +02:00
blitter m_hw_blitter ;
2017-09-08 16:52:13 +02:00
std : : vector < u32 > m_temporary_surfaces ;
2017-02-16 19:29:56 +01:00
2017-09-22 15:12:10 +02:00
cached_texture_section & create_texture ( u32 id , u32 texaddr , u32 texsize , u32 w , u32 h , u32 depth , u32 mipmaps )
2016-02-15 10:50:14 +01:00
{
2017-09-22 15:12:10 +02:00
cached_texture_section & tex = find_cached_texture ( texaddr , texsize , true , w , h , depth ) ;
2017-03-29 21:27:29 +02:00
tex . reset ( texaddr , texsize , false ) ;
2017-09-22 15:12:10 +02:00
tex . create_read_only ( id , w , h , depth , mipmaps ) ;
2017-03-29 21:27:29 +02:00
read_only_range = tex . get_min_max ( read_only_range ) ;
2017-09-08 16:52:13 +02:00
return tex ;
2016-02-15 10:50:14 +01:00
}
2017-02-13 15:22:25 +01:00
void clear ( )
2016-02-15 10:50:14 +01:00
{
2017-09-08 16:52:13 +02:00
for ( auto & address_range : m_cache )
2016-02-15 10:50:14 +01:00
{
2017-09-08 16:52:13 +02:00
auto & range_data = address_range . second ;
for ( auto & tex : range_data . data )
{
tex . destroy ( ) ;
}
2016-02-15 10:50:14 +01:00
2017-09-08 16:52:13 +02:00
range_data . data . resize ( 0 ) ;
2016-02-15 10:50:14 +01:00
}
2017-09-08 16:52:13 +02:00
clear_temporary_subresources ( ) ;
m_unreleased_texture_objects = 0 ;
2016-02-15 10:50:14 +01:00
}
2017-09-08 16:52:13 +02:00
void clear_temporary_subresources ( )
2016-02-15 10:50:14 +01:00
{
2017-09-08 16:52:13 +02:00
for ( u32 & id : m_temporary_surfaces )
2017-02-13 15:22:25 +01:00
{
2017-09-08 16:52:13 +02:00
glDeleteTextures ( 1 , & id ) ;
2016-02-15 10:50:14 +01:00
}
2017-02-13 15:22:25 +01:00
2017-09-08 16:52:13 +02:00
m_temporary_surfaces . resize ( 0 ) ;
2017-02-13 15:22:25 +01:00
}
2017-11-02 16:54:57 +01:00
u32 create_temporary_subresource_impl ( u32 src_id , GLenum sized_internal_fmt , const GLenum dst_type , u16 x , u16 y , u16 width , u16 height )
2017-02-16 19:29:56 +01:00
{
u32 dst_id = 0 ;
2017-09-19 14:46:16 +02:00
GLenum ifmt ;
glBindTexture ( GL_TEXTURE_2D , src_id ) ;
glGetTexLevelParameteriv ( GL_TEXTURE_2D , 0 , GL_TEXTURE_INTERNAL_FORMAT , ( GLint * ) & ifmt ) ;
switch ( ifmt )
{
case GL_DEPTH_COMPONENT16 :
case GL_DEPTH_COMPONENT24 :
case GL_DEPTH24_STENCIL8 :
sized_internal_fmt = ifmt ;
break ;
}
2017-02-16 19:29:56 +01:00
glGenTextures ( 1 , & dst_id ) ;
2017-11-02 16:54:57 +01:00
glBindTexture ( dst_type , dst_id ) ;
2017-02-16 19:29:56 +01:00
2017-11-02 16:54:57 +01:00
if ( dst_type = = GL_TEXTURE_2D )
glTexStorage2D ( GL_TEXTURE_2D , 1 , sized_internal_fmt , width , height ) ;
else if ( dst_type = = GL_TEXTURE_1D )
glTexStorage1D ( GL_TEXTURE_1D , 1 , sized_internal_fmt , width ) ;
glTexParameteri ( dst_type , GL_TEXTURE_MIN_FILTER , GL_LINEAR ) ;
glTexParameteri ( dst_type , GL_TEXTURE_MAG_FILTER , GL_LINEAR ) ;
glTexParameteri ( dst_type , GL_TEXTURE_BASE_LEVEL , 0 ) ;
glTexParameteri ( dst_type , GL_TEXTURE_MAX_LEVEL , 0 ) ;
2017-02-16 19:29:56 +01:00
//Empty GL_ERROR
glGetError ( ) ;
glCopyImageSubData ( src_id , GL_TEXTURE_2D , 0 , x , y , 0 ,
2017-11-02 16:54:57 +01:00
dst_id , dst_type , 0 , 0 , 0 , 0 , width , height , 1 ) ;
2017-02-16 19:29:56 +01:00
m_temporary_surfaces . push_back ( dst_id ) ;
//Check for error
if ( GLenum err = glGetError ( ) )
{
LOG_WARNING ( RSX , " Failed to copy image subresource with GL error 0x%X " , err ) ;
return 0 ;
}
2017-04-04 18:14:36 +02:00
2017-02-16 19:29:56 +01:00
return dst_id ;
}
2017-09-08 16:52:13 +02:00
protected :
void free_texture_section ( cached_texture_section & tex ) override
2017-02-16 19:29:56 +01:00
{
2017-09-08 16:52:13 +02:00
tex . destroy ( ) ;
2017-02-16 19:29:56 +01:00
}
2017-09-08 16:52:13 +02:00
u32 create_temporary_subresource_view ( void * & , u32 * src , u32 gcm_format , u16 x , u16 y , u16 w , u16 h ) override
2017-02-13 15:22:25 +01:00
{
2017-09-08 16:52:13 +02:00
const GLenum ifmt = gl : : get_sized_internal_format ( gcm_format ) ;
2017-11-02 16:54:57 +01:00
return create_temporary_subresource_impl ( * src , ifmt , GL_TEXTURE_2D , x , y , w , h ) ;
2016-02-15 10:50:14 +01:00
}
2017-09-08 16:52:13 +02:00
u32 create_temporary_subresource_view ( void * & , gl : : texture * src , u32 gcm_format , u16 x , u16 y , u16 w , u16 h ) override
2017-08-13 23:27:19 +02:00
{
2017-09-08 16:52:13 +02:00
if ( auto as_rtt = dynamic_cast < gl : : render_target * > ( src ) )
2017-08-13 23:27:19 +02:00
{
2017-11-02 16:54:57 +01:00
return create_temporary_subresource_impl ( src - > id ( ) , ( GLenum ) as_rtt - > get_compatible_internal_format ( ) , GL_TEXTURE_2D , x , y , w , h ) ;
2017-08-13 23:27:19 +02:00
}
2017-09-08 16:52:13 +02:00
else
2017-08-13 23:27:19 +02:00
{
2017-09-08 16:52:13 +02:00
const GLenum ifmt = gl : : get_sized_internal_format ( gcm_format ) ;
2017-11-02 16:54:57 +01:00
return create_temporary_subresource_impl ( src - > id ( ) , ifmt , GL_TEXTURE_2D , x , y , w , h ) ;
2017-08-13 23:27:19 +02:00
}
}
2017-11-02 19:54:19 +01:00
u32 generate_cubemap_from_images ( void * & , const u32 gcm_format , u16 size , std : : array < u32 , 6 > & sources ) override
2017-11-02 16:54:57 +01:00
{
2017-11-02 19:54:19 +01:00
const GLenum ifmt = gl : : get_sized_internal_format ( gcm_format ) ;
GLuint dst_id = 0 ;
glGenTextures ( 1 , & dst_id ) ;
glBindTexture ( GL_TEXTURE_CUBE_MAP , dst_id ) ;
glTexStorage2D ( GL_TEXTURE_CUBE_MAP , 1 , ifmt , size , size ) ;
glTexParameteri ( GL_TEXTURE_CUBE_MAP , GL_TEXTURE_MIN_FILTER , GL_LINEAR ) ;
glTexParameteri ( GL_TEXTURE_CUBE_MAP , GL_TEXTURE_MAG_FILTER , GL_LINEAR ) ;
glTexParameteri ( GL_TEXTURE_CUBE_MAP , GL_TEXTURE_BASE_LEVEL , 0 ) ;
glTexParameteri ( GL_TEXTURE_CUBE_MAP , GL_TEXTURE_MAX_LEVEL , 0 ) ;
//Empty GL_ERROR
glGetError ( ) ;
for ( int i = 0 ; i < 6 ; + + i )
{
2017-11-03 13:48:27 +01:00
if ( sources [ i ] ! = 0 )
{
glCopyImageSubData ( sources [ i ] , GL_TEXTURE_2D , 0 , 0 , 0 , 0 ,
dst_id , GL_TEXTURE_CUBE_MAP , 0 , 0 , 0 , i , size , size , 1 ) ;
}
2017-11-02 19:54:19 +01:00
}
m_temporary_surfaces . push_back ( dst_id ) ;
if ( GLenum err = glGetError ( ) )
{
LOG_WARNING ( RSX , " Failed to copy image subresource with GL error 0x%X " , err ) ;
return 0 ;
}
return dst_id ;
2017-11-02 16:54:57 +01:00
}
2017-09-08 16:52:13 +02:00
cached_texture_section * create_new_texture ( void * & , u32 rsx_address , u32 rsx_size , u16 width , u16 height , u16 depth , u16 mipmaps , const u32 gcm_format ,
2017-09-18 19:22:34 +02:00
const rsx : : texture_upload_context context , const rsx : : texture_dimension_extended type , const rsx : : texture_create_flags flags ,
2017-10-12 14:48:31 +02:00
std : : pair < std : : array < u8 , 4 > , std : : array < u8 , 4 > > & /*remap_vector*/ ) override
2016-02-15 10:50:14 +01:00
{
2017-09-08 16:52:13 +02:00
u32 vram_texture = gl : : create_texture ( gcm_format , width , height , depth , mipmaps , type ) ;
bool depth_flag = false ;
2017-02-16 19:29:56 +01:00
2017-09-08 16:52:13 +02:00
switch ( gcm_format )
2016-02-15 10:50:14 +01:00
{
2017-09-08 16:52:13 +02:00
case CELL_GCM_TEXTURE_DEPTH24_D8 :
case CELL_GCM_TEXTURE_DEPTH16 :
depth_flag = true ;
break ;
2017-02-13 15:22:25 +01:00
}
2016-02-15 10:50:14 +01:00
2017-09-14 13:37:14 +02:00
if ( flags = = rsx : : texture_create_flags : : swapped_native_component_order )
{
glBindTexture ( GL_TEXTURE_2D , vram_texture ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_SWIZZLE_R , GL_ALPHA ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_SWIZZLE_G , GL_RED ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_SWIZZLE_B , GL_GREEN ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_SWIZZLE_A , GL_BLUE ) ;
}
2017-09-22 15:12:10 +02:00
auto & cached = create_texture ( vram_texture , rsx_address , rsx_size , width , height , depth , mipmaps ) ;
2017-02-13 15:22:25 +01:00
cached . set_dirty ( false ) ;
2017-09-08 16:52:13 +02:00
cached . set_depth_flag ( depth_flag ) ;
2017-09-14 13:37:14 +02:00
cached . set_view_flags ( flags ) ;
2017-09-18 19:22:34 +02:00
cached . set_context ( context ) ;
2017-11-02 16:54:57 +01:00
cached . set_image_type ( type ) ;
2016-03-11 20:25:49 +01:00
2017-09-19 14:46:16 +02:00
//Its not necessary to lock blit dst textures as they are just reused as necessary
if ( context ! = rsx : : texture_upload_context : : blit_engine_dst | | g_cfg . video . strict_rendering_mode )
2017-10-28 21:17:27 +02:00
{
2017-09-19 14:46:16 +02:00
cached . protect ( utils : : protection : : ro ) ;
2017-10-28 21:17:27 +02:00
update_cache_tag ( ) ;
}
2017-09-19 14:46:16 +02:00
2017-09-08 16:52:13 +02:00
return & cached ;
2016-02-15 10:50:14 +01:00
}
2017-09-08 16:52:13 +02:00
cached_texture_section * upload_image_from_cpu ( void * & , u32 rsx_address , u16 width , u16 height , u16 depth , u16 mipmaps , u16 pitch , const u32 gcm_format ,
2017-09-14 13:37:14 +02:00
const rsx : : texture_upload_context context , std : : vector < rsx_subresource_layout > & subresource_layout , const rsx : : texture_dimension_extended type , const bool swizzled ,
2017-09-08 16:52:13 +02:00
std : : pair < std : : array < u8 , 4 > , std : : array < u8 , 4 > > & remap_vector ) override
2016-02-15 10:50:14 +01:00
{
2017-09-08 16:52:13 +02:00
void * unused = nullptr ;
2017-09-18 19:22:34 +02:00
auto section = create_new_texture ( unused , rsx_address , pitch * height , width , height , depth , mipmaps , gcm_format , context , type ,
2017-09-08 16:52:13 +02:00
rsx : : texture_create_flags : : default_component_order , remap_vector ) ;
2016-02-15 10:50:14 +01:00
2017-09-14 13:37:14 +02:00
//Swizzling is ignored for blit engine copy and emulated using remapping
bool input_swizzled = ( context = = rsx : : texture_upload_context : : blit_engine_src ) ? false : swizzled ;
2017-09-19 14:46:16 +02:00
gl : : upload_texture ( section - > get_raw_texture ( ) , rsx_address , gcm_format , width , height , depth , mipmaps , input_swizzled , type , subresource_layout , remap_vector , false ) ;
2017-09-08 16:52:13 +02:00
return section ;
2017-02-16 19:29:56 +01:00
}
2017-09-08 16:52:13 +02:00
void enforce_surface_creation_type ( cached_texture_section & section , const rsx : : texture_create_flags flags ) override
2017-02-16 19:29:56 +01:00
{
2017-09-14 13:37:14 +02:00
if ( flags = = section . get_view_flags ( ) )
return ;
if ( flags = = rsx : : texture_create_flags : : swapped_native_component_order )
{
glBindTexture ( GL_TEXTURE_2D , section . get_raw_texture ( ) ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_SWIZZLE_R , GL_ALPHA ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_SWIZZLE_G , GL_RED ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_SWIZZLE_B , GL_GREEN ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_SWIZZLE_A , GL_BLUE ) ;
}
section . set_view_flags ( flags ) ;
2017-02-13 15:22:25 +01:00
}
2016-02-15 10:50:14 +01:00
2017-09-08 16:52:13 +02:00
void insert_texture_barrier ( ) override
2017-02-13 15:22:25 +01:00
{
2017-09-08 16:52:13 +02:00
auto & caps = gl : : get_driver_caps ( ) ;
2016-02-15 10:50:14 +01:00
2017-09-08 16:52:13 +02:00
if ( caps . ARB_texture_barrier_supported )
glTextureBarrier ( ) ;
else if ( caps . NV_texture_barrier_supported )
glTextureBarrierNV ( ) ;
2017-08-07 23:54:40 +02:00
}
2017-09-08 16:52:13 +02:00
public :
2016-02-15 10:50:14 +01:00
2017-09-08 16:52:13 +02:00
texture_cache ( ) { }
2016-02-15 10:50:14 +01:00
2017-09-08 16:52:13 +02:00
~ texture_cache ( ) { }
2016-02-15 10:50:14 +01:00
2017-09-08 16:52:13 +02:00
void initialize ( )
2017-02-16 19:29:56 +01:00
{
2017-09-08 16:52:13 +02:00
m_hw_blitter . init ( ) ;
2017-09-28 20:32:00 +02:00
g_hw_blitter = & m_hw_blitter ;
2016-02-15 10:50:14 +01:00
}
2017-03-29 21:27:29 +02:00
2017-09-08 16:52:13 +02:00
void destroy ( ) override
2017-08-07 23:54:40 +02:00
{
2017-09-08 16:52:13 +02:00
clear ( ) ;
2017-09-28 20:32:00 +02:00
g_hw_blitter = nullptr ;
2017-09-08 16:52:13 +02:00
m_hw_blitter . destroy ( ) ;
2017-08-07 23:54:40 +02:00
}
2017-09-28 20:32:00 +02:00
2017-09-19 14:46:16 +02:00
bool is_depth_texture ( const u32 rsx_address , const u32 rsx_size ) override
2017-09-04 12:05:02 +02:00
{
2017-09-14 13:37:14 +02:00
reader_lock lock ( m_cache_mutex ) ;
2017-09-19 14:46:16 +02:00
auto found = m_cache . find ( get_block_address ( rsx_address ) ) ;
2017-09-18 19:22:34 +02:00
if ( found = = m_cache . end ( ) )
return false ;
if ( found - > second . valid_count = = 0 )
return false ;
for ( auto & tex : found - > second . data )
{
if ( tex . is_dirty ( ) )
continue ;
2017-09-19 14:46:16 +02:00
if ( ! tex . overlaps ( rsx_address , true ) )
continue ;
if ( ( rsx_address + rsx_size - tex . get_section_base ( ) ) < = tex . get_section_size ( ) )
return tex . is_depth_texture ( ) ;
}
2017-09-04 12:05:02 +02:00
return false ;
}
2017-09-08 16:52:13 +02:00
void on_frame_end ( ) override
2017-03-29 21:27:29 +02:00
{
2017-09-08 16:52:13 +02:00
if ( m_unreleased_texture_objects > = m_max_zombie_objects )
2017-09-04 12:05:02 +02:00
{
2017-09-08 16:52:13 +02:00
purge_dirty ( ) ;
2017-09-04 12:05:02 +02:00
}
2017-09-08 16:52:13 +02:00
clear_temporary_subresources ( ) ;
2017-11-03 12:16:55 +01:00
m_temporary_subresource_cache . clear ( ) ;
2017-09-08 16:52:13 +02:00
}
2017-03-29 21:27:29 +02:00
2017-09-08 16:52:13 +02:00
bool blit ( rsx : : blit_src_info & src , rsx : : blit_dst_info & dst , bool linear_interpolate , gl_render_targets & m_rtts )
{
void * unused = nullptr ;
return upload_scaled_image ( src , dst , linear_interpolate , unused , m_rtts , m_hw_blitter ) ;
}
2016-02-15 10:50:14 +01:00
} ;
2017-04-04 18:14:36 +02:00
}