2012-11-15 00:39:56 +01:00
# include "stdafx.h"
2014-06-17 17:44:03 +02:00
# include "Utilities/Log.h"
2014-06-02 19:27:24 +02:00
# include "Emu/Memory/Memory.h"
# include "Emu/System.h"
2013-11-09 22:29:49 +01:00
# include "GLFragmentProgram.h"
2012-11-15 00:39:56 +01:00
2014-06-07 16:15:49 +02:00
void GLFragmentDecompilerThread : : SetDst ( std : : string code , bool append_mask )
2012-11-15 00:39:56 +01:00
{
2014-12-17 15:01:59 +01:00
if ( ! src0 . exec_if_eq & & ! src0 . exec_if_gr & & ! src0 . exec_if_lt ) return ;
2013-08-10 23:56:24 +02:00
2014-12-17 15:01:59 +01:00
switch ( src1 . scale )
2012-11-15 00:39:56 +01:00
{
2014-06-07 16:15:49 +02:00
case 0 : break ;
case 1 : code = " ( " + code + " * 2.0) " ; break ;
case 2 : code = " ( " + code + " * 4.0) " ; break ;
case 3 : code = " ( " + code + " * 8.0) " ; break ;
case 5 : code = " ( " + code + " / 2.0) " ; break ;
case 6 : code = " ( " + code + " / 4.0) " ; break ;
case 7 : code = " ( " + code + " / 8.0) " ; break ;
2012-11-15 00:39:56 +01:00
2014-06-07 16:15:49 +02:00
default :
2014-06-27 15:26:46 +02:00
LOG_ERROR ( RSX , " Bad scale: %d " , fmt : : by_value ( src1 . scale ) ) ;
2014-06-07 16:15:49 +02:00
Emu . Pause ( ) ;
2014-12-17 15:01:59 +01:00
break ;
2012-11-15 00:39:56 +01:00
}
2014-12-17 15:01:59 +01:00
if ( dst . saturate )
2013-08-17 00:22:26 +02:00
{
2013-08-19 01:06:11 +02:00
code = " clamp( " + code + " , 0.0, 1.0) " ;
2013-08-17 00:22:26 +02:00
}
2014-12-17 15:01:59 +01:00
code + = ( append_mask ? " $m " : " " ) ;
2014-01-31 19:40:18 +01:00
2014-12-17 15:01:59 +01:00
if ( dst . no_dest )
2014-01-31 21:54:26 +01:00
{
2014-12-17 15:01:59 +01:00
if ( dst . set_cond )
{
AddCode ( " $ifcond " + m_parr . AddParam ( PARAM_NONE , " vec4 " , " cc " + std : : to_string ( src0 . cond_mod_reg_index ) ) + " $m = " + code + " ; " ) ;
}
else
{
AddCode ( " $ifcond " + code + " ; " ) ;
}
return ;
2014-01-31 19:40:18 +01:00
}
2014-06-07 18:20:39 +02:00
2014-12-17 15:01:59 +01:00
std : : string dest = AddReg ( dst . dest_reg , dst . fp16 ) + " $m " ;
2014-12-22 00:10:22 +01:00
AddCodeCond ( Format ( dest ) , code ) ;
2014-12-17 15:01:59 +01:00
//AddCode("$ifcond " + dest + code + (append_mask ? "$m;" : ";"));
if ( dst . set_cond )
2014-06-08 16:52:35 +02:00
{
2014-12-17 15:01:59 +01:00
AddCode ( m_parr . AddParam ( PARAM_NONE , " vec4 " , " cc " + std : : to_string ( src0 . cond_mod_reg_index ) ) + " $m = " + dest + " ; " ) ;
2014-01-31 19:40:18 +01:00
}
2014-06-07 16:15:49 +02:00
}
2012-11-15 00:39:56 +01:00
2014-06-07 16:15:49 +02:00
void GLFragmentDecompilerThread : : AddCode ( const std : : string & code )
{
main . append ( m_code_level , ' \t ' ) + = Format ( code ) + " \n " ;
2012-11-15 00:39:56 +01:00
}
2013-11-27 20:16:19 +01:00
std : : string GLFragmentDecompilerThread : : GetMask ( )
2012-11-15 00:39:56 +01:00
{
2014-01-31 19:40:18 +01:00
std : : string ret ;
2012-11-15 00:39:56 +01:00
2013-08-26 16:18:59 +02:00
static const char dst_mask [ 4 ] =
2013-08-17 00:22:26 +02:00
{
2013-08-26 16:18:59 +02:00
' x ' , ' y ' , ' z ' , ' w ' ,
2013-08-17 00:22:26 +02:00
} ;
2014-12-17 15:01:59 +01:00
if ( dst . mask_x ) ret + = dst_mask [ 0 ] ;
if ( dst . mask_y ) ret + = dst_mask [ 1 ] ;
if ( dst . mask_z ) ret + = dst_mask [ 2 ] ;
if ( dst . mask_w ) ret + = dst_mask [ 3 ] ;
2012-11-15 00:39:56 +01:00
2013-11-27 20:16:19 +01:00
return ret . empty ( ) | | strncmp ( ret . c_str ( ) , dst_mask , 4 ) = = 0 ? " " : ( " . " + ret ) ;
2012-11-15 00:39:56 +01:00
}
2013-11-27 20:16:19 +01:00
std : : string GLFragmentDecompilerThread : : AddReg ( u32 index , int fp16 )
2012-11-15 00:39:56 +01:00
{
2014-12-17 15:01:59 +01:00
return m_parr . AddParam ( PARAM_NONE , " vec4 " , std : : string ( fp16 ? " h " : " r " ) + std : : to_string ( index ) , " vec4(0.0) " ) ;
2013-08-26 16:18:59 +02:00
}
2013-11-09 22:29:49 +01:00
bool GLFragmentDecompilerThread : : HasReg ( u32 index , int fp16 )
2013-08-26 16:18:59 +02:00
{
2014-06-07 16:15:49 +02:00
return m_parr . HasParam ( PARAM_NONE , " vec4 " ,
2013-11-27 20:16:19 +01:00
std : : string ( fp16 ? " h " : " r " ) + std : : to_string ( index ) ) ;
2013-06-30 10:46:29 +02:00
}
2014-06-07 18:20:39 +02:00
std : : string GLFragmentDecompilerThread : : AddCond ( )
2013-08-19 01:06:11 +02:00
{
2014-12-17 15:01:59 +01:00
return m_parr . AddParam ( PARAM_NONE , " vec4 " , " cc " + std : : to_string ( src0 . cond_reg_index ) ) ;
2013-08-19 01:06:11 +02:00
}
2013-11-27 20:16:19 +01:00
std : : string GLFragmentDecompilerThread : : AddConst ( )
2013-06-30 10:46:29 +02:00
{
2014-02-02 20:42:32 +01:00
std : : string name = std : : string ( " fc " ) + std : : to_string ( m_size + 4 * 4 ) ;
2014-12-17 15:01:59 +01:00
if ( m_parr . HasParam ( PARAM_UNIFORM , " vec4 " , name ) )
2014-02-02 20:42:32 +01:00
{
return name ;
}
2014-01-31 21:44:35 +01:00
2014-10-11 19:20:01 +02:00
auto data = vm : : ptr < u32 > : : make ( m_addr + m_size + m_offset ) ;
2013-08-10 23:56:24 +02:00
m_offset + = 4 * 4 ;
u32 x = GetData ( data [ 0 ] ) ;
u32 y = GetData ( data [ 1 ] ) ;
u32 z = GetData ( data [ 2 ] ) ;
u32 w = GetData ( data [ 3 ] ) ;
2014-02-02 20:42:32 +01:00
return m_parr . AddParam ( PARAM_UNIFORM , " vec4 " , name ,
2013-11-27 20:16:19 +01:00
std : : string ( " vec4( " ) + std : : to_string ( ( float & ) x ) + " , " + std : : to_string ( ( float & ) y )
+ " , " + std : : to_string ( ( float & ) z ) + " , " + std : : to_string ( ( float & ) w ) + " ) " ) ;
2012-11-15 00:39:56 +01:00
}
2013-11-27 20:16:19 +01:00
std : : string GLFragmentDecompilerThread : : AddTex ( )
2012-11-15 00:39:56 +01:00
{
2013-11-27 20:16:19 +01:00
return m_parr . AddParam ( PARAM_UNIFORM , " sampler2D " , std : : string ( " tex " ) + std : : to_string ( dst . tex_num ) ) ;
2012-11-15 00:39:56 +01:00
}
2014-06-08 16:52:35 +02:00
std : : string GLFragmentDecompilerThread : : Format ( const std : : string & code )
2014-06-07 16:15:49 +02:00
{
const std : : pair < std : : string , std : : function < std : : string ( ) > > repl_list [ ] =
{
{ " $$ " , [ ] ( ) - > std : : string { return " $ " ; } } ,
{ " $0 " , std : : bind ( std : : mem_fn ( & GLFragmentDecompilerThread : : GetSRC < SRC0 > ) , this , src0 ) } ,
{ " $1 " , std : : bind ( std : : mem_fn ( & GLFragmentDecompilerThread : : GetSRC < SRC1 > ) , this , src1 ) } ,
{ " $2 " , std : : bind ( std : : mem_fn ( & GLFragmentDecompilerThread : : GetSRC < SRC2 > ) , this , src2 ) } ,
{ " $t " , std : : bind ( std : : mem_fn ( & GLFragmentDecompilerThread : : AddTex ) , this ) } ,
2014-06-08 16:52:35 +02:00
{ " $m " , std : : bind ( std : : mem_fn ( & GLFragmentDecompilerThread : : GetMask ) , this ) } ,
{ " $ifcond " , [ this ] ( ) - > std : : string
2014-12-17 15:01:59 +01:00
{
const std : : string & cond = GetCond ( ) ;
if ( cond = = " true " ) return " " ;
return " if( " + cond + " ) " ;
}
2014-06-08 16:52:35 +02:00
} ,
2014-06-07 16:15:49 +02:00
{ " $cond " , std : : bind ( std : : mem_fn ( & GLFragmentDecompilerThread : : GetCond ) , this ) } ,
{ " $c " , std : : bind ( std : : mem_fn ( & GLFragmentDecompilerThread : : AddConst ) , this ) }
} ;
return fmt : : replace_all ( code , repl_list ) ;
}
std : : string GLFragmentDecompilerThread : : GetCond ( )
{
if ( src0 . exec_if_gr & & src0 . exec_if_lt & & src0 . exec_if_eq )
{
return " true " ;
}
else if ( ! src0 . exec_if_gr & & ! src0 . exec_if_lt & & ! src0 . exec_if_eq )
{
return " false " ;
}
static const char f [ 4 ] = { ' x ' , ' y ' , ' z ' , ' w ' } ;
std : : string swizzle , cond ;
swizzle + = f [ src0 . cond_swizzle_x ] ;
swizzle + = f [ src0 . cond_swizzle_y ] ;
swizzle + = f [ src0 . cond_swizzle_z ] ;
swizzle + = f [ src0 . cond_swizzle_w ] ;
swizzle = swizzle = = " xyzw " ? " " : " . " + swizzle ;
if ( src0 . exec_if_gr & & src0 . exec_if_eq )
{
cond = " greaterThanEqual " ;
}
else if ( src0 . exec_if_lt & & src0 . exec_if_eq )
{
cond = " lessThanEqual " ;
}
else if ( src0 . exec_if_gr & & src0 . exec_if_lt )
{
cond = " notEqual " ;
}
else if ( src0 . exec_if_gr )
{
cond = " greaterThan " ;
}
else if ( src0 . exec_if_lt )
{
cond = " lessThan " ;
}
else //if(src0.exec_if_eq)
{
cond = " equal " ;
}
2014-06-07 18:20:39 +02:00
return " any( " + cond + " ( " + AddCond ( ) + swizzle + " , vec4(0.0))) " ;
2014-06-07 16:15:49 +02:00
}
2014-12-17 15:01:59 +01:00
void GLFragmentDecompilerThread : : AddCodeCond ( const std : : string & dst , const std : : string & src )
{
if ( src0 . exec_if_gr & & src0 . exec_if_lt & & src0 . exec_if_eq )
{
AddCode ( dst + " = " + src + " ; " ) ;
return ;
}
if ( ! src0 . exec_if_gr & & ! src0 . exec_if_lt & & ! src0 . exec_if_eq )
{
AddCode ( " // " + dst + " = " + src + " ; " ) ;
return ;
}
static const char f [ 4 ] = { ' x ' , ' y ' , ' z ' , ' w ' } ;
std : : string swizzle , cond ;
swizzle + = f [ src0 . cond_swizzle_x ] ;
swizzle + = f [ src0 . cond_swizzle_y ] ;
swizzle + = f [ src0 . cond_swizzle_z ] ;
swizzle + = f [ src0 . cond_swizzle_w ] ;
swizzle = swizzle = = " xyzw " ? " " : " . " + swizzle ;
if ( src0 . exec_if_gr & & src0 . exec_if_eq )
{
cond = " greaterThanEqual " ;
}
else if ( src0 . exec_if_lt & & src0 . exec_if_eq )
{
cond = " lessThanEqual " ;
}
else if ( src0 . exec_if_gr & & src0 . exec_if_lt )
{
cond = " notEqual " ;
}
else if ( src0 . exec_if_gr )
{
cond = " greaterThan " ;
}
else if ( src0 . exec_if_lt )
{
cond = " lessThan " ;
}
else //if(src0.exec_if_eq)
{
cond = " equal " ;
}
cond = cond + " ( " + AddCond ( ) + swizzle + " , vec4(0.0)) " ;
ShaderVar dst_var ( dst ) ;
dst_var . symplify ( ) ;
//const char *c_mask = f;
if ( dst_var . swizzles [ 0 ] . length ( ) = = 1 )
{
AddCode ( " if ( " + cond + " .x) " + dst + " = vec4( " + src + " ).x; " ) ;
}
else
{
for ( int i = 0 ; i < dst_var . swizzles [ 0 ] . length ( ) ; + + i )
{
AddCode ( " if ( " + cond + " . " + f [ i ] + " ) " + dst + " . " + f [ i ] + " = " + src + " . " + f [ i ] + " ; " ) ;
}
}
}
2013-11-27 20:16:19 +01:00
template < typename T > std : : string GLFragmentDecompilerThread : : GetSRC ( T src )
2012-11-15 00:39:56 +01:00
{
2014-06-07 17:52:25 +02:00
std : : string ret ;
2012-11-15 00:39:56 +01:00
2014-12-17 15:01:59 +01:00
switch ( src . reg_type )
2012-11-15 00:39:56 +01:00
{
case 0 : //tmp
2013-08-10 23:56:24 +02:00
ret + = AddReg ( src . tmp_reg_index , src . fp16 ) ;
2014-12-17 15:01:59 +01:00
break ;
2012-11-15 00:39:56 +01:00
case 1 : //input
{
2013-11-27 20:16:19 +01:00
static const std : : string reg_table [ ] =
2012-11-15 00:39:56 +01:00
{
" gl_Position " ,
2014-06-08 16:52:35 +02:00
" diff_color " , " spec_color " ,
2012-11-15 00:39:56 +01:00
" fogc " ,
2014-06-07 17:52:25 +02:00
" tc0 " , " tc1 " , " tc2 " , " tc3 " , " tc4 " , " tc5 " , " tc6 " , " tc7 " , " tc8 " , " tc9 " ,
" ssa "
2012-11-15 00:39:56 +01:00
} ;
2014-12-17 15:01:59 +01:00
switch ( dst . src_attr_reg_num )
2012-11-15 00:39:56 +01:00
{
case 0x00 : ret + = reg_table [ 0 ] ; break ;
default :
2014-12-17 15:01:59 +01:00
if ( dst . src_attr_reg_num < sizeof ( reg_table ) / sizeof ( reg_table [ 0 ] ) )
2012-11-15 00:39:56 +01:00
{
ret + = m_parr . AddParam ( PARAM_IN , " vec4 " , reg_table [ dst . src_attr_reg_num ] ) ;
}
else
{
2014-06-27 15:26:46 +02:00
LOG_ERROR ( RSX , " Bad src reg num: %d " , fmt : : by_value ( dst . src_attr_reg_num ) ) ;
2012-11-15 00:39:56 +01:00
ret + = m_parr . AddParam ( PARAM_IN , " vec4 " , " unk " ) ;
Emu . Pause ( ) ;
}
2014-12-17 15:01:59 +01:00
break ;
2012-11-15 00:39:56 +01:00
}
}
break ;
2013-06-30 10:46:29 +02:00
case 2 : //const
ret + = AddConst ( ) ;
2014-12-17 15:01:59 +01:00
break ;
2012-11-15 00:39:56 +01:00
default :
2014-06-27 15:26:46 +02:00
LOG_ERROR ( RSX , " Bad src type %d " , fmt : : by_value ( src . reg_type ) ) ;
2012-11-15 00:39:56 +01:00
Emu . Pause ( ) ;
2014-12-17 15:01:59 +01:00
break ;
2012-11-15 00:39:56 +01:00
}
2014-12-17 15:01:59 +01:00
static const char f [ 4 ] = { ' x ' , ' y ' , ' z ' , ' w ' } ;
2013-08-17 00:22:26 +02:00
2013-11-27 20:16:19 +01:00
std : : string swizzle = " " ;
2012-11-15 00:39:56 +01:00
swizzle + = f [ src . swizzle_x ] ;
swizzle + = f [ src . swizzle_y ] ;
swizzle + = f [ src . swizzle_z ] ;
swizzle + = f [ src . swizzle_w ] ;
2014-12-17 15:01:59 +01:00
if ( strncmp ( swizzle . c_str ( ) , f , 4 ) ! = 0 ) ret + = " . " + swizzle ;
2012-11-15 00:39:56 +01:00
2014-12-17 15:01:59 +01:00
if ( src . abs ) ret = " abs( " + ret + " ) " ;
if ( src . neg ) ret = " - " + ret ;
2012-11-15 00:39:56 +01:00
return ret ;
}
2013-11-27 20:16:19 +01:00
std : : string GLFragmentDecompilerThread : : BuildCode ( )
2012-11-15 00:39:56 +01:00
{
2014-04-01 02:33:55 +02:00
//main += fmt::Format("\tgl_FragColor = %c0;\n", m_ctrl & 0x40 ? 'r' : 'h');
2014-07-23 20:36:57 +02:00
const std : : pair < std : : string , std : : string > table [ ] =
2014-06-07 16:15:49 +02:00
{
2014-07-21 11:57:07 +02:00
{ " ocol0 " , m_ctrl & 0x40 ? " r0 " : " h0 " } ,
2014-12-17 15:01:59 +01:00
{ " ocol1 " , m_ctrl & 0x40 ? " r2 " : " h4 " } ,
{ " ocol2 " , m_ctrl & 0x40 ? " r3 " : " h6 " } ,
{ " ocol3 " , m_ctrl & 0x40 ? " r4 " : " h8 " } ,
2014-06-07 16:15:49 +02:00
} ;
for ( int i = 0 ; i < sizeof ( table ) / sizeof ( * table ) ; + + i )
{
if ( m_parr . HasParam ( PARAM_NONE , " vec4 " , table [ i ] . second ) )
2014-07-21 11:57:07 +02:00
AddCode ( m_parr . AddParam ( PARAM_OUT , " vec4 " , table [ i ] . first , i ) + " = " + table [ i ] . second + " ; " ) ;
2014-06-07 16:15:49 +02:00
}
2014-12-17 15:01:59 +01:00
if ( m_ctrl & 0xe ) main + = m_ctrl & 0x40 ? " \t gl_FragDepth = r1.z; \n " : " \t gl_FragDepth = h2.z; \n " ;
2013-11-26 05:34:19 +01:00
2014-03-13 01:26:53 +01:00
std : : string p ;
2012-11-15 00:39:56 +01:00
2014-08-18 22:07:52 +02:00
for ( auto & param : m_parr . params ) {
p + = param . Format ( ) ;
2012-11-15 00:39:56 +01:00
}
2013-11-27 20:16:19 +01:00
return std : : string ( " #version 330 \n "
2014-07-18 17:03:39 +02:00
" \n "
+ p + " \n "
" void main() \n { \n " + main + " } \n " ) ;
2012-11-15 00:39:56 +01:00
}
2013-11-09 22:29:49 +01:00
void GLFragmentDecompilerThread : : Task ( )
2012-11-15 00:39:56 +01:00
{
2014-10-11 19:20:01 +02:00
auto data = vm : : ptr < u32 > : : make ( m_addr ) ;
2013-06-30 10:46:29 +02:00
m_size = 0 ;
2013-11-19 22:10:23 +01:00
m_location = 0 ;
2014-06-07 16:15:49 +02:00
m_loop_count = 0 ;
m_code_level = 1 ;
2012-11-15 00:39:56 +01:00
2014-12-17 15:01:59 +01:00
enum
{
FORCE_NONE ,
FORCE_SCT ,
FORCE_SCB ,
} ;
int forced_unit = FORCE_NONE ;
while ( true )
2012-11-15 00:39:56 +01:00
{
2014-06-07 17:32:15 +02:00
for ( auto finded = std : : find ( m_end_offsets . begin ( ) , m_end_offsets . end ( ) , m_size ) ;
finded ! = m_end_offsets . end ( ) ;
finded = std : : find ( m_end_offsets . begin ( ) , m_end_offsets . end ( ) , m_size ) )
2014-06-07 16:15:49 +02:00
{
m_end_offsets . erase ( finded ) ;
m_code_level - - ;
AddCode ( " } " ) ;
m_loop_count - - ;
}
2014-06-07 17:32:15 +02:00
for ( auto finded = std : : find ( m_else_offsets . begin ( ) , m_else_offsets . end ( ) , m_size ) ;
finded ! = m_else_offsets . end ( ) ;
finded = std : : find ( m_else_offsets . begin ( ) , m_else_offsets . end ( ) , m_size ) )
2014-06-07 16:15:49 +02:00
{
m_else_offsets . erase ( finded ) ;
m_code_level - - ;
AddCode ( " } " ) ;
AddCode ( " else " ) ;
AddCode ( " { " ) ;
m_code_level + + ;
}
2012-11-15 00:39:56 +01:00
dst . HEX = GetData ( data [ 0 ] ) ;
src0 . HEX = GetData ( data [ 1 ] ) ;
src1 . HEX = GetData ( data [ 2 ] ) ;
src2 . HEX = GetData ( data [ 3 ] ) ;
2014-08-31 17:01:48 +02:00
m_offset = 4 * sizeof ( u32 ) ;
2013-08-10 23:56:24 +02:00
2013-11-03 20:23:16 +01:00
const u32 opcode = dst . opcode | ( src1 . opcode_is_branch < < 6 ) ;
2014-12-17 15:01:59 +01:00
auto SCT = [ & ] ( )
2012-11-15 00:39:56 +01:00
{
2014-12-17 15:01:59 +01:00
switch ( opcode )
2014-07-19 13:58:32 +02:00
{
2014-12-17 15:01:59 +01:00
case RSX_FP_OPCODE_ADD : SetDst ( " ($0 + $1) " ) ; break ;
case RSX_FP_OPCODE_DIV : SetDst ( " ($0 / $1) " ) ; break ;
case RSX_FP_OPCODE_DIVSQ : SetDst ( " ($0 / sqrt($1)) " ) ; break ;
case RSX_FP_OPCODE_DP2 : SetDst ( " vec4(dot($0.xy, $1.xy)) " ) ; break ;
case RSX_FP_OPCODE_DP3 : SetDst ( " vec4(dot($0.xyz, $1.xyz)) " ) ; break ;
case RSX_FP_OPCODE_DP4 : SetDst ( " vec4(dot($0, $1)) " ) ; break ;
case RSX_FP_OPCODE_DP2A : SetDst ( " vec4($0.x * $1.x + $0.y * $1.y + $2.x) " ) ; break ;
case RSX_FP_OPCODE_MAD : SetDst ( " ($0 * $1 + $2) " ) ; break ;
case RSX_FP_OPCODE_MAX : SetDst ( " max($0, $1) " ) ; break ;
case RSX_FP_OPCODE_MIN : SetDst ( " min($0, $1) " ) ; break ;
case RSX_FP_OPCODE_MOV : SetDst ( " $0 " ) ; break ;
case RSX_FP_OPCODE_MUL : SetDst ( " ($0 * $1) " ) ; break ;
case RSX_FP_OPCODE_RCP : SetDst ( " 1 / $0 " ) ; break ;
case RSX_FP_OPCODE_RSQ : SetDst ( " inversesqrt(abs($0)) " ) ; break ;
case RSX_FP_OPCODE_SEQ : SetDst ( " vec4(equal($0, $1)) " ) ; break ;
case RSX_FP_OPCODE_SFL : SetDst ( " vec4(0.0) " ) ; break ;
case RSX_FP_OPCODE_SGE : SetDst ( " vec4(greaterThanEqual($0, $1)) " ) ; break ;
case RSX_FP_OPCODE_SGT : SetDst ( " vec4(greaterThan($0, $1)) " ) ; break ;
case RSX_FP_OPCODE_SLE : SetDst ( " vec4(lessThanEqual($0, $1)) " ) ; break ;
case RSX_FP_OPCODE_SLT : SetDst ( " vec4(lessThan($0, $1)) " ) ; break ;
case RSX_FP_OPCODE_SNE : SetDst ( " vec4(notEqual($0, $1)) " ) ; break ;
case RSX_FP_OPCODE_STR : SetDst ( " vec4(1.0) " ) ; break ;
default :
return false ;
2014-07-19 13:58:32 +02:00
}
2014-12-17 15:01:59 +01:00
return true ;
} ;
auto SCB = [ & ] ( )
{
switch ( opcode )
2014-07-19 13:58:32 +02:00
{
2014-12-17 15:01:59 +01:00
case RSX_FP_OPCODE_ADD : SetDst ( " ($0 + $1) " ) ; break ;
case RSX_FP_OPCODE_COS : SetDst ( " cos($0) " ) ; break ;
case RSX_FP_OPCODE_DP2 : SetDst ( " vec4(dot($0.xy, $1.xy)) " ) ; break ;
case RSX_FP_OPCODE_DP3 : SetDst ( " vec4(dot($0.xyz, $1.xyz)) " ) ; break ;
case RSX_FP_OPCODE_DP4 : SetDst ( " vec4(dot($0, $1)) " ) ; break ;
case RSX_FP_OPCODE_DP2A : SetDst ( " vec4($0.x * $1.x + $0.y * $1.y + $2.x) " ) ; break ;
case RSX_FP_OPCODE_DST : SetDst ( " vec4(distance($0, $1)) " ) ; break ;
2014-12-28 08:53:01 +01:00
case RSX_FP_OPCODE_REFL : LOG_ERROR ( RSX , " Unimplemented SCB instruction: REFL " ) ; break ; // TODO: Is this in the right category?
2014-12-17 15:01:59 +01:00
case RSX_FP_OPCODE_EX2 : SetDst ( " exp2($0) " ) ; break ;
case RSX_FP_OPCODE_FLR : SetDst ( " floor($0) " ) ; break ;
case RSX_FP_OPCODE_FRC : SetDst ( " fract($0) " ) ; break ;
2014-12-28 23:21:34 +01:00
case RSX_FP_OPCODE_LIT : SetDst ( " vec4(1.0, $0.x, ($0.x > 0.0 ? exp($0.w * log2($0.y)) : 0.0), 1.0) " ) ; break ;
2014-12-17 15:01:59 +01:00
case RSX_FP_OPCODE_LIF : SetDst ( " vec4(1.0, $0.y, ($0.y > 0 ? pow(2.0, $0.w) : 0.0), 1.0) " ) ; break ;
2014-12-28 08:53:01 +01:00
case RSX_FP_OPCODE_LRP : LOG_ERROR ( RSX , " Unimplemented SCB instruction: LRP " ) ; break ; // TODO: Is this in the right category?
2014-12-17 15:01:59 +01:00
case RSX_FP_OPCODE_LG2 : SetDst ( " log2($0) " ) ; break ;
case RSX_FP_OPCODE_MAD : SetDst ( " ($0 * $1 + $2) " ) ; break ;
case RSX_FP_OPCODE_MAX : SetDst ( " max($0, $1) " ) ; break ;
case RSX_FP_OPCODE_MIN : SetDst ( " min($0, $1) " ) ; break ;
case RSX_FP_OPCODE_MOV : SetDst ( " $0 " ) ; break ;
case RSX_FP_OPCODE_MUL : SetDst ( " ($0 * $1) " ) ; break ;
2014-12-28 08:53:01 +01:00
case RSX_FP_OPCODE_PK2 : SetDst ( " packSnorm2x16($0) " ) ; break ; // TODO: More testing (Sonic The Hedgehog (NPUB-30442/NPEB-00478))
case RSX_FP_OPCODE_PK4 : SetDst ( " packSnorm4x8($0) " ) ; break ; // TODO: More testing (Sonic The Hedgehog (NPUB-30442/NPEB-00478))
2014-12-17 15:01:59 +01:00
case RSX_FP_OPCODE_PK16 : LOG_ERROR ( RSX , " Unimplemented SCB instruction: PK16 " ) ; break ;
case RSX_FP_OPCODE_PKB : LOG_ERROR ( RSX , " Unimplemented SCB instruction: PKB " ) ; break ;
case RSX_FP_OPCODE_PKG : LOG_ERROR ( RSX , " Unimplemented SCB instruction: PKG " ) ; break ;
case RSX_FP_OPCODE_SEQ : SetDst ( " vec4(equal($0, $1)) " ) ; break ;
case RSX_FP_OPCODE_SFL : SetDst ( " vec4(0.0) " ) ; break ;
case RSX_FP_OPCODE_SGE : SetDst ( " vec4(greaterThanEqual($0, $1)) " ) ; break ;
case RSX_FP_OPCODE_SGT : SetDst ( " vec4(greaterThan($0, $1)) " ) ; break ;
case RSX_FP_OPCODE_SIN : SetDst ( " sin($0) " ) ; break ;
case RSX_FP_OPCODE_SLE : SetDst ( " vec4(lessThanEqual($0, $1)) " ) ; break ;
case RSX_FP_OPCODE_SLT : SetDst ( " vec4(lessThan($0, $1)) " ) ; break ;
case RSX_FP_OPCODE_SNE : SetDst ( " vec4(notEqual($0, $1)) " ) ; break ;
case RSX_FP_OPCODE_STR : SetDst ( " vec4(1.0) " ) ; break ;
default :
return false ;
2014-07-19 13:58:32 +02:00
}
2014-12-17 15:01:59 +01:00
return true ;
} ;
auto TEX_SRB = [ & ] ( )
{
switch ( opcode )
2014-07-19 13:58:32 +02:00
{
2014-12-17 15:01:59 +01:00
case RSX_FP_OPCODE_DDX : SetDst ( " dFdx($0) " ) ; break ;
case RSX_FP_OPCODE_DDY : SetDst ( " dFdy($0) " ) ; break ;
case RSX_FP_OPCODE_NRM : SetDst ( " normalize($0) " ) ; break ;
2014-12-28 08:53:01 +01:00
case RSX_FP_OPCODE_BEM : LOG_ERROR ( RSX , " Unimplemented TEX_SRB instruction: BEM " ) ; break ;
2014-12-17 15:01:59 +01:00
case RSX_FP_OPCODE_TEX : SetDst ( " texture($t, $0.xy) " ) ; break ;
2014-12-28 08:53:01 +01:00
case RSX_FP_OPCODE_TEXBEM : LOG_ERROR ( RSX , " Unimplemented TEX_SRB instruction: TEXBEM " ) ; break ;
case RSX_FP_OPCODE_TXP : SetDst ( " textureProj($t, $0.xy, $1) " ) ; break ; //TODO: More testing (Sonic The Hedgehog (NPUB-30442/NPEB-00478) and The Simpsons Arcade Game (NPUB30563))
case RSX_FP_OPCODE_TXPBEM : LOG_ERROR ( RSX , " Unimplemented TEX_SRB instruction: TXPBEM " ) ; break ;
2014-12-17 15:01:59 +01:00
case RSX_FP_OPCODE_TXD : LOG_ERROR ( RSX , " Unimplemented TEX_SRB instruction: TXD " ) ; break ;
2014-12-27 10:00:44 +01:00
case RSX_FP_OPCODE_TXB : SetDst ( " texture($t, $0.xy, $1.x) " ) ; break ;
2014-12-26 16:56:55 +01:00
case RSX_FP_OPCODE_TXL : SetDst ( " textureLod($t, $0.xy, $1.x) " ) ; break ;
2014-12-28 08:53:01 +01:00
case RSX_FP_OPCODE_UP2 : SetDst ( " unpackSnorm2x16($0) " ) ; break ; // TODO: More testing (Sonic The Hedgehog (NPUB-30442/NPEB-00478))
case RSX_FP_OPCODE_UP4 : SetDst ( " unpackSnorm4x8($0) " ) ; break ; // TODO: More testing (Sonic The Hedgehog (NPUB-30442/NPEB-00478))
2014-12-17 15:01:59 +01:00
case RSX_FP_OPCODE_UP16 : LOG_ERROR ( RSX , " Unimplemented TEX_SRB instruction: UP16 " ) ; break ;
case RSX_FP_OPCODE_UPB : LOG_ERROR ( RSX , " Unimplemented TEX_SRB instruction: UPB " ) ; break ;
case RSX_FP_OPCODE_UPG : LOG_ERROR ( RSX , " Unimplemented TEX_SRB instruction: UPG " ) ; break ;
default :
return false ;
2014-07-19 13:58:32 +02:00
}
2014-12-17 15:01:59 +01:00
return true ;
} ;
auto SIP = [ & ] ( )
{
switch ( opcode )
2014-07-19 13:58:32 +02:00
{
2014-12-17 15:01:59 +01:00
case RSX_FP_OPCODE_BRK : SetDst ( " break " ) ; break ;
case RSX_FP_OPCODE_CAL : LOG_ERROR ( RSX , " Unimplemented SIP instruction: CAL " ) ; break ;
case RSX_FP_OPCODE_FENCT : forced_unit = FORCE_SCT ; break ;
case RSX_FP_OPCODE_FENCB : forced_unit = FORCE_SCB ; break ;
case RSX_FP_OPCODE_IFE :
AddCode ( " if($cond) " ) ;
m_else_offsets . push_back ( src1 . else_offset < < 2 ) ;
2014-07-19 13:58:32 +02:00
m_end_offsets . push_back ( src2 . end_offset < < 2 ) ;
AddCode ( " { " ) ;
m_code_level + + ;
2014-12-17 15:01:59 +01:00
break ;
case RSX_FP_OPCODE_LOOP :
if ( ! src0 . exec_if_eq & & ! src0 . exec_if_gr & & ! src0 . exec_if_lt )
{
AddCode ( fmt : : Format ( " $ifcond for(int i%u = %u; i%u < %u; i%u += %u) {} //-> %u //LOOP " ,
m_loop_count , src1 . init_counter , m_loop_count , src1 . end_counter , m_loop_count , src1 . increment , src2 . end_offset ) ) ;
}
else
{
AddCode ( fmt : : Format ( " $ifcond for(int i%u = %u; i%u < %u; i%u += %u) //LOOP " ,
m_loop_count , src1 . init_counter , m_loop_count , src1 . end_counter , m_loop_count , src1 . increment ) ) ;
m_loop_count + + ;
m_end_offsets . push_back ( src2 . end_offset < < 2 ) ;
AddCode ( " { " ) ;
m_code_level + + ;
}
break ;
case RSX_FP_OPCODE_REP :
if ( ! src0 . exec_if_eq & & ! src0 . exec_if_gr & & ! src0 . exec_if_lt )
{
AddCode ( fmt : : Format ( " $ifcond for(int i%u = %u; i%u < %u; i%u += %u) {} //-> %u //REP " ,
m_loop_count , src1 . init_counter , m_loop_count , src1 . end_counter , m_loop_count , src1 . increment , src2 . end_offset ) ) ;
}
else
{
AddCode ( fmt : : Format ( " if($cond) for(int i%u = %u; i%u < %u; i%u += %u) //REP " ,
m_loop_count , src1 . init_counter , m_loop_count , src1 . end_counter , m_loop_count , src1 . increment ) ) ;
m_loop_count + + ;
m_end_offsets . push_back ( src2 . end_offset < < 2 ) ;
AddCode ( " { " ) ;
m_code_level + + ;
}
break ;
case RSX_FP_OPCODE_RET : SetDst ( " return " ) ; break ;
default :
return false ;
2014-07-19 13:58:32 +02:00
}
2014-12-17 15:01:59 +01:00
return true ;
} ;
switch ( opcode )
{
case RSX_FP_OPCODE_NOP : break ;
case RSX_FP_OPCODE_KIL : SetDst ( " discard " , false ) ; break ;
2014-06-07 16:15:49 +02:00
2012-11-15 00:39:56 +01:00
default :
2014-12-17 15:01:59 +01:00
if ( forced_unit = = FORCE_NONE )
{
if ( SIP ( ) ) break ;
if ( SCT ( ) ) break ;
if ( TEX_SRB ( ) ) break ;
if ( SCB ( ) ) break ;
}
else if ( forced_unit = = FORCE_SCT )
{
forced_unit = FORCE_NONE ;
if ( SCT ( ) ) break ;
}
else if ( forced_unit = = FORCE_SCB )
{
forced_unit = FORCE_NONE ;
if ( SCB ( ) ) break ;
}
LOG_ERROR ( RSX , " Unknown/illegal instruction: 0x%x (forced unit %d) " , opcode , forced_unit ) ;
break ;
2012-11-15 00:39:56 +01:00
}
2013-08-10 23:56:24 +02:00
m_size + = m_offset ;
2012-11-15 00:39:56 +01:00
2014-12-17 15:01:59 +01:00
if ( dst . end ) break ;
2012-11-15 00:39:56 +01:00
2014-08-31 17:01:48 +02:00
assert ( m_offset % sizeof ( u32 ) = = 0 ) ;
data + = m_offset / sizeof ( u32 ) ;
2012-11-15 00:39:56 +01:00
}
2014-06-21 23:23:24 +02:00
// flush m_code_level
m_code_level = 1 ;
2012-11-15 00:39:56 +01:00
m_shader = BuildCode ( ) ;
2013-11-27 20:16:19 +01:00
main . clear ( ) ;
2014-04-10 00:54:32 +02:00
m_parr . params . clear ( ) ;
2012-11-15 00:39:56 +01:00
}
2014-12-17 15:01:59 +01:00
GLShaderProgram : : GLShaderProgram ( )
2013-08-17 00:22:26 +02:00
: m_decompiler_thread ( nullptr )
2014-04-15 16:12:15 +02:00
, m_id ( 0 )
2012-11-15 00:39:56 +01:00
{
}
2013-11-09 22:29:49 +01:00
GLShaderProgram : : ~ GLShaderProgram ( )
2012-11-15 00:39:56 +01:00
{
2014-12-17 15:01:59 +01:00
if ( m_decompiler_thread )
2012-11-15 00:39:56 +01:00
{
Wait ( ) ;
2014-12-17 15:01:59 +01:00
if ( m_decompiler_thread - > IsAlive ( ) )
2013-06-30 10:46:29 +02:00
{
m_decompiler_thread - > Stop ( ) ;
}
2014-12-17 15:01:59 +01:00
2013-06-30 10:46:29 +02:00
delete m_decompiler_thread ;
m_decompiler_thread = nullptr ;
2012-11-15 00:39:56 +01:00
}
Delete ( ) ;
}
2014-04-15 16:12:15 +02:00
void GLShaderProgram : : Wait ( )
{
2014-12-17 15:01:59 +01:00
if ( m_decompiler_thread & & m_decompiler_thread - > IsAlive ( ) )
2014-04-15 16:12:15 +02:00
{
m_decompiler_thread - > Join ( ) ;
}
}
2013-11-09 22:29:49 +01:00
void GLShaderProgram : : Decompile ( RSXShaderProgram & prog )
2012-11-15 00:39:56 +01:00
{
2014-04-15 16:12:15 +02:00
GLFragmentDecompilerThread decompiler ( m_shader , m_parr , prog . addr , prog . size , prog . ctrl ) ;
decompiler . Task ( ) ;
}
void GLShaderProgram : : DecompileAsync ( RSXShaderProgram & prog )
{
2014-12-17 15:01:59 +01:00
if ( m_decompiler_thread )
2012-11-15 00:39:56 +01:00
{
Wait ( ) ;
2014-12-17 15:01:59 +01:00
if ( m_decompiler_thread - > IsAlive ( ) )
2013-06-30 10:46:29 +02:00
{
m_decompiler_thread - > Stop ( ) ;
}
2014-12-17 15:01:59 +01:00
2013-06-30 10:46:29 +02:00
delete m_decompiler_thread ;
m_decompiler_thread = nullptr ;
2012-11-15 00:39:56 +01:00
}
2014-04-15 16:12:15 +02:00
m_decompiler_thread = new GLFragmentDecompilerThread ( m_shader , m_parr , prog . addr , prog . size , prog . ctrl ) ;
2013-06-30 10:46:29 +02:00
m_decompiler_thread - > Start ( ) ;
2012-11-15 00:39:56 +01:00
}
2013-11-09 22:29:49 +01:00
void GLShaderProgram : : Compile ( )
2012-11-15 00:39:56 +01:00
{
2014-12-28 23:37:32 +01:00
if ( m_id )
{
glDeleteShader ( m_id ) ;
}
2012-11-15 00:39:56 +01:00
2014-04-15 16:12:15 +02:00
m_id = glCreateShader ( GL_FRAGMENT_SHADER ) ;
2012-11-15 00:39:56 +01:00
2014-04-15 16:12:15 +02:00
const char * str = m_shader . c_str ( ) ;
const int strlen = m_shader . length ( ) ;
2012-11-15 00:39:56 +01:00
2014-04-15 16:12:15 +02:00
glShaderSource ( m_id , 1 , & str , & strlen ) ;
glCompileShader ( m_id ) ;
2012-11-15 00:39:56 +01:00
2014-04-15 16:12:15 +02:00
GLint compileStatus = GL_FALSE ;
glGetShaderiv ( m_id , GL_COMPILE_STATUS , & compileStatus ) ; // Determine the result of the glCompileShader call
if ( compileStatus ! = GL_TRUE ) // If the shader failed to compile...
2012-11-15 00:39:56 +01:00
{
2014-04-15 16:12:15 +02:00
GLint infoLength ;
glGetShaderiv ( m_id , GL_INFO_LOG_LENGTH , & infoLength ) ; // Retrieve the length in bytes (including trailing NULL) of the shader info log
2012-11-15 00:39:56 +01:00
2014-04-15 16:12:15 +02:00
if ( infoLength > 0 )
2012-11-15 00:39:56 +01:00
{
GLsizei len ;
2014-04-15 16:12:15 +02:00
char * buf = new char [ infoLength ] ; // Buffer to store infoLog
glGetShaderInfoLog ( m_id , infoLength , & len , buf ) ; // Retrieve the shader info log into our buffer
2014-06-27 15:26:46 +02:00
LOG_ERROR ( RSX , " Failed to compile shader: %s " , buf ) ; // Write log to the console
2014-04-15 16:12:15 +02:00
2013-06-30 10:46:29 +02:00
delete [ ] buf ;
2012-11-15 00:39:56 +01:00
}
2014-06-17 17:44:03 +02:00
LOG_NOTICE ( RSX , m_shader . c_str ( ) ) ; // Log the text of the shader that failed to compile
2014-04-15 16:12:15 +02:00
Emu . Pause ( ) ; // Pause the emulator, we can't really continue from here
2012-11-15 00:39:56 +01:00
}
}
2013-11-09 22:29:49 +01:00
void GLShaderProgram : : Delete ( )
2012-11-15 00:39:56 +01:00
{
2014-08-18 22:07:52 +02:00
for ( auto & param : m_parr . params ) {
param . items . clear ( ) ;
param . type . clear ( ) ;
2012-11-15 00:39:56 +01:00
}
2013-06-30 10:46:29 +02:00
2014-04-15 16:12:15 +02:00
m_parr . params . clear ( ) ;
m_shader . clear ( ) ;
2012-11-15 00:39:56 +01:00
2014-04-15 16:12:15 +02:00
if ( m_id )
2012-11-15 00:39:56 +01:00
{
2014-06-19 22:34:09 +02:00
if ( Emu . IsStopped ( ) )
{
2014-06-27 15:26:46 +02:00
LOG_WARNING ( RSX , " GLShaderProgram::Delete(): glDeleteShader(%d) avoided " , m_id ) ;
2014-06-19 22:34:09 +02:00
}
else
{
glDeleteShader ( m_id ) ;
}
2014-04-15 16:12:15 +02:00
m_id = 0 ;
2012-11-15 00:39:56 +01:00
}
2013-11-19 11:30:58 +01:00
}