#include "externals.hh" #include "ops.hh" #include "globals.hh" #include "sync.hh" using namespace cops; /*= Command execution ========================================================*/ void cops::Execute( T_Operations const& operations , T_Context& context ) { for ( auto const& op : operations ) { op->execute( context ); } } T_Operations& T_Program::function( T_String const& name , const uint32_t args ) { T_Function* op( functions.get( name ) ); if ( !op ) { functions.add( T_Function{ name , args } ); op = functions.get( name ); } assert( op->arguments == args ); return op->operations; } /*= X_OpFailure ==============================================================*/ X_OpFailure::X_OpFailure( T_String const& source , uint32_t line , T_String&& error ) noexcept : std::exception( ) , source_( source ) , line_( line ) , error_( std::move( error ) ) { ebcl::T_StringBuilder sb; sb << "Program error (" << source << ", l. " << line << "): " << error << '\0'; fullMessage_ = std::move( sb ); } char const* X_OpFailure::what( ) const noexcept { return fullMessage_.data( ); } /*= T_Context ================================================================*/ T_Context& T_Context::store( T_String const& name , const float value ) { vars.set( name , value ); return *this; } /*= T_Op =====================================================================*/ T_Op::T_Op( const E_Op op ) : op_( op ) { } T_Op::~T_Op( ) { } X_OpFailure T_Op::error( ebcl::T_StringBuilder& message ) const noexcept { return X_OpFailure( source , line , std::move( message ) ); } X_OpFailure T_Op::error( T_String const& message ) const noexcept { return X_OpFailure( source , line , T_String( message ) ); } /*= OPLoadConstant ===========================================================*/ OPLoadConstant::OPLoadConstant( const float constant ) : T_Op( OP_LOAD_CONSTANT ) , constant( constant ) { } void OPLoadConstant::execute( T_Context& ctx ) const { ctx.opStack.add( constant ); } /*= OPLoadVariable ===========================================================*/ OPLoadVariable::OPLoadVariable( T_String const& variable ) : T_Op( OP_LOAD_VARIABLE ) , variable( variable ) { } void OPLoadVariable::execute( T_Context& ctx ) const { auto const* v( ctx.vars.get( variable ) ); if ( !v ) { ebcl::T_StringBuilder sb; sb << "variable '" << variable << "' not found" << '\0'; throw error( sb ); } ctx.opStack.add( *v ); } /*= OPLoadInput ==============================================================*/ OPLoadInput::OPLoadInput( T_String const& input ) : T_Op( OP_LOAD_VARIABLE ) , input( input ) { } void OPLoadInput::execute( T_Context& ctx ) const { const auto i( Globals::Sync( ).inputPos( input ) ); ctx.opStack.add( Globals::Sync( ).inputs( )[ i ] ); } /*= OPStoreVariable ==========================================================*/ OPStoreVariable::OPStoreVariable( T_String const& variable ) : T_Op( OP_LOAD_VARIABLE ) , variable( variable ) { } void OPStoreVariable::execute( T_Context& ctx ) const { if ( ctx.opStack.empty( ) ) { throw error( "stack is empty" ); } ctx.store( variable , ctx.opStack.last( ) ); ctx.opStack.removeLast( ); } /*= Arithmetic operators =====================================================*/ void OPAdd::execute( T_Context& ctx ) const { if ( ctx.opStack.size( ) < 2 ) { throw error( "missing operands in stack" ); } const float v( ctx.opStack.last( ) ); ctx.opStack.removeLast( ); ctx.opStack.last( ) += v; } void OPMul::execute( T_Context& ctx ) const { if ( ctx.opStack.size( ) < 2 ) { throw error( "missing operands in stack" ); } const float v( ctx.opStack.last( ) ); ctx.opStack.removeLast( ); ctx.opStack.last( ) *= v; } void OPNeg::execute( T_Context& ctx ) const { if ( ctx.opStack.empty( ) ) { throw error( "missing operand in stack" ); } ctx.opStack.last( ) = -ctx.opStack.last( ); } void OPInv::execute( T_Context& ctx ) const { if ( ctx.opStack.empty( ) || ctx.opStack.last( ) == 0 ) { throw error( "missing operand in stack" ); } ctx.opStack.last( ) = 1.0f / ctx.opStack.last( ); } /*= Stack operations =========================================================*/ OPDup::OPDup( const uint32_t stackIndex ) : T_Op( OP_DUP ) , stackIndex( stackIndex ) { } void OPDup::execute( T_Context& ctx ) const { if ( ctx.opStack.size( ) <= stackIndex ) { ebcl::T_StringBuilder sb; sb << "stack does not have " << ( stackIndex + 1 ) << " items (only " << ctx.opStack.size( ) << ")"; throw error( sb ); } ctx.opStack.add( *( ctx.opStack.end( ) - stackIndex - 1 ) ); } /*----------------------------------------------------------------------------*/ OPXchg::OPXchg( const uint32_t stackIndex ) : T_Op( OP_XCHG ) , stackIndex( stackIndex ) { } void OPXchg::execute( T_Context& ctx ) const { if ( ctx.opStack.size( ) <= stackIndex ) { ebcl::T_StringBuilder sb( "stack does not have " ); sb << ( stackIndex + 1 ) << " items"; throw error( sb ); } if ( stackIndex ) { std::swap( *( ctx.opStack.end( ) - 1 ) , *( ctx.opStack.end( ) - stackIndex - 1 ) ); } } /*= OPSetUniform =============================================================*/ // GENERAL FIXME // program identifier should be an entry in a table // or, you know, a program name OPSetUniform::OPSetUniform( const uint32_t count , const bool integer ) : T_Op( OP_SET_UNIFORM ) , count( count ) , integer( integer ) { } void OPSetUniform::execute( T_Context& ctx ) const { if ( count == 0 || ctx.opStack.size( ) < count + 2 ) { ebcl::T_StringBuilder sb( "stack does not have " ); sb << ( count + 2 ) << " items"; throw error( sb ); } typedef void (*F_SetUniform_)( int , int , int , void* ); void const* const funcs[] = { &glProgramUniform1fv , &glProgramUniform2fv , &glProgramUniform3fv , &glProgramUniform4fv , &glProgramUniform1iv , &glProgramUniform2iv , &glProgramUniform3iv , &glProgramUniform4iv , }; const F_SetUniform_ func{ *(F_SetUniform_*) funcs[ count + ( integer ? 4 : 0 ) - 1 ] }; const GLuint program( ctx.opStack.last( ) ); ctx.opStack.removeLast( ); const GLuint uniform( ctx.opStack.last( ) ); ctx.opStack.removeLast( ); if ( integer ) { int values[ count ]; auto i = count; while ( i != 0 ) { values[ count - i ] = int( ctx.opStack.last( ) ); ctx.opStack.removeLast( ); i --; } func( program , uniform , 1 , values ); } else { float values[ count ]; auto i = count; while ( i != 0 ) { values[ count - i ] = ctx.opStack.last( ); ctx.opStack.removeLast( ); i --; } func( program , uniform , 1 , values ); } } /*= OPUsePipeline ============================================================*/ // GENERAL FIXME // pipeline identifier should be an entry in a table // or, you know, a program name OPUsePipeline::OPUsePipeline( const uint32_t index ) : T_Op( OP_USE_PIPELINE ) , pipeline( index ) { } void OPUsePipeline::execute( T_Context& ) const { glBindProgramPipeline( pipeline ); } /*= OPUseTexture =============================================================*/ // GENERAL FIXME // texture & sampler identifiers should be entries in a table OPUseTexture::OPUseTexture( const uint32_t binding , const uint32_t texture , const uint32_t sampler ) : T_Op( OP_USE_TEXTURE ) , binding( binding ) , texture( texture ) , sampler( sampler ) { } void OPUseTexture::execute( T_Context& ) const { glBindTextureUnit( binding , texture ); glBindSampler( binding , sampler ); } /*= OPUseFramebuffer =========================================================*/ // GENERAL FIXME // framebuffer identifier should be an entry in a table OPUseFramebuffer::OPUseFramebuffer( const uint32_t framebuffer ) : T_Op( OP_USE_FRAMEBUFFER ) , framebuffer( framebuffer ) { } void OPUseFramebuffer::execute( T_Context& ) const { glBindFramebuffer( GL_FRAMEBUFFER , framebuffer ); } /*= OPSetViewport ============================================================*/ void OPSetViewport::execute( T_Context& ctx ) const { if ( ctx.opStack.size( ) < 4 ) { throw error( "stack does not have 4 items" ); } glViewport( *( ctx.opStack.end( ) - 1 ) , *( ctx.opStack.end( ) - 2 ) , *( ctx.opStack.end( ) - 3 ) , *( ctx.opStack.end( ) - 4 ) ); ctx.opStack.resize( ctx.opStack.size( ) - 4 ); } /*= Draw commands ============================================================*/ void OPClear::execute( T_Context& ctx ) const { if ( ctx.opStack.size( ) < 4 ) { throw error( "stack does not have 4 items" ); } glClearColor( *( ctx.opStack.end( ) - 1 ) , *( ctx.opStack.end( ) - 2 ) , *( ctx.opStack.end( ) - 3 ) , *( ctx.opStack.end( ) - 4 ) ); ctx.opStack.resize( ctx.opStack.size( ) - 4 ); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); } void OPFullscreen::execute( T_Context& ) const { glBindVertexArray( 0 ); glDrawArrays( GL_TRIANGLE_STRIP , 0 , 4 ); } /*= Flow control =============================================================*/ OPCall::OPCall( T_String const& function ) : T_Op( OP_CALL ) , function( function ) { } void OPCall::execute( T_Context& ctx ) const { auto const* fp( ctx.program->functions.get( function ) ); if ( !fp ) { ebcl::T_StringBuilder sb( "function '" ); sb << function << "' not found" << '\0'; throw error( sb ); } auto const& func( *fp ); const auto ssz( ctx.opStack.size( ) ); if ( ssz < func.arguments ) { ebcl::T_StringBuilder sb( "function '" ); sb << function << "' requires " << func.arguments << " argument" << ( func.arguments > 1 ? "s" : "" ) << '\0'; throw error( sb ); } Execute( func.operations , ctx ); if ( ctx.opStack.size( ) < ssz ) { ebcl::T_StringBuilder sb( "function '" ); sb << function << "' ate the stack" << '\0'; throw error( sb ); } ctx.opStack.resize( ssz - func.arguments ); }