daedalus/Source/SysCTR/HLEGraphics/Combiner/CombinerTree.cpp
2023-04-25 11:01:34 +10:00

619 lines
19 KiB
C++

/*
Copyright (C) 2007 StrmnNrmn
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "stdafx.h"
#include "CombinerTree.h"
#include "CombinerExpression.h"
#include "RenderSettings.h"
#include "BlendConstant.h"
#include "HLEGraphics/RDP.h"
#include "Utility/Stream.h"
//*****************************************************************************
//
//*****************************************************************************
namespace
{
const ECombinerInput CombinerInput32[ 32 ] =
{
CI_COMBINED, CI_TEXEL0, CI_TEXEL1, CI_PRIMITIVE, CI_SHADE, CI_ENV, CI_1, CI_COMBINED_ALPHA,
CI_TEXEL0_ALPHA, CI_TEXEL1_ALPHA, CI_PRIMITIVE_ALPHA, CI_SHADE_ALPHA, CI_ENV_ALPHA, CI_LOD_FRACTION, CI_PRIM_LOD_FRACTION, CI_K5,
CI_UNKNOWN, CI_UNKNOWN, CI_UNKNOWN, CI_UNKNOWN, CI_UNKNOWN, CI_UNKNOWN, CI_UNKNOWN, CI_UNKNOWN,
CI_UNKNOWN, CI_UNKNOWN, CI_UNKNOWN, CI_UNKNOWN, CI_UNKNOWN, CI_UNKNOWN, CI_UNKNOWN, CI_0,
};
const ECombinerInput CombinerInput16[ 16 ] =
{
CI_COMBINED, CI_TEXEL0, CI_TEXEL1, CI_PRIMITIVE, CI_SHADE, CI_ENV, CI_1, CI_COMBINED_ALPHA,
CI_TEXEL0_ALPHA, CI_TEXEL1_ALPHA, CI_PRIMITIVE_ALPHA, CI_SHADE_ALPHA, CI_ENV_ALPHA, CI_LOD_FRACTION, CI_PRIM_LOD_FRACTION, CI_0,
};
const ECombinerInput CombinerInput8[ 8 ] =
{
CI_COMBINED, CI_TEXEL0, CI_TEXEL1, CI_PRIMITIVE, CI_SHADE, CI_ENV, CI_1, CI_0,
};
const ECombinerInput CombinerInputAlphaC1_8[ 8 ] =
{
CI_LOD_FRACTION, CI_TEXEL0, CI_TEXEL1, CI_PRIMITIVE, CI_SHADE, CI_ENV, CI_1, CI_0,
};
const ECombinerInput CombinerInputAlphaC2_8[ 8 ] =
{
CI_COMBINED, CI_TEXEL0, CI_TEXEL1, CI_PRIMITIVE, CI_SHADE, CI_ENV, CI_1, CI_0,
};
enum EBuildConstantExpressionOptions
{
BCE_ALLOW_SHADE,
BCE_DISALLOW_SHADE,
};
static const CBlendConstantExpression * BuildConstantExpression( const CCombinerOperand * operand, EBuildConstantExpressionOptions options )
{
if(operand->IsInput())
{
const CCombinerInput * input( static_cast< const CCombinerInput * >( operand ) );
switch( input->GetInput() )
{
case CI_PRIMITIVE: return new CBlendConstantExpressionValue( BC_PRIMITIVE );
case CI_ENV: return new CBlendConstantExpressionValue( BC_ENVIRONMENT );
case CI_PRIMITIVE_ALPHA: return new CBlendConstantExpressionValue( BC_PRIMITIVE_ALPHA );
case CI_ENV_ALPHA: return new CBlendConstantExpressionValue( BC_ENVIRONMENT_ALPHA );
case CI_1: return new CBlendConstantExpressionValue( BC_1 );
case CI_0: return new CBlendConstantExpressionValue( BC_0 );
case CI_SHADE:
if( options == BCE_ALLOW_SHADE )
return new CBlendConstantExpressionValue( BC_SHADE );
else
return NULL;
default:
return NULL;
}
}
else if(operand->IsSum())
{
const CCombinerSum * sum( static_cast< const CCombinerSum * >( operand ) );
const CBlendConstantExpression * sum_expr( NULL );
for( u32 i = 0; i < sum->GetNumOperands(); ++i )
{
const CCombinerOperand * sum_term( sum->GetOperand( i ) );
const CBlendConstantExpression * lhs( sum_expr );
const CBlendConstantExpression * rhs( BuildConstantExpression( sum_term, options ) );
if( rhs == NULL )
{
delete sum_expr;
return NULL;
}
if( sum->IsTermNegated( i ) )
{
if( lhs == NULL )
{
lhs = new CBlendConstantExpressionValue( BC_0 );
}
sum_expr = new CBlendConstantExpressionSub( lhs, rhs );
}
else
{
if( lhs == NULL )
{
sum_expr = rhs;
}
else
{
sum_expr = new CBlendConstantExpressionAdd( lhs, rhs );
}
}
}
return sum_expr;
}
else if(operand->IsProduct())
{
const CCombinerProduct * product( static_cast< const CCombinerProduct * >( operand ) );
const CBlendConstantExpression * product_expr( NULL );
for( u32 i = 0; i < product->GetNumOperands(); ++i )
{
const CCombinerOperand * product_term( product->GetOperand( i ) );
const CBlendConstantExpression * lhs( product_expr );
const CBlendConstantExpression * rhs( BuildConstantExpression( product_term, options ) );
if( rhs == NULL )
{
delete product_expr;
return NULL;
}
if( lhs == NULL )
{
product_expr = rhs;
}
else
{
product_expr = new CBlendConstantExpressionMul( lhs, rhs );
}
}
return product_expr;
}
else
{
return NULL;
}
}
}
//*****************************************************************************
//
//*****************************************************************************
CCombinerTree::CCombinerTree( u64 mux, bool two_cycles )
: mMux( mux )
, mCycle1( NULL )
, mCycle1A( NULL )
, mCycle2( NULL )
, mCycle2A( NULL )
{
RDP_Combine m; m.mux = mux;
//fprintf(fh, "\n\t\tcase 0x%08x%08xLL:\n", mux0, mux1);
//fprintf(fh, "\t\t//aRGB0: (%s - %s) * %s + %s\n", sc_colcombtypes16[aRGB0], sc_colcombtypes16[bRGB0], sc_colcombtypes32[cRGB0], sc_colcombtypes8[dRGB0]);
//fprintf(fh, "\t\t//aA0 : (%s - %s) * %s + %s\n", sc_colcombtypes8[aA0], sc_colcombtypes8[bA0], sc_colcombtypes8[cA0], sc_colcombtypes8[dA0]);
//fprintf(fh, "\t\t//aRGB1: (%s - %s) * %s + %s\n", sc_colcombtypes16[aRGB1], sc_colcombtypes16[bRGB1], sc_colcombtypes32[cRGB1], sc_colcombtypes8[dRGB1]);
//fprintf(fh, "\t\t//aA1 : (%s - %s) * %s + %s\n", sc_colcombtypes8[aA1], sc_colcombtypes8[bA1], sc_colcombtypes8[cA1], sc_colcombtypes8[dA1]);
mCycle1 = BuildCycle1( CombinerInput16[m.aRGB0], CombinerInput16[m.bRGB0], CombinerInput32[m.cRGB0], CombinerInput8[m.dRGB0] );
mCycle1A = BuildCycle1( CombinerInputAlphaC1_8[m.aA0], CombinerInputAlphaC1_8[m.bA0], CombinerInputAlphaC1_8[m.cA0], CombinerInputAlphaC1_8[m.dA0] );
if( two_cycles )
{
mCycle2 = BuildCycle2( CombinerInput16[m.aRGB1], CombinerInput16[m.bRGB1], CombinerInput32[m.cRGB1], CombinerInput8[m.dRGB1], mCycle1 );
mCycle2A = BuildCycle2( CombinerInputAlphaC2_8[m.aA1], CombinerInputAlphaC2_8[m.bA1], CombinerInputAlphaC2_8[m.cA1], CombinerInputAlphaC2_8[m.dA1], mCycle1A );
mBlendStates = GenerateBlendStates( mCycle2, mCycle2A );
}
else
{
mBlendStates = GenerateBlendStates( mCycle1, mCycle1A );
}
}
//*****************************************************************************
//
//*****************************************************************************
CCombinerTree::~CCombinerTree()
{
delete mCycle1;
delete mCycle1A;
delete mCycle2;
delete mCycle2A;
}
//*****************************************************************************
//
//*****************************************************************************
CCombinerOperand * CCombinerTree::BuildCycle1( ECombinerInput a, ECombinerInput b, ECombinerInput c, ECombinerInput d )
{
CCombinerOperand * input_a( new CCombinerInput( a ) );
CCombinerOperand * input_b( new CCombinerInput( b ) );
CCombinerOperand * input_c( new CCombinerInput( c ) );
CCombinerOperand * input_d( new CCombinerInput( d ) );
return Build( input_a, input_b, input_c, input_d );
}
//*****************************************************************************
//
//*****************************************************************************
CCombinerOperand * CCombinerTree::BuildCycle2( ECombinerInput a, ECombinerInput b, ECombinerInput c, ECombinerInput d, const CCombinerOperand * cycle_1_output )
{
CCombinerOperand * input_a( a == CI_COMBINED ? cycle_1_output->Clone() : new CCombinerInput( a ) );
CCombinerOperand * input_b( b == CI_COMBINED ? cycle_1_output->Clone() : new CCombinerInput( b ) );
CCombinerOperand * input_c( c == CI_COMBINED ? cycle_1_output->Clone() : new CCombinerInput( c ) );
CCombinerOperand * input_d( d == CI_COMBINED ? cycle_1_output->Clone() : new CCombinerInput( d ) );
return Build( input_a, input_b, input_c, input_d );
}
//*****************************************************************************
// Build an expression of the form output = (A-B)*C + D, and simplify.
//*****************************************************************************
CCombinerOperand * CCombinerTree::Build( CCombinerOperand * a, CCombinerOperand * b, CCombinerOperand * c, CCombinerOperand * d )
{
CCombinerSum * sum( new CCombinerSum( NULL ) );
sum->Add( a );
sum->Sub( b );
CCombinerProduct * product( new CCombinerProduct( sum ) );
product->Mul( c );
CCombinerSum * output( new CCombinerSum( product ) );
output->Add( d );
return Simplify( output );
}
//*****************************************************************************
//
//*****************************************************************************
COutputStream & CCombinerTree::Stream( COutputStream & stream ) const
{
stream << "RGB: "; mCycle2->Stream( stream ); stream << "\n";
stream << "Alpha: "; mCycle2A->Stream( stream ); stream << "\n";
return stream;
}
//*****************************************************************************
//
//*****************************************************************************
CBlendStates * CCombinerTree::GenerateBlendStates( const CCombinerOperand * colour_operand, const CCombinerOperand * alpha_operand ) const
{
CBlendStates * states( new CBlendStates );
states->SetAlphaSettings( GenerateAlphaRenderSettings( alpha_operand ) );
GenerateRenderSettings( states, colour_operand );
return states;
}
//*****************************************************************************
//
//*****************************************************************************
namespace
{
void ApplyAlphaModulateTerm( CAlphaRenderSettings * settings, const CCombinerOperand * operand )
{
if( operand->IsInput() )
{
const CCombinerInput * input( static_cast< const CCombinerInput * >( operand ) );
switch( input->GetInput() )
{
case CI_TEXEL0:
settings->AddTermTexel0();
break;
case CI_TEXEL1:
settings->AddTermTexel1();
break;
case CI_SHADE:
settings->AddTermConstant( new CBlendConstantExpressionValue( BC_SHADE ) );
break;
case CI_PRIMITIVE:
settings->AddTermConstant( new CBlendConstantExpressionValue( BC_PRIMITIVE ) );
break;
case CI_ENV:
settings->AddTermConstant( new CBlendConstantExpressionValue( BC_ENVIRONMENT ) );
break;
case CI_PRIMITIVE_ALPHA:
settings->AddTermConstant( new CBlendConstantExpressionValue( BC_PRIMITIVE_ALPHA ) );
break;
case CI_ENV_ALPHA:
settings->AddTermConstant( new CBlendConstantExpressionValue( BC_ENVIRONMENT_ALPHA ) );
break;
case CI_0:
settings->AddTermConstant( new CBlendConstantExpressionValue( BC_0 ) );
break;
case CI_1:
settings->AddTermConstant( new CBlendConstantExpressionValue( BC_1 ) );
break;
default:
#ifdef DAEDALUS_DEBUG_DISPLAYLIST
printf( "Unhandled Alpha Input: %s\n", GetCombinerInputName( input->GetInput() ) );
#endif
settings->SetInexact();
break;
}
}
else
{
//
// Try to reduce to a constant term, and add that
//
const CBlendConstantExpression * constant_expression( BuildConstantExpression( operand, BCE_ALLOW_SHADE ) );
if( constant_expression != NULL )
{
settings->AddTermConstant( constant_expression );
}
else
{
#ifdef DAEDALUS_DEBUG_DISPLAYLIST
COutputStringStream str;
operand->Stream( str );
printf( "\n********************************\n" );
printf( "Unhandled alpha - not a simple term: %s\n", str.c_str() );
printf( "********************************\n\n" );
#endif
settings->SetInexact();
}
}
}
}
//*****************************************************************************
//
//*****************************************************************************
CAlphaRenderSettings * CCombinerTree::GenerateAlphaRenderSettings( const CCombinerOperand * operand ) const
{
COutputStringStream str;
operand->Stream( str );
CAlphaRenderSettings * settings( new CAlphaRenderSettings( str.c_str() ) );
if(operand->IsProduct())
{
const CCombinerProduct * product( static_cast< const CCombinerProduct * >( operand ) );
for( u32 i = 0; i < product->GetNumOperands(); ++i )
{
ApplyAlphaModulateTerm( settings, product->GetOperand( i ) );
}
}
else
{
ApplyAlphaModulateTerm( settings, operand );
}
settings->Finalise();
return settings;
}
//*****************************************************************************
//
//*****************************************************************************
namespace
{
void ApplyModulateTerm( CRenderSettingsModulate * settings, const CCombinerOperand * operand )
{
if( operand->IsInput() )
{
const CCombinerInput * input( static_cast< const CCombinerInput * >( operand ) );
switch( input->GetInput() )
{
case CI_TEXEL0:
settings->AddTermTexel0();
break;
case CI_TEXEL1:
settings->AddTermTexel1();
break;
case CI_SHADE:
settings->AddTermConstant( new CBlendConstantExpressionValue( BC_SHADE ) );
break;
case CI_PRIMITIVE:
settings->AddTermConstant( new CBlendConstantExpressionValue( BC_PRIMITIVE ) );
break;
case CI_ENV:
settings->AddTermConstant( new CBlendConstantExpressionValue( BC_ENVIRONMENT ) );
break;
case CI_PRIMITIVE_ALPHA:
settings->AddTermConstant( new CBlendConstantExpressionValue( BC_PRIMITIVE_ALPHA ) );
break;
case CI_ENV_ALPHA:
settings->AddTermConstant( new CBlendConstantExpressionValue( BC_ENVIRONMENT_ALPHA ) );
break;
case CI_0:
settings->AddTermConstant( new CBlendConstantExpressionValue( BC_0 ) );
break;
case CI_1:
settings->AddTermConstant( new CBlendConstantExpressionValue( BC_1 ) );
break;
default:
#ifdef DAEDALUS_DEBUG_DISPLAYLIST
printf( "Unhandled Input: %s\n", GetCombinerInputName( input->GetInput() ) );
#endif
settings->SetInexact();
break;
}
}
else
{
//
// Try to reduce to a constant term, and add that
//
const CBlendConstantExpression * constant_expression( BuildConstantExpression( operand, BCE_ALLOW_SHADE ) );
if( constant_expression != NULL )
{
settings->AddTermConstant( constant_expression );
}
else
{
#ifdef DAEDALUS_DEBUG_DISPLAYLIST
COutputStringStream str;
operand->Stream( str );
printf( "\n********************************\n" );
printf( "Unhandled rgb - not a simple term: %s\n", str.c_str() );
printf( "********************************\n\n" );
#endif
settings->SetInexact();
}
}
}
}
//*****************************************************************************
//
//*****************************************************************************
void CCombinerTree::GenerateRenderSettings( CBlendStates * states, const CCombinerOperand * operand ) const
{
if(operand->IsInput())
{
COutputStringStream str;
operand->Stream( str );
CRenderSettingsModulate * settings( new CRenderSettingsModulate( str.c_str() ) );
ApplyModulateTerm( settings, operand );
settings->Finalise();
states->AddColourSettings( settings );
}
else if(operand->IsSum())
{
const CCombinerSum * sum( static_cast< const CCombinerSum * >( operand ) );
for( u32 i = 0; i < sum->GetNumOperands(); ++i )
{
const CCombinerOperand * sum_term( sum->GetOperand( i ) );
// Recurse
if( sum->IsTermNegated( i ) )
{
printf( "Negative term!!\n" );
COutputStringStream str;
str << "- ";
sum->Stream( str );
states->AddColourSettings( new CRenderSettingsInvalid( str.c_str() ) );
}
else
{
GenerateRenderSettings( states, sum_term );
}
}
}
else if(operand->IsProduct())
{
const CCombinerProduct * product( static_cast< const CCombinerProduct * >( operand ) );
COutputStringStream str;
product->Stream( str );
CRenderSettingsModulate * settings( new CRenderSettingsModulate( str.c_str() ) );
for( u32 i = 0; i < product->GetNumOperands(); ++i )
{
ApplyModulateTerm( settings, product->GetOperand( i ) );
}
settings->Finalise();
states->AddColourSettings( settings );
}
else if(operand->IsBlend())
{
const CCombinerBlend * blend( static_cast< const CCombinerBlend * >( operand ) );
const CCombinerOperand * operand_a( blend->GetInputA() ); // Needs to be a constant factor /w shade
const CCombinerOperand * operand_b( blend->GetInputB() ); // Needs to be a constant factor
const CCombinerOperand * operand_f( blend->GetInputF() );
COutputStringStream str;
blend->Stream( str );
bool handled( false );
if( operand_f->IsInput( CI_TEXEL0 ) )
{
const CBlendConstantExpression * expr_a( BuildConstantExpression( operand_a, BCE_ALLOW_SHADE ) );
const CBlendConstantExpression * expr_b( BuildConstantExpression( operand_b, BCE_DISALLOW_SHADE ) );
if( expr_a != NULL && expr_b != NULL )
{
states->AddColourSettings( new CRenderSettingsBlend( str.c_str(), expr_a, expr_b ) );
handled = true;
}
else
{
delete expr_a;
delete expr_b;
}
}
else
{
const CBlendConstantExpression * expr_a( BuildConstantExpression( operand_a, BCE_ALLOW_SHADE ) );
const CBlendConstantExpression * expr_b( BuildConstantExpression( operand_b, BCE_ALLOW_SHADE ) );
const CBlendConstantExpression * expr_f( BuildConstantExpression( operand_f, BCE_ALLOW_SHADE ) );
if( expr_a != NULL && expr_b != NULL && expr_f != NULL )
{
const CBlendConstantExpressionBlend * expr_blend( new CBlendConstantExpressionBlend( expr_a, expr_b, expr_f ) );
CRenderSettingsModulate * settings( new CRenderSettingsModulate( str.c_str() ) );
settings->AddTermConstant( expr_blend );
settings->Finalise();
states->AddColourSettings( settings );
handled = true;
}
else
{
delete expr_a;
delete expr_b;
delete expr_f;
}
}
if( !handled )
{
#ifdef DAEDALUS_DEBUG_DISPLAYLIST
printf( "CANNOT BLEND!\n" );
#endif
states->AddColourSettings( new CRenderSettingsInvalid( str.c_str() ) );
}
}
else
{
COutputStringStream str;
operand->Stream( str );
#ifdef DAEDALUS_DEBUG_DISPLAYLIST
printf( "\n********************************\n" );
printf( "Unhandled - inner operand is not an input/product/sum: %s\n", str.c_str() );
printf( "********************************\n\n" );
#endif
states->AddColourSettings( new CRenderSettingsInvalid( str.c_str() ) );
}
}
//*****************************************************************************
//
//*****************************************************************************
CCombinerOperand * CCombinerTree::Simplify( CCombinerOperand * operand )
{
bool did_something;
do
{
CCombinerOperand * new_tree( operand->SimplifyAndReduce() );
did_something = !new_tree->IsEqual( *operand );
delete operand;
operand = new_tree;
}
while( did_something );
return operand;
}