Emmanuel BENOîT
68d01ca42e
Shaders are no longer found under /shaders in the project; they can be anywhere. In addition, paths for both (program) instructions in the script and include directives in shader source code are relative to the file which contains them.
967 lines
28 KiB
C++
967 lines
28 KiB
C++
#include "externals.hh"
|
|
|
|
#include "common.hh"
|
|
#include "c-project.hh"
|
|
#include "c-ops.hh"
|
|
#include "c-opcomp.hh"
|
|
|
|
#include <ebcl/Algorithms.hh>
|
|
|
|
using namespace ebcl;
|
|
using namespace ops;
|
|
using namespace opast;
|
|
|
|
|
|
#define M_LOGSTR_( S , L ) \
|
|
logger( [](){ return T_StringBuilder{ S }; } , L )
|
|
|
|
|
|
namespace {
|
|
|
|
struct T_CompilerImpl_
|
|
{
|
|
T_CompilerImpl_( F_OPLogger* const logger ,
|
|
const bool noUIInstructions ) noexcept
|
|
: logger( *logger ) , noUIInstructions( noUIInstructions )
|
|
{}
|
|
|
|
P_OpProgram compile( T_OpsParserOutput const& input ) noexcept;
|
|
|
|
private:
|
|
F_OPLogger& logger;
|
|
const bool noUIInstructions;
|
|
|
|
T_Visitor< A_Node > astVisitor{ ASTVisitorBrowser };
|
|
T_Set< uint32_t > constants{ UseTag< IndexBacked< > >( ) };
|
|
T_KeyValueTable< T_String , uint32_t > locations;
|
|
|
|
T_OpsParserOutput* input;
|
|
P_OpProgram output;
|
|
|
|
uint32_t fiVariables , fiFramebuffers ,
|
|
fiPipelines , fiPrograms ,
|
|
fiSamplers , fiTextures;
|
|
|
|
uint32_t sdMain , sdFPU;
|
|
|
|
/*
|
|
* Conditionals look something like this:
|
|
*
|
|
* 01 { compute expression and push it }
|
|
* 02 skip-cond value-for-case-1 2 { 5 - 2 - 1 }
|
|
* 03 { case 1 code }
|
|
* 04 skip 6 { 11 - 4 - 1 }
|
|
* 05 skip-cond value-for-case-2 3 { 9 - 5 - 1 }
|
|
* 06 { case 2 code }
|
|
* 07 { case 2 code }
|
|
* 08 skip 2 { 11 - 8 - 1 }
|
|
* 09 { default case code }
|
|
* 10 { default case code }
|
|
* 11 pop 1
|
|
*
|
|
* So, in order to adjust the skips once we know about them,
|
|
* we need to store the location of the previous skip-cond,
|
|
* and the locations of all end-of-case skips.
|
|
*/
|
|
struct T_CondInfo_ {
|
|
uint32_t prevCase;
|
|
T_AutoArray< uint32_t , 16 > caseEnds;
|
|
};
|
|
T_AutoArray< T_CondInfo_ , 16 > condJumps;
|
|
|
|
uint32_t fbCurrent; // Current framebuffer attachment
|
|
|
|
void gatherConstants( ) noexcept;
|
|
void countAssets( ) noexcept;
|
|
|
|
bool compileNode( uint32_t funcIndex ,
|
|
A_Node& node ,
|
|
bool exit ) noexcept;
|
|
void processFunction(
|
|
bool exit ,
|
|
uint32_t args ,
|
|
uint32_t lvars ,
|
|
T_SRDLocation const& location ) noexcept;
|
|
|
|
bool processIdentifier(
|
|
uint32_t funcIndex ,
|
|
T_IdentifierExprNode const& node ) noexcept;
|
|
bool processIdentifier(
|
|
uint32_t funcIndex ,
|
|
T_String const& id ,
|
|
T_SRDLocation const& location ) noexcept;
|
|
|
|
void addInstruction(
|
|
E_OpType op ,
|
|
T_SRDLocation const& location ) noexcept;
|
|
void addInstruction(
|
|
E_OpType op ,
|
|
uint32_t arg0 ,
|
|
T_SRDLocation const& location ) noexcept;
|
|
void addInstruction(
|
|
E_OpType op ,
|
|
std::initializer_list< uint32_t > args ,
|
|
T_SRDLocation const& location ) noexcept;
|
|
void applyStackEffects(
|
|
T_Op const& op ) noexcept;
|
|
};
|
|
|
|
|
|
P_OpProgram T_CompilerImpl_::compile(
|
|
T_OpsParserOutput const& in ) noexcept
|
|
{
|
|
input = const_cast< T_OpsParserOutput* >( &in );
|
|
output = NewOwned< T_OpProgram >( );
|
|
|
|
// Gather all constants used in expressions, count resources
|
|
gatherConstants( );
|
|
countAssets( );
|
|
|
|
// Get function indices
|
|
// FIXME ideally we should remap functions so that init is always 0
|
|
// and frame is always 1
|
|
output->init = input->root.functionIndex( "*init*" );
|
|
output->frame = input->root.functionIndex( "*frame*" );
|
|
logger( [this]( ) {
|
|
T_StringBuilder sb;
|
|
sb << "Function indices\n\tinit: " << output->init
|
|
<< "\n\tframe: " << output->frame;
|
|
return sb;
|
|
} , 3 );
|
|
|
|
// Compile each function
|
|
uint32_t nInstr = 0;
|
|
uint32_t cfi;
|
|
for ( cfi = 0u ; cfi < input->root.nFunctions( ) ; cfi ++ ) {
|
|
output->ops.next( );
|
|
auto& func( input->root.function( cfi ) );
|
|
logger( [&]( ) {
|
|
T_StringBuilder sb;
|
|
sb << "... Compiling function " << func.name( );
|
|
return sb;
|
|
} , 3 );
|
|
|
|
sdMain = sdFPU = 0;
|
|
astVisitor.visit( func ,
|
|
[=]( A_Node& node , const bool exit ) -> bool {
|
|
return compileNode( cfi , node , exit );
|
|
} );
|
|
|
|
logger( [&]( ) {
|
|
T_StringBuilder sb , temp;
|
|
sb << "...... Compiled function " << func.name( );
|
|
for ( auto i = 0u ; i < output->ops.sizeOf( cfi ) ; i ++ ) {
|
|
temp << "(" << output->ops.get( cfi , i ) << ")";
|
|
while ( temp.length( ) < 30 ) {
|
|
temp << ' ';
|
|
}
|
|
sb << "\n\t\t" << temp << "{ "
|
|
<< output->ops.get( cfi , i ).location
|
|
<< " }";
|
|
temp.clear( );
|
|
}
|
|
return sb;
|
|
} , 6 );
|
|
logger( [&]( ) {
|
|
T_StringBuilder sb;
|
|
sb << "...... " << output->ops.sizeOf( cfi )
|
|
<< " instructions";
|
|
return sb;
|
|
} , 3 );
|
|
nInstr += output->ops.sizeOf( cfi );
|
|
}
|
|
logger( [=]( ) {
|
|
T_StringBuilder sb;
|
|
sb << "... Generated " << nInstr << " instructions";
|
|
return sb;
|
|
} , 2 );
|
|
|
|
return std::move( output );
|
|
}
|
|
|
|
void T_CompilerImpl_::gatherConstants( ) noexcept
|
|
{
|
|
M_LOGSTR_( "... Gathering constants" , 2 );
|
|
constants.clear( );
|
|
astVisitor.visit( input->root , [&]( A_Node& node , const bool exit ) {
|
|
if ( exit ) {
|
|
return true;
|
|
}
|
|
|
|
T_OpValue value;
|
|
if ( node.type( ) == A_Node::EXPR_CONST ) {
|
|
value.f = dynamic_cast< T_ConstantExprNode& >( node ).floatValue( );
|
|
} else if ( node.type( ) == A_Node::OP_INPUT ) {
|
|
value.f = dynamic_cast< T_InputInstrNode& >( node ).defValue( );
|
|
} else {
|
|
return true;
|
|
}
|
|
constants.add( value.u );
|
|
return true;
|
|
} );
|
|
for ( auto i = 0u ; i < constants.size( ) ; i ++ ) {
|
|
output->constants.add( constants[ i ] );
|
|
}
|
|
|
|
logger( [&]() {
|
|
T_StringBuilder sb;
|
|
char temp[ 9 ];
|
|
sb << constants.size( ) << " constants\n\t";
|
|
for ( auto i = 0u ; i < constants.size( ) ; i ++ ) {
|
|
snprintf( temp , 9 , "%08x" , constants[ i ] );
|
|
sb << " " << temp;
|
|
if ( i % 4 == 3 && i != constants.size( ) - 1 ) {
|
|
sb << "\n\t";
|
|
}
|
|
}
|
|
return sb;
|
|
} , 3 );
|
|
}
|
|
|
|
void T_CompilerImpl_::countAssets( ) noexcept
|
|
{
|
|
M_LOGSTR_( "... Counting resources" , 2 );
|
|
locations.clear( );
|
|
locations.add( T_String::Pooled( "time" ) , 0u );
|
|
locations.add( T_String::Pooled( "width" ) , 1u );
|
|
locations.add( T_String::Pooled( "height" ) , 2u );
|
|
|
|
auto const nt{ input->types.size( ) };
|
|
for ( auto i = 0u ; i < nt ; i ++ ) {
|
|
const auto t{ input->types.values( )[ i ] };
|
|
auto const& n{ input->types.keys( )[ i ] };
|
|
switch ( t ) {
|
|
case E_DataType::FRAMEBUFFER:
|
|
locations.add( n , output->nFramebuffers );
|
|
output->nFramebuffers ++;
|
|
break;
|
|
case E_DataType::PIPELINE:
|
|
locations.add( n , output->nPipelines );
|
|
output->nPipelines ++;
|
|
break;
|
|
case E_DataType::PROGRAM:
|
|
locations.add( n , output->nPrograms );
|
|
output->nPrograms ++;
|
|
break;
|
|
case E_DataType::SAMPLER:
|
|
locations.add( n , output->nSamplers );
|
|
output->nSamplers ++;
|
|
break;
|
|
case E_DataType::TEXTURE:
|
|
locations.add( n , output->nTextures );
|
|
output->nTextures ++;
|
|
break;
|
|
case E_DataType::VARIABLE:
|
|
locations.add( n , output->nVariables + 3 );
|
|
output->nVariables ++;
|
|
break;
|
|
|
|
case E_DataType::INPUT:
|
|
assert( !output->inputs.contains(
|
|
input->types.keys( )[ i ] ) );
|
|
output->inputs.add( input->types.keys( )[ i ] );
|
|
break;
|
|
|
|
case E_DataType::BUILTIN:
|
|
case E_DataType::UNKNOWN:
|
|
break;
|
|
}
|
|
}
|
|
|
|
fiVariables = 3 + constants.size( );
|
|
fiFramebuffers = fiVariables + output->nVariables;
|
|
fiPipelines = fiFramebuffers + output->nFramebuffers;
|
|
fiPrograms = fiPipelines + output->nPipelines;
|
|
fiSamplers = fiPrograms + output->nPrograms;
|
|
fiTextures = fiSamplers + output->nSamplers;
|
|
|
|
for ( auto i = 0u ; i < nt ; i ++ ) {
|
|
const auto li{ locations.indexOf( input->types.keys( )[ i ] ) };
|
|
if ( li == T_HashIndex::INVALID_INDEX ) {
|
|
continue;
|
|
}
|
|
|
|
const auto t{ input->types.values( )[ i ] };
|
|
auto& pos{ locations[ li ] };
|
|
switch ( t ) {
|
|
case E_DataType::FRAMEBUFFER: pos += fiFramebuffers; break;
|
|
case E_DataType::PIPELINE: pos += fiPipelines; break;
|
|
case E_DataType::PROGRAM: pos += fiPrograms; break;
|
|
case E_DataType::SAMPLER: pos += fiSamplers; break;
|
|
case E_DataType::TEXTURE: pos += fiTextures; break;
|
|
|
|
case E_DataType::VARIABLE:
|
|
case E_DataType::INPUT:
|
|
case E_DataType::BUILTIN:
|
|
case E_DataType::UNKNOWN:
|
|
break;
|
|
}
|
|
}
|
|
|
|
logger( [&]() {
|
|
T_StringBuilder sb;
|
|
sb << "... Resource counts:\n"
|
|
<< "\t\tFramebuffers:\t" << output->nFramebuffers << '\n'
|
|
<< "\t\tPipelines: \t" << output->nPipelines << '\n'
|
|
<< "\t\tPrograms: \t" << output->nPrograms << '\n'
|
|
<< "\t\tSamplers: \t" << output->nSamplers << '\n'
|
|
<< "\t\tTextures: \t" << output->nTextures << '\n'
|
|
<< "\t\tInputs: \t" << output->inputs.size( ) << '\n'
|
|
<< "\tTable ranges:\n"
|
|
<< "\t\tBuilt-ins \t0\t2\n"
|
|
<< "\t\tConstants \t3\t" << fiVariables - 1 << '\n'
|
|
<< "\t\tVariables \t" << fiVariables << '\t' << fiFramebuffers - 1 << '\n'
|
|
<< "\t\tFramebuffers \t" << fiFramebuffers << '\t' << fiPipelines - 1 << '\n'
|
|
<< "\t\tPipelines \t" << fiPipelines << '\t' << fiPrograms - 1 << '\n'
|
|
<< "\t\tPrograms \t" << fiPrograms << '\t' << fiSamplers - 1 << '\n'
|
|
<< "\t\tSamplers \t" << fiSamplers << '\t' << fiTextures - 1 << '\n'
|
|
<< "\t\tTextures \t" << fiTextures << '\t' << fiTextures + output->nTextures - 1
|
|
;
|
|
return sb;
|
|
} , 3 );
|
|
logger( [&]() {
|
|
T_StringBuilder sb;
|
|
T_Array< uint32_t > indices( locations.size( ) );
|
|
indices.ensureCapacity( locations.size( ) );
|
|
for ( auto i = 0u ; i < locations.size( ) ; i ++ ) {
|
|
indices.add( i );
|
|
}
|
|
indices.sort( [this]( uint32_t a , uint32_t b ) {
|
|
return T_Comparator< uint32_t >::compare(
|
|
locations.values( )[ a ] ,
|
|
locations.values( )[ b ] );
|
|
} );
|
|
sb << "... Location map (constants not included)";
|
|
for ( auto idx : indices ) {
|
|
sb << "\n\t" << locations.values( )[ idx ]
|
|
<< '\t' << locations.keys( )[ idx ];
|
|
}
|
|
return sb;
|
|
} , 4 );
|
|
}
|
|
|
|
bool T_CompilerImpl_::compileNode(
|
|
const uint32_t funcIndex ,
|
|
A_Node& node ,
|
|
const bool exit ) noexcept
|
|
{
|
|
switch ( node.type( ) ) {
|
|
|
|
//- PROGRAM STRUCTURE -----------------------------------------------------------------
|
|
|
|
case A_Node::ROOT:
|
|
fprintf( stderr , "Internal error: root node found during compilation\n" );
|
|
std::abort( );
|
|
break;
|
|
|
|
case A_Node::ILIST: break;
|
|
case A_Node::OP_LOCALS: break;
|
|
|
|
case A_Node::DECL_FN:
|
|
{
|
|
T_FuncNode& fn( (T_FuncNode&) node );
|
|
const auto args( fn.arguments( ) );
|
|
processFunction( exit , args , fn.locals( ) - args , node.location( ) );
|
|
break;
|
|
}
|
|
|
|
case A_Node::DECL_INIT:
|
|
{
|
|
A_FuncNode& fn( (A_FuncNode&) node );
|
|
processFunction( exit , 0 , fn.locals( ) , node.location( ) );
|
|
if ( output->nFramebuffers ) {
|
|
addInstruction( OP_OFFSET , fiFramebuffers , node.location( ) );
|
|
addInstruction( OP_GEN_ASSETS , { 0 , output->nFramebuffers } , node.location( ) );
|
|
}
|
|
if ( output->nPipelines ) {
|
|
addInstruction( OP_OFFSET , fiPipelines , node.location( ) );
|
|
addInstruction( OP_GEN_ASSETS , { 1 , output->nPipelines } , node.location( ) );
|
|
}
|
|
if ( output->nSamplers ) {
|
|
addInstruction( OP_OFFSET , fiSamplers , node.location( ) );
|
|
addInstruction( OP_GEN_ASSETS , { 2 , output->nSamplers } , node.location( ) );
|
|
}
|
|
if ( output->nTextures ) {
|
|
addInstruction( OP_OFFSET , fiTextures , node.location( ) );
|
|
addInstruction( OP_GEN_ASSETS , { 3 , output->nTextures } , node.location( ) );
|
|
}
|
|
break;
|
|
}
|
|
|
|
case A_Node::DECL_FRAME:
|
|
{
|
|
A_FuncNode& fn( (A_FuncNode&) node );
|
|
processFunction( exit , 0 , fn.locals( ) , node.location( ) );
|
|
break;
|
|
}
|
|
|
|
|
|
//- GENERAL / FLOW CONTROL INSTRUCTIONS -----------------------------------------------
|
|
|
|
case A_Node::OP_CALL:
|
|
if ( exit ) {
|
|
auto& call( (T_CallInstrNode&) node );
|
|
const auto fi( input->root.functionIndex( call.id( ) ) );
|
|
assert( fi >= 0 );
|
|
auto& callee( input->root.function( fi ) );
|
|
assert( callee.type( ) == A_Node::DECL_FN );
|
|
auto& fcallee( (T_FuncNode&) callee );
|
|
const auto args( fcallee.arguments( ) );
|
|
assert( sdMain > args );
|
|
addInstruction( OP_CALL , fi , node.location( ) );
|
|
sdMain -= args;
|
|
}
|
|
break;
|
|
|
|
case A_Node::OP_SET:
|
|
if ( exit ) {
|
|
auto& id( ((T_SetInstrNode&)node).id( ) );
|
|
auto& func{ input->root.function( funcIndex ) };
|
|
if ( func.hasLocal( id ) ) {
|
|
const auto pos( func.getLocalIndex( id ) + 1 );
|
|
addInstruction( OP_FP_SSTORE , sdMain - pos - 1 ,
|
|
node.location( ) );
|
|
} else {
|
|
addInstruction( OP_FP_STORE ,
|
|
*locations.get( ((T_SetInstrNode&)node).id( ) ) ,
|
|
node.location( ) );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case A_Node::OP_COND:
|
|
if ( exit ) {
|
|
// Update all skips
|
|
const auto cpos( output->ops.sizeOf( funcIndex ) );
|
|
auto const& skips( condJumps.last( ).caseEnds );
|
|
for ( auto i = 0u ; i < skips.size( ) ; i ++ ) {
|
|
const auto skip( skips[ i ] );
|
|
assert( output->ops.get( funcIndex , skip ).op == OP_SKIP );
|
|
output->ops.get( funcIndex , skip ).args[ 0 ] = ( cpos - skip - 1 );
|
|
}
|
|
|
|
addInstruction( OP_POP , 0 , node.location( ) );
|
|
sdMain --;
|
|
condJumps.removeLast( );
|
|
} else {
|
|
condJumps.addNew( );
|
|
}
|
|
break;
|
|
case A_Node::TN_CONDITION:
|
|
if ( exit ) {
|
|
addInstruction( OP_PUSH , node.location( ) );
|
|
addInstruction( OP_FP_SSTORE_INT , 0 , node.location( ) );
|
|
}
|
|
break;
|
|
case A_Node::TN_CASE:
|
|
if ( exit ) {
|
|
// Store a potential skip location
|
|
condJumps.last( ).caseEnds.add( output->ops.sizeOf( funcIndex ) );
|
|
|
|
// Update the initial skip
|
|
const auto cpos( output->ops.sizeOf( funcIndex ) );
|
|
const auto ppos( condJumps.last( ).prevCase );
|
|
const auto diff( cpos - ppos );
|
|
output->ops.get( funcIndex , ppos ).args[ 0 ] = diff - 1;
|
|
} else {
|
|
// If there is a previous skip location, insert the skip instruction
|
|
if ( !condJumps.last( ).caseEnds.empty( ) ) {
|
|
addInstruction( OP_SKIP , 0 , node.location( ) );
|
|
const auto ppos( condJumps.last( ).prevCase );
|
|
assert( output->ops.get( funcIndex , ppos ).op == OP_COND_SKIP );
|
|
output->ops.get( funcIndex , ppos ).args[ 0 ] ++;
|
|
}
|
|
|
|
// Add the conditional skip
|
|
auto& c( (T_CondInstrNode::T_ValuedCase&) node );
|
|
condJumps.last( ).prevCase = output->ops.sizeOf( funcIndex );
|
|
addInstruction( OP_COND_SKIP , { 0 , uint32_t( c.value( ) ) } ,
|
|
node.location( ) );
|
|
}
|
|
break;
|
|
case A_Node::TN_DEFAULT:
|
|
// If there is a previous skip location, insert the skip instruction
|
|
// and update the previous conditional skip
|
|
if ( !( exit || condJumps.last( ).caseEnds.empty( ) ) ) {
|
|
addInstruction( OP_SKIP , 0 , node.location( ) );
|
|
const auto ppos( condJumps.last( ).prevCase );
|
|
assert( output->ops.get( funcIndex , ppos ).op == OP_COND_SKIP );
|
|
output->ops.get( funcIndex , ppos ).args[ 0 ] ++;
|
|
}
|
|
break;
|
|
|
|
|
|
//- ASSET DECLARATIONS ----------------------------------------------------------------
|
|
|
|
case A_Node::OP_PIPELINE:
|
|
if ( exit ) {
|
|
auto& pn( (T_PipelineInstrNode&) node );
|
|
for ( auto i = 0u ; i < pn.size( ) ; i ++ ) {
|
|
processIdentifier( funcIndex , pn.program( i ) ,
|
|
pn.pLocation( i ) );
|
|
}
|
|
processIdentifier( funcIndex , pn.id( ) , pn.location( ) );
|
|
addInstruction( OP_INIT_PIPELINE , pn.size( ) - 1 , pn.location( ) );
|
|
sdMain -= pn.size( );
|
|
}
|
|
break;
|
|
|
|
case A_Node::OP_PROGRAM:
|
|
if ( exit ) {
|
|
auto& pn( (T_ProgramInstrNode&) node );
|
|
const auto p{ pn.path( ).makeRelative( Common::Project( ).basePath( ) ) };
|
|
if ( !output->progNames.contains( p ) ) {
|
|
output->progNames.add( p );
|
|
}
|
|
assert( locations.contains( pn.id( ) ) );
|
|
|
|
addInstruction( OP_INIT_PROGRAM , output->progNames.indexOf( p ) ,
|
|
pn.location( ) );
|
|
addInstruction( OP_STORE , *locations.get( pn.id( ) ) , pn.idLocation( ) );
|
|
}
|
|
break;
|
|
|
|
case A_Node::OP_SAMPLER:
|
|
if ( exit ) {
|
|
auto& sn( (T_SamplerInstrNode&) node );
|
|
if ( !sn.minLod( ) ) {
|
|
// XXX maybe use OP_RES_STACK
|
|
addInstruction( OP_CONST , 0 , node.location( ) );
|
|
addInstruction( OP_PUSH , node.location( ) );
|
|
addInstruction( OP_PUSH , node.location( ) );
|
|
}
|
|
processIdentifier( funcIndex , sn.id( ) , sn.idLocation( ) );
|
|
addInstruction( OP_INIT_SAMPLER , {
|
|
( uint32_t( sn.sampling( ) ) << 2 )
|
|
| ( ( sn.mipmap( ) ? 1 : 0 ) << 1 )
|
|
| ( sn.mipmap( ) ? uint32_t( *sn.mipmap( ) ) : 0 ) ,
|
|
uint32_t( sn.wrapping( ) )
|
|
} , node.location( ) );
|
|
}
|
|
break;
|
|
|
|
case A_Node::OP_TEXTURE:
|
|
if ( exit ) {
|
|
auto& tn( (T_TextureInstrNode&) node );
|
|
const auto hasLOD( tn.lods( ) );
|
|
processIdentifier( funcIndex , tn.id( ) , tn.idLocation( ) );
|
|
addInstruction( OP_INIT_TEXTURE , {
|
|
uint32_t( tn.texType( ) ) , hasLOD ? 1u : 0u
|
|
} , tn.location( ) );
|
|
if ( hasLOD ) {
|
|
sdMain --;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case A_Node::OP_FRAMEBUFFER:
|
|
if ( !exit ) {
|
|
auto& fbn( (T_FramebufferInstrNode&) node );
|
|
processIdentifier( funcIndex , fbn.id( ) , fbn.idLocation( ) );
|
|
addInstruction( OP_USE_FRAMEBUFFER , fbn.location( ) );
|
|
fbCurrent = 0;
|
|
}
|
|
break;
|
|
case A_Node::TN_FBATT:
|
|
if ( exit ) {
|
|
auto& fan( (T_FramebufferInstrNode::T_Attachment&) node );
|
|
processIdentifier( funcIndex , fan.id( ) , fan.location( ) );
|
|
const uint32_t lod( fan.lod( ) ? 1 : 0 ) ,
|
|
id( fan.isDepth( ) ? 0 : ++fbCurrent );
|
|
addInstruction( OP_FB_ATTACH , { id , lod } , fan.location( ) );
|
|
if ( lod ) {
|
|
sdMain --;
|
|
}
|
|
if ( id ) {
|
|
addInstruction( OP_FB_TOGGLE , { id - 1 , 1 } , fan.location( ) );
|
|
}
|
|
}
|
|
break;
|
|
|
|
|
|
//- STATE -----------------------------------------------------------------------------
|
|
|
|
case A_Node::OP_MAINOUT:
|
|
if ( exit ) {
|
|
addInstruction( OP_CONST , 0 , node.location( ) );
|
|
addInstruction( OP_PUSH , node.location( ) );
|
|
addInstruction( OP_USE_FRAMEBUFFER , node.location( ) );
|
|
}
|
|
break;
|
|
|
|
case A_Node::OP_IMAGE:
|
|
if ( exit ) {
|
|
auto& imn{ (T_ImageInstrNode&) node };
|
|
processIdentifier( funcIndex , imn.id( ) , imn.idLocation( ) );
|
|
addInstruction( OP_IMAGE , {
|
|
uint32_t( imn.accessMode( ) ) ,
|
|
imn.hasParameter( T_ImageInstrNode::P_LAYER ) ? 1u : 0u
|
|
} , node.location( ) );
|
|
}
|
|
break;
|
|
|
|
case A_Node::OP_USE_FRAMEBUFFER:
|
|
if ( exit ) {
|
|
auto& fbn( (T_UseInstrNode&) node );
|
|
processIdentifier( funcIndex , fbn.id( ) , fbn.idLocation( ) );
|
|
addInstruction( OP_USE_FRAMEBUFFER , fbn.location( ) );
|
|
}
|
|
break;
|
|
|
|
case A_Node::OP_USE_PIPELINE:
|
|
if ( exit ) {
|
|
auto& fbn( (T_UseInstrNode&) node );
|
|
processIdentifier( funcIndex , fbn.id( ) , fbn.idLocation( ) );
|
|
addInstruction( OP_USE_PIPELINE , fbn.location( ) );
|
|
}
|
|
break;
|
|
|
|
case A_Node::OP_USE_PROGRAM:
|
|
if ( exit ) {
|
|
auto& fbn( (T_UseInstrNode&) node );
|
|
processIdentifier( funcIndex , fbn.id( ) , fbn.idLocation( ) );
|
|
addInstruction( OP_USE_PROGRAM , fbn.location( ) );
|
|
}
|
|
break;
|
|
|
|
case A_Node::OP_USE_TEXTURE:
|
|
if ( exit ) {
|
|
auto& tn( (T_UseTextureInstrNode&) node );
|
|
processIdentifier( funcIndex , tn.samplerId( ) , tn.samplerIdLocation( ) );
|
|
processIdentifier( funcIndex , tn.id( ) , tn.idLocation( ) );
|
|
addInstruction( OP_USE_TEXTURE , tn.bank( ) , tn.location( ) );
|
|
}
|
|
break;
|
|
|
|
case A_Node::OP_UNIFORMS:
|
|
if ( exit ) {
|
|
auto& un( (T_UniformsInstrNode&) node );
|
|
addInstruction( OP_CONST , un.uloc( ) , un.ulocLocation( ) );
|
|
addInstruction( OP_PUSH , un.ulocLocation( ) );
|
|
processIdentifier( funcIndex , un.progId( ) , un.progIdLocation( ) );
|
|
addInstruction( OP_UNIFORMS , { un.size( ) - 1 , un.integers( ) ? 1u : 0u } ,
|
|
un.location( ) );
|
|
sdMain -= un.size( );
|
|
}
|
|
break;
|
|
|
|
case A_Node::OP_VIEWPORT:
|
|
if ( exit ) {
|
|
addInstruction( OP_VIEWPORT , node.location( ) );
|
|
}
|
|
break;
|
|
|
|
|
|
//- RENDERING -------------------------------------------------------------------------
|
|
|
|
case A_Node::OP_FULLSCREEN:
|
|
if ( exit ) {
|
|
addInstruction( OP_FULLSCREEN , node.location( ) );
|
|
}
|
|
break;
|
|
|
|
case A_Node::OP_CLEAR:
|
|
if ( exit ) {
|
|
addInstruction( OP_CLEAR , node.location( ) );
|
|
}
|
|
break;
|
|
|
|
case A_Node::OP_COMPUTE:
|
|
if ( exit ) {
|
|
addInstruction( OP_COMPUTE , node.location( ) );
|
|
}
|
|
break;
|
|
|
|
|
|
//- DEBUGGING / UI CONTROLS -----------------------------------------------------------
|
|
|
|
case A_Node::OP_PROFILE:
|
|
if ( noUIInstructions ) {
|
|
break;
|
|
}
|
|
if ( exit ) {
|
|
addInstruction( OP_UI_PEXIT , node.location( ) );
|
|
} else {
|
|
auto& pn( (T_ProfileInstrNode&) node );
|
|
if ( ! output->uiStrings.contains( pn.text( ) ) ) {
|
|
output->uiStrings.add( pn.text( ) );
|
|
}
|
|
addInstruction( OP_UI_PENTER , output->uiStrings.indexOf( pn.text( ) ) ,
|
|
pn.location( ) );
|
|
}
|
|
break;
|
|
|
|
case A_Node::OP_INPUT:
|
|
if ( noUIInstructions ) {
|
|
break;
|
|
}
|
|
if ( exit ) {
|
|
auto& in( (T_InputInstrNode&) node );
|
|
T_OpValue value;
|
|
value.f = in.defValue( );
|
|
addInstruction( OP_UI_INPUT_DFT , {
|
|
uint32_t( output->inputs.indexOf( in.id( ) ) ) ,
|
|
uint32_t( constants.indexOf( value.u ) + 3 + output->nVariables ) } ,
|
|
node.location( ) );
|
|
}
|
|
break;
|
|
|
|
case A_Node::OP_ODBG:
|
|
if ( noUIInstructions ) {
|
|
break;
|
|
}
|
|
if ( exit ) {
|
|
auto& n( (T_OutputDebugInstrNode&) node );
|
|
if ( ! output->uiStrings.contains( n.description( ) ) ) {
|
|
output->uiStrings.add( n.description( ) );
|
|
}
|
|
processIdentifier( funcIndex , n.texture( ) , n.textureLocation( ) );
|
|
addInstruction( OP_UI_ODBG , {
|
|
uint32_t( n.mode( ) ) ,
|
|
uint32_t( output->uiStrings.indexOf( n.description( ) ) ) } ,
|
|
node.location( ) );
|
|
}
|
|
break;
|
|
|
|
case A_Node::OP_OVERRIDES:
|
|
if ( noUIInstructions ) {
|
|
break;
|
|
}
|
|
if ( exit ) {
|
|
auto& n{ (T_OverridesInstrNode&) node };
|
|
const auto idx{ output->overrides.add( n.extractRoot( ) ) };
|
|
addInstruction( OP_UI_INPUT_OVR , idx , node.location( ) );
|
|
}
|
|
break;
|
|
|
|
|
|
//- EXPRESSIONS - ARGUMENTS -----------------------------------------------------------
|
|
|
|
case A_Node::TN_ARG:
|
|
{
|
|
auto& n( (T_ArgumentNode&)node );
|
|
if ( n.isIdentifier( ) && !exit ) {
|
|
const bool main{ processIdentifier( funcIndex ,
|
|
(T_IdentifierExprNode&) n.expression( ) ) };
|
|
if ( !main ) {
|
|
addInstruction( OP_PUSH , node.location( ) );
|
|
addInstruction( OP_FP_SSTORE , 0 , node.location( ) );
|
|
}
|
|
return false;
|
|
} else if ( exit && !n.isIdentifier( ) ) {
|
|
addInstruction( OP_PUSH , node.location( ) );
|
|
addInstruction( OP_FP_SSTORE , 0 , node.location( ) );
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
//- EXPRESSIONS - OPERATORS -----------------------------------------------------------
|
|
|
|
case A_Node::EXPR_CMP_EQ: case A_Node::EXPR_CMP_NE:
|
|
case A_Node::EXPR_CMP_GT: case A_Node::EXPR_CMP_GE:
|
|
case A_Node::EXPR_CMP_LT: case A_Node::EXPR_CMP_LE:
|
|
if ( exit ) {
|
|
const uint32_t op( dynamic_cast< T_BinaryOperatorNode& >( node ).op( )
|
|
- T_BinaryOperatorNode::CMP_EQ );
|
|
addInstruction( OP_FP_CMP , op , node.location( ) );
|
|
}
|
|
break;
|
|
|
|
case A_Node::EXPR_ADD: if ( exit ) { addInstruction( OP_FP_ADD , node.location( ) ); } break;
|
|
case A_Node::EXPR_SUB: if ( exit ) { addInstruction( OP_FP_SUB , node.location( ) ); } break;
|
|
case A_Node::EXPR_MUL: if ( exit ) { addInstruction( OP_FP_MUL , node.location( ) ); } break;
|
|
case A_Node::EXPR_DIV: if ( exit ) { addInstruction( OP_FP_DIV , node.location( ) ); } break;
|
|
case A_Node::EXPR_POW: if ( exit ) { addInstruction( OP_FP_POW , node.location( ) ); } break;
|
|
|
|
case A_Node::EXPR_NEG: if ( exit ) { addInstruction( OP_FP_NEG , node.location( ) ); } break;
|
|
case A_Node::EXPR_INV: if ( exit ) { addInstruction( OP_FP_INV , node.location( ) ); } break;
|
|
case A_Node::EXPR_NOT: if ( exit ) { addInstruction( OP_FP_NOT , node.location( ) ); } break;
|
|
case A_Node::EXPR_SIN: if ( exit ) { addInstruction( OP_FP_SIN , node.location( ) ); } break;
|
|
case A_Node::EXPR_COS: if ( exit ) { addInstruction( OP_FP_COS , node.location( ) ); } break;
|
|
case A_Node::EXPR_TAN: if ( exit ) { addInstruction( OP_FP_TAN , node.location( ) ); } break;
|
|
case A_Node::EXPR_SQRT: if ( exit ) { addInstruction( OP_FP_SQRT , node.location( ) ); } break;
|
|
case A_Node::EXPR_EXP: if ( exit ) { addInstruction( OP_FP_EXP , node.location( ) ); } break;
|
|
case A_Node::EXPR_LN: if ( exit ) { addInstruction( OP_FP_LN , node.location( ) ); } break;
|
|
|
|
|
|
//- EXPRESSIONS - TERMINAL NODES ------------------------------------------------------
|
|
|
|
case A_Node::EXPR_CONST:
|
|
if ( !exit ) {
|
|
T_OpValue value;
|
|
value.f = dynamic_cast< T_ConstantExprNode& >( node ).floatValue( );
|
|
addInstruction( OP_FP_LOAD ,
|
|
constants.indexOf( value.u ) + 3 + output->nVariables ,
|
|
node.location( ) );
|
|
}
|
|
break;
|
|
|
|
case A_Node::EXPR_ID:
|
|
if ( !exit ) {
|
|
processIdentifier( funcIndex ,
|
|
dynamic_cast< T_IdentifierExprNode& >( node ) );
|
|
}
|
|
break;
|
|
|
|
case A_Node::EXPR_INPUT:
|
|
if ( !exit ) {
|
|
auto& in( (T_InputExprNode&) node );
|
|
assert( output->inputs.contains( in.id( ) ) );
|
|
addInstruction( OP_GET_INPUT ,
|
|
output->inputs.indexOf( in.id( ) ) ,
|
|
in.location( ) );
|
|
}
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void T_CompilerImpl_::processFunction(
|
|
const bool exit ,
|
|
const uint32_t args ,
|
|
const uint32_t lvars ,
|
|
T_SRDLocation const& location ) noexcept
|
|
{
|
|
if ( exit ) {
|
|
assert( sdMain == args + lvars + 1 );
|
|
assert( sdFPU == 0 );
|
|
if ( lvars ) {
|
|
addInstruction( OP_POP , lvars - 1 , location );
|
|
sdMain -= lvars;
|
|
}
|
|
sdMain -= args;
|
|
addInstruction( OP_RET , args , location );
|
|
assert( sdMain == 0 );
|
|
} else {
|
|
if ( lvars ) {
|
|
addInstruction( OP_RES_STACK , lvars - 1 , location );
|
|
sdMain += lvars;
|
|
}
|
|
sdMain += 1 + args;
|
|
}
|
|
}
|
|
|
|
// Returns true if the identifier caused a push to the main stack, false
|
|
// if it pushed to the FPU stack.
|
|
bool T_CompilerImpl_::processIdentifier(
|
|
const uint32_t funcIndex ,
|
|
T_IdentifierExprNode const& node ) noexcept
|
|
{
|
|
return processIdentifier( funcIndex , node.id( ) , node.location( ) );
|
|
}
|
|
|
|
bool T_CompilerImpl_::processIdentifier(
|
|
uint32_t funcIndex ,
|
|
T_String const& id ,
|
|
T_SRDLocation const& location ) noexcept
|
|
{
|
|
auto& func{ input->root.function( funcIndex ) };
|
|
if ( func.hasLocal( id ) ) {
|
|
const E_DataType dt{ func.getLocalType( id ) };
|
|
assert( dt != E_DataType::UNKNOWN );
|
|
|
|
uint32_t stackPos;
|
|
if ( func.isArgument( id ) ) {
|
|
auto const& fn( (T_FuncNode&) func );
|
|
const auto nArgs( fn.arguments( ) );
|
|
stackPos = nArgs - 1 - func.getLocalIndex( id );
|
|
} else {
|
|
stackPos = func.getLocalIndex( id ) + 1;
|
|
}
|
|
assert( stackPos < sdMain );
|
|
|
|
const auto p( sdMain - ( stackPos + 1 ) );
|
|
if ( dt == E_DataType::VARIABLE ) {
|
|
addInstruction( OP_FP_SLOAD , p , location );
|
|
return false;
|
|
}
|
|
|
|
addInstruction( OP_SLOAD , p , location );
|
|
addInstruction( OP_PUSH , location );
|
|
return true;
|
|
}
|
|
|
|
assert( input->types.contains( id ) );
|
|
const E_DataType dt{ *( input->types.get( id ) ) };
|
|
assert( dt != E_DataType::UNKNOWN );
|
|
assert( locations.contains( id ) );
|
|
if ( dt == E_DataType::VARIABLE || dt == E_DataType::BUILTIN ) {
|
|
addInstruction( OP_FP_LOAD , *locations.get( id ) ,
|
|
location );
|
|
return false;
|
|
}
|
|
|
|
addInstruction( OP_LOAD , *locations.get( id ) ,
|
|
location );
|
|
addInstruction( OP_PUSH , location );
|
|
return true;
|
|
}
|
|
|
|
|
|
void T_CompilerImpl_::addInstruction(
|
|
const E_OpType op ,
|
|
T_SRDLocation const& location ) noexcept
|
|
{
|
|
assert( ArgumentsFor( op ) == 0 );
|
|
applyStackEffects( output->ops.addNew( op , location ) );
|
|
}
|
|
|
|
void T_CompilerImpl_::addInstruction(
|
|
const E_OpType op ,
|
|
const uint32_t arg0 ,
|
|
T_SRDLocation const& location ) noexcept
|
|
{
|
|
assert( ArgumentsFor( op ) == 1 );
|
|
applyStackEffects( output->ops.addNew( op , location , arg0 ) );
|
|
}
|
|
|
|
void T_CompilerImpl_::addInstruction(
|
|
const E_OpType op ,
|
|
std::initializer_list< uint32_t > args ,
|
|
T_SRDLocation const& location ) noexcept
|
|
{
|
|
assert( ArgumentsFor( op ) == args.size( ) );
|
|
applyStackEffects( output->ops.addNew( op , location , args ) );
|
|
}
|
|
|
|
void T_CompilerImpl_::applyStackEffects(
|
|
T_Op const& op ) noexcept
|
|
{
|
|
const auto m( DeltaMainStack( op.op ) );
|
|
const auto f( DeltaFPUStack( op.op ) );
|
|
|
|
logger( [&]() {
|
|
T_StringBuilder sb;
|
|
sb << "applying stack effects for (" << op << ") - sdMain " << sdMain
|
|
<< " (" << m << ") sdFPU " << sdFPU << " (" << f << ')';
|
|
return sb;
|
|
} , 5 );
|
|
|
|
if ( m ) {
|
|
assert( m > 0 || sdMain >= uint32_t( -m ) );
|
|
sdMain += m;
|
|
}
|
|
|
|
if ( f ) {
|
|
assert( ( f > 0 && sdFPU + f < 8 ) || ( f < 0 && sdFPU >= uint32_t( -m ) ) );
|
|
sdFPU += m;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*= T_OpsCompiler ==============================================================*/
|
|
|
|
T_OpsCompiler::T_OpsCompiler(
|
|
const bool noUIInstructions ) noexcept
|
|
: A_PrivateImplementation( new T_CompilerImpl_( &logger_ , noUIInstructions ) )
|
|
{ }
|
|
|
|
P_OpProgram T_OpsCompiler::compile(
|
|
T_OpsParserOutput const& input ) noexcept
|
|
{
|
|
return p< T_CompilerImpl_ >( ).compile( input );
|
|
}
|