demotool/ops.cc

922 lines
25 KiB
C++
Raw Normal View History

2017-11-12 17:46:47 +01:00
#include "externals.hh"
#include "control.hh"
#include "globals.hh"
#include "profiling.hh"
#include "rendertarget.hh"
#include "sync.hh"
2017-11-12 17:46:47 +01:00
using namespace ops;
using namespace ebcl;
/*= OPCODE INFORMATIONS ========================================================*/
namespace {
struct T_OpInfo
{
char const* name;
uint32_t nArgs;
int32_t sdMain{ 0 };
int32_t sdFPU{ 0 };
uint32_t fpuReq{ 0 };
2017-11-12 17:46:47 +01:00
template< typename ... Mods >
constexpr T_OpInfo( char const* name ,
uint32_t nArgs ,
Mods&&... mods );
constexpr T_OpInfo( char const* name );
};
template< typename ... Mods >
constexpr void ApplyMods_( T_OpInfo& op , Mods&&... mods );
template< >
constexpr void ApplyMods_( T_OpInfo& )
{ }
template< typename M0 , typename ... Mods >
constexpr void ApplyMods_(
T_OpInfo& op ,
M0&& mod ,
Mods&&... mods )
{
mod.applyTo( op );
ApplyMods_( op , std::forward< Mods >( mods ) ... );
}
template< typename ... Mods >
constexpr T_OpInfo::T_OpInfo(
char const* name ,
uint32_t nArgs ,
Mods&&... mods )
: name( name ) , nArgs( nArgs )
{
ApplyMods_( *this , std::forward< Mods >( mods ) ... );
}
constexpr T_OpInfo::T_OpInfo( char const* name )
: T_OpInfo( name , 0 )
{}
struct OpStackMain
{
int32_t value;
explicit constexpr OpStackMain( int32_t v ) : value( v ) { }
constexpr void applyTo( T_OpInfo& op )
{ op.sdMain = value; }
2017-11-12 17:46:47 +01:00
};
struct OpStackFPU
{
int32_t value;
explicit constexpr OpStackFPU( int32_t v ) : value( v ) { }
constexpr void applyTo( T_OpInfo& op )
{ op.sdFPU = value; }
};
2017-11-12 17:46:47 +01:00
static T_KeyValueTable< E_OpType , T_OpInfo > OpInfoTable_{ ([]() {
T_KeyValueTable< E_OpType , T_OpInfo > infos;
infos.add( E_OpType::OP_END , T_OpInfo{ "end" } );
//
infos.add( E_OpType::OP_CALL , T_OpInfo{ "call" , 1 } );
infos.add( E_OpType::OP_RET , T_OpInfo{ "ret" , 1 , OpStackMain{ -1 } } );
2017-11-12 22:20:11 +01:00
//
infos.add( E_OpType::OP_SKIP , T_OpInfo{ "skip" , 1 } );
infos.add( E_OpType::OP_COND_SKIP , T_OpInfo{ "cond-skip" , 2 } );
2017-11-12 17:46:47 +01:00
//
infos.add( E_OpType::OP_RES_STACK , T_OpInfo{ "res-stack" , 1 } );
infos.add( E_OpType::OP_PUSH , T_OpInfo{ "push" , 0 , OpStackMain{ 1 } } );
2017-11-12 17:46:47 +01:00
infos.add( E_OpType::OP_POP , T_OpInfo{ "pop" , 1 } );
infos.add( E_OpType::OP_DUP , T_OpInfo{ "dup" , 1 , OpStackMain{ 1 } } );
2017-11-12 17:46:47 +01:00
//
infos.add( E_OpType::OP_LOAD , T_OpInfo{ "load" , 1 } );
infos.add( E_OpType::OP_STORE , T_OpInfo{ "store" , 1 } );
2017-11-12 17:46:47 +01:00
infos.add( E_OpType::OP_SLOAD , T_OpInfo{ "load-stack" , 1 } );
2017-11-13 17:20:45 +01:00
infos.add( E_OpType::OP_CONST , T_OpInfo{ "const" , 1 } );
infos.add( E_OpType::OP_OFFSET , T_OpInfo{ "offset" , 1 } );
2017-11-12 17:46:47 +01:00
//
infos.add( E_OpType::OP_FP_LOAD , T_OpInfo{ "fp-load" , 1 , OpStackFPU{ 1 } } );
infos.add( E_OpType::OP_FP_SLOAD , T_OpInfo{ "fp-load-stack" , 1 , OpStackFPU{ 1 } } );
infos.add( E_OpType::OP_FP_STORE , T_OpInfo{ "fp-store" , 1 , OpStackFPU{ -1 } } );
infos.add( E_OpType::OP_FP_SSTORE , T_OpInfo{ "fp-store-stack" , 1 , OpStackFPU{ -1 } } );
infos.add( E_OpType::OP_FP_SSTORE_INT , T_OpInfo{ "fp-store-stack-int" , 1 , OpStackFPU{ -1 } } );
2017-11-12 17:46:47 +01:00
//
infos.add( E_OpType::OP_FP_CMP , T_OpInfo{ "fp-cmp" , 1 , OpStackFPU{ -1 } } );
infos.add( E_OpType::OP_FP_ADD , T_OpInfo{ "fp-add" , 0 , OpStackFPU{ -1 } } );
infos.add( E_OpType::OP_FP_SUB , T_OpInfo{ "fp-sub" , 0 , OpStackFPU{ -1 } } );
infos.add( E_OpType::OP_FP_MUL , T_OpInfo{ "fp-mul" , 0 , OpStackFPU{ -1 } } );
infos.add( E_OpType::OP_FP_DIV , T_OpInfo{ "fp-div" , 0 , OpStackFPU{ -1 } } );
infos.add( E_OpType::OP_FP_POW , T_OpInfo{ "fp-pow" , 0 , OpStackFPU{ -1 } } );
//
infos.add( E_OpType::OP_FP_NEG , T_OpInfo{ "fp-neg" , 0 } );
infos.add( E_OpType::OP_FP_INV , T_OpInfo{ "fp-inv" , 0 } );
infos.add( E_OpType::OP_FP_NOT , T_OpInfo{ "fp-not" , 0 } );
infos.add( E_OpType::OP_FP_SIN , T_OpInfo{ "fp-sin" , 0 } );
infos.add( E_OpType::OP_FP_COS , T_OpInfo{ "fp-cos" , 0 } );
infos.add( E_OpType::OP_FP_TAN , T_OpInfo{ "fp-tan" , 0 } );
infos.add( E_OpType::OP_FP_SQRT , T_OpInfo{ "fp-sqrt" , 0 } );
infos.add( E_OpType::OP_FP_EXP , T_OpInfo{ "fp-exp" , 0 } );
infos.add( E_OpType::OP_FP_LN , T_OpInfo{ "fp-ln" , 0 } );
//
infos.add( E_OpType::OP_GEN_ASSETS , T_OpInfo{ "gen-assets" , 2 } );
2017-11-13 10:02:16 +01:00
infos.add( E_OpType::OP_INIT_PIPELINE , T_OpInfo{ "pipeline" , 1 , OpStackMain{ -1 } } );
infos.add( E_OpType::OP_INIT_PROGRAM , T_OpInfo{ "program" , 1 } );
2017-11-13 10:02:16 +01:00
infos.add( E_OpType::OP_INIT_TEXTURE , T_OpInfo{ "texture" , 2 , OpStackMain{ -3 } } );
infos.add( E_OpType::OP_INIT_SAMPLER , T_OpInfo{ "sampler" , 2 , OpStackMain{ -3 } } );
infos.add( E_OpType::OP_FB_ATTACH , T_OpInfo{ "fb-attach" , 2 , OpStackMain{ -1 } } );
//
infos.add( E_OpType::OP_USE_FRAMEBUFFER , T_OpInfo{ "use-framebuffer" , 0 , OpStackMain{ -1 } } );
infos.add( E_OpType::OP_FB_TOGGLE , T_OpInfo{ "fb-toggle" , 2 } );
infos.add( E_OpType::OP_USE_PIPELINE , T_OpInfo{ "use-pipeline" , 0 , OpStackMain{ -1 } } );
infos.add( E_OpType::OP_USE_PROGRAM , T_OpInfo{ "use-program" , 0 , OpStackMain{ -1 } } );
infos.add( E_OpType::OP_USE_TEXTURE , T_OpInfo{ "use-texture" , 1 , OpStackMain{ -2 } } );
2017-11-13 17:20:45 +01:00
infos.add( E_OpType::OP_UNIFORMS , T_OpInfo{ "uniforms" , 2 , OpStackMain{ -2 } } );
2017-11-13 17:28:57 +01:00
infos.add( E_OpType::OP_VIEWPORT , T_OpInfo{ "viewport" , 0 , OpStackMain{ -4 } } );
//
infos.add( E_OpType::OP_FULLSCREEN , T_OpInfo{ "fullscreen" } );
2017-11-13 10:02:16 +01:00
infos.add( E_OpType::OP_CLEAR , T_OpInfo{ "clear" , 0 , OpStackMain{ -4 } } );
//
infos.add( E_OpType::OP_UI_PENTER , T_OpInfo{ "ui-prof-enter" , 1 } );
infos.add( E_OpType::OP_UI_PEXIT , T_OpInfo{ "ui-prof-exit" } );
infos.add( E_OpType::OP_UI_INPUT_DFT , T_OpInfo{ "ui-input-default" , 2 } );
infos.add( E_OpType::OP_UI_ODBG , T_OpInfo{ "ui-odbg" , 2 , OpStackMain{ -1 } } );
2017-11-12 17:46:47 +01:00
return infos;
})( ) };
} // namespace
uint32_t ops::ArgumentsFor(
const E_OpType op ) noexcept
{
assert( OpInfoTable_.contains( op ) );
return OpInfoTable_.get( op )->nArgs;
}
int32_t ops::DeltaMainStack(
const E_OpType op ) noexcept
{
assert( OpInfoTable_.contains( op ) );
return OpInfoTable_.get( op )->sdMain;
}
int32_t ops::DeltaFPUStack(
const E_OpType op ) noexcept
{
assert( OpInfoTable_.contains( op ) );
return OpInfoTable_.get( op )->sdFPU;
}
2017-11-12 17:46:47 +01:00
/*= STRING FORMATTING ==========================================================*/
T_StringBuilder& ops::operator<<(
T_StringBuilder& sb ,
const E_OpType et )
{
assert( OpInfoTable_.contains( et ) );
sb << OpInfoTable_.get( et )->name;
return sb;
}
T_StringBuilder& ops::operator<<(
T_StringBuilder& sb ,
T_Op const& op )
{
sb << op.op;
const auto args{ OpInfoTable_.get( op.op )->nArgs };
2017-11-12 22:20:11 +01:00
for ( auto i = 0u ; i < args ; i ++ ) {
sb << ' ' << op.args[ i ];
2017-11-12 17:46:47 +01:00
}
return sb;
}
/*= EMULATOR ===================================================================*/
X_OpFailure::X_OpFailure(
T_Op const& op ,
T_String error ) noexcept
: op_( &op ) , error_( std::move( error ) )
{
T_StringBuilder sb;
sb << "operation (" << op << ") failed: "
<< error_ << "; source: " << op.location << '\0';
fullMessage_ = std::move( sb );
}
char const* X_OpFailure::what( ) const noexcept
{
return fullMessage_.data( );
}
/*------------------------------------------------------------------------------*/
T_OpContext::T_OpContext(
T_OpProgram& program ) noexcept
: program( program )
{
stack.ensureCapacity( stack.growth( ) );
const auto nc{ program.constants.size( ) };
const auto ts{ 3 + nc + program.nVariables
+ program.nFramebuffers + program.nPrograms
+ program.nPipelines + program.nSamplers
+ program.nTextures };
values.resize( ts );
initialInputs.resize( program.inputs.size( ) );
framebuffers.resize( program.nFramebuffers );
pipelines.resize( program.nPipelines );
samplers.resize( program.nSamplers );
textures.resize( program.nTextures );
memset( &values[ 0 ] , 0 , values.size( ) * 4 );
for ( auto i = 0u ; i < nc ; i ++ ) {
values[ i + 3 + program.nVariables ] = program.constants[ i ];
}
}
2017-11-14 17:46:45 +01:00
namespace {
struct T_RunGuard
{
T_OpContext& context;
T_RunGuard( T_OpContext& ctx ) noexcept : context( ctx ) {}
~T_RunGuard( )
{
while ( !context.profiling.empty( ) ) {
Globals::Profiler( ).end( context.profiling.last( ) );
context.profiling.removeLast( );
}
glUseProgram( 0 );
glBindProgramPipeline( 0 );
glBindFramebuffer( GL_FRAMEBUFFER , 0 );
Globals::Textures( ).reset( );
}
};
}
void T_OpContext::run(
const E_RunTarget target ,
const float time ,
const float width ,
const float height )
{
assert( !aborted );
2017-11-14 17:46:45 +01:00
T_RunGuard rg( *this );
x87sp = 0;
instrPtr = program.ops.firstOf( target == R_INIT ? program.init : program.frame );
values[ 0 ] = time;
values[ 1 ] = width;
values[ 2 ] = height;
stack.clear( );
stack.add( 0xffffffff );
2017-11-14 17:42:26 +01:00
while ( !stack.empty( ) ) {
auto const& instr{ program.ops[ instrPtr ] };
//#define YOUR_MUM_IS_A_TRACE
#ifdef YOUR_MUM_IS_A_TRACE
printf( "VALUES\n00" );
for ( auto i = 0u ; i < values.size( ) ; i ++ ) {
printf( " %08x" , values[ i ].u );
if ( i % 4 == 3 ) {
printf( "\n%02x" , i + 1 );
}
}
if ( values.size( ) % 4 != 0 ) {
printf( "\n" );
}
T_StringBuilder sb;
sb << "EXECUTE " << instrPtr << ":\t(" << instr << ") {"
<< instr.location << "}\nSTACK:";
for ( auto i = 0u ; i < stack.size( ) ; i ++ ) {
sb << ' ' << stack[ i ].u;
}
sb << "\nFPU STACK:";
for ( auto i = 0u ; i < x87sp ; i ++ ) {
sb << ' ' << x87stack[ i ];
}
sb << '\n' << '\0';
printf( "%s" , sb.data( ) );
#endif
switch ( instr.op ) {
case OP_END:
throw X_OpFailure{ instr , "invalid instruction" };
// --------------------------------------------------------------------------------
case OP_CALL:
if ( program.ops.size( ) <= instr.args[ 0 ] ) {
throw X_OpFailure{ instr , "invalid function index" };
}
stack.add( instrPtr + 1 );
instrPtr = program.ops.firstOf( instr.args[ 0 ] );
continue;
case OP_RET:
ensureStack( instr , 1 + instr.args[ 0 ] );
instrPtr = stack.last( ).u;
stack.resize( stack.size( ) - instr.args[ 0 ] - 1 );
continue;
case OP_SKIP:
// FIXME: make sure we don't go past the end
instrPtr += instr.args[ 0 ];
break;
case OP_COND_SKIP:
ensureStack( instr , 1 );
if ( stack.last( ).u != instr.args[ 1 ] ) {
// FIXME: make sure we don't go past the end
instrPtr += instr.args[ 0 ];
}
break;
// --------------------------------------------------------------------------------
case OP_RES_STACK:
stack.resize( stack.size( ) + instr.args[ 0 ] );
break;
case OP_PUSH:
stack.add( wreg );
break;
case OP_POP:
ensureStack( instr , instr.args[ 0 ] + 1 );
stack.resize( stack.size( ) - instr.args[ 0 ] - 1 );
break;
case OP_DUP:
ensureStack( instr , instr.args[ 0 ] + 1 );
stack.add( stack[ stack.size( ) - instr.args[ 0 ] - 1 ] );
break;
// --------------------------------------------------------------------------------
case OP_LOAD:
checkAddress( instr , instr.args[ 0 ] );
wreg = values[ instr.args[ 0 ] ];
break;
case OP_STORE:
checkAddress( instr , instr.args[ 0 ] );
values[ instr.args[ 0 ] ] = wreg;
break;
case OP_SLOAD:
ensureStack( instr , instr.args[ 0 ] + 1 );
wreg = stack[ stack.size( ) - instr.args[ 0 ] - 1 ];
break;
case OP_CONST:
case OP_OFFSET:
wreg = instr.args[ 0 ];
break;
// --------------------------------------------------------------------------------
case OP_FP_LOAD:
ensureFpuStack( instr , 0 , 1 );
checkAddress( instr , instr.args[ 0 ] );
x87stack[ x87sp ++ ] = values[ instr.args[ 0 ] ].f;
break;
case OP_FP_STORE:
ensureFpuStack( instr , 1 , 0 );
checkAddress( instr , instr.args[ 0 ] );
values[ instr.args[ 0 ] ].f = x87stack[ -- x87sp ];
break;
case OP_FP_SLOAD:
ensureFpuStack( instr , 0 , 1 );
ensureStack( instr , instr.args[ 0 ] + 1 );
x87stack[ x87sp ++ ] = stack[ stack.size( ) - instr.args[ 0 ] - 1 ].f;
break;
case OP_FP_SSTORE:
ensureFpuStack( instr , 1 , 0 );
ensureStack( instr , instr.args[ 0 ] + 1 );
stack[ stack.size( ) - instr.args[ 0 ] - 1 ].f = x87stack[ -- x87sp ];
break;
case OP_FP_SSTORE_INT:
ensureFpuStack( instr , 1 , 0 );
ensureStack( instr , instr.args[ 0 ] + 1 );
stack[ stack.size( ) - instr.args[ 0 ] - 1 ].i = int32_t( x87stack[ -- x87sp ] );
break;
// --------------------------------------------------------------------------------
case OP_FP_CMP:
{
ensureFpuStack( instr , 2 , 0 );
const auto v2( x87stack[ x87sp - 1 ] ) ,
v1( x87stack[ x87sp - 2 ] );
x87sp --;
const bool rv{ ([&]( ) -> bool {
switch ( instr.args[ 0 ] ) {
case 0: return v1 == v2;
case 1: return v1 != v2;
case 2: return v1 > v2;
case 3: return v1 >= v2;
case 4: return v1 < v2;
case 5: return v1 <= v2;
}
throw X_OpFailure( instr , "invalid operation" );
})() };
x87stack[ x87sp - 1 ] = rv ? 1 : 0;
break;
}
case OP_FP_ADD:
{
ensureFpuStack( instr , 2 , 0 );
x87stack[ x87sp - 2 ] += x87stack[ x87sp - 1 ];
x87sp --;
break;
}
case OP_FP_SUB:
{
ensureFpuStack( instr , 2 , 0 );
x87stack[ x87sp - 2 ] -= x87stack[ x87sp - 1 ];
x87sp --;
break;
}
case OP_FP_MUL:
{
ensureFpuStack( instr , 2 , 0 );
x87stack[ x87sp - 2 ] *= x87stack[ x87sp - 1 ];
x87sp --;
break;
}
case OP_FP_DIV:
{
ensureFpuStack( instr , 2 , 0 );
if ( x87stack[ x87sp - 1 ] == 0 ) {
throw X_OpFailure{ instr , "arithmetic error" };
}
x87stack[ x87sp - 2 ] /= x87stack[ x87sp - 1 ];
x87sp --;
break;
}
case OP_FP_POW:
{
ensureFpuStack( instr , 2 , 0 );
x87stack[ x87sp - 2 ] = pow( x87stack[ x87sp - 2 ] ,
x87stack[ x87sp - 1 ] );
x87sp --;
break;
}
// --------------------------------------------------------------------------------
case OP_FP_NEG:
{
ensureFpuStack( instr , 1 , 1 );
x87stack[ x87sp - 1 ] = -x87stack[ x87sp - 1 ];
break;
}
case OP_FP_INV:
{
ensureFpuStack( instr , 1 , 1 );
if ( x87stack[ x87sp - 1 ] == 0 ) {
throw X_OpFailure{ instr , "arithmetic error" };
}
x87stack[ x87sp - 1 ] = 1.0 / x87stack[ x87sp - 1 ];
break;
}
case OP_FP_NOT:
{
ensureFpuStack( instr , 1 , 0 );
x87stack[ x87sp - 1 ] = x87stack[ x87sp - 1 ] == 0 ? 1 : 0;
break;
}
case OP_FP_SIN:
{
ensureFpuStack( instr , 1 , 0 );
x87stack[ x87sp - 1 ] = sin( x87stack[ x87sp - 1 ] );
break;
}
case OP_FP_COS:
{
ensureFpuStack( instr , 1 , 0 );
x87stack[ x87sp - 1 ] = cos( x87stack[ x87sp - 1 ] );
break;
}
case OP_FP_TAN:
{
ensureFpuStack( instr , 1 , 1 );
const auto c( cos( x87stack[ x87sp - 1 ] ) );
if ( c == 0 ) {
throw X_OpFailure{ instr , "arithmetic error" };
}
x87stack[ x87sp - 1 ] = sin( x87stack[ x87sp - 1 ] ) / c;
break;
}
case OP_FP_SQRT:
{
ensureFpuStack( instr , 1 , 0 );
const auto v( x87stack[ x87sp - 1 ] );
if ( v < 0 ) {
throw X_OpFailure{ instr , "arithmetic error" };
}
x87stack[ x87sp - 1 ] = sqrt( v );
break;
}
case OP_FP_EXP:
{
ensureFpuStack( instr , 1 , 0 ); // FIXME: need FPU stack space
x87stack[ x87sp - 1 ] = exp( x87stack[ x87sp - 1 ] );
break;
}
case OP_FP_LN:
{
ensureFpuStack( instr , 1 , 0 ); // FIXME: need FPU stack space
x87stack[ x87sp - 1 ] = log( x87stack[ x87sp - 1 ] );
break;
}
// --------------------------------------------------------------------------------
case OP_GEN_ASSETS:
{
auto idx = 0u;
switch ( instr.args[ 0 ] ) {
case 3: idx += program.nSamplers;
// fallthrough
case 2: idx += program.nPrograms + program.nPipelines;
// fallthrough
case 1: idx += program.nFramebuffers;
// fallthrough
case 0: idx += 3 + program.nVariables + program.constants.size( ); break;
default:
throw X_OpFailure( instr , "invalid argument" );
}
auto count = 0u;
switch ( instr.args[ 0 ] ) {
case 3: count = program.nTextures; break;
case 2: count = program.nSamplers; break;
case 1: count = program.nPipelines; break;
case 0: count = program.nFramebuffers; break;
}
for ( auto i = 0u ; i < count ; i ++ ) {
values[ idx + i ].u = i + 1;
}
break;
}
case OP_INIT_PIPELINE:
{
ensureStack( instr , 2 + instr.args[ 0 ] );
const auto sv( stack.last( ).u );
stack.removeLast( );
if ( sv == 0 || sv > pipelines.size( ) ) {
throw X_OpFailure{ instr , "invalid pipeline" };
}
const auto plIndex( sv - 1 );
T_String progNames[ 6 ];
for ( auto i = 0u ; i <= instr.args[ 0 ] ; i ++ ) {
const auto prIndex( stack.last( ).u );
stack.removeLast( );
if ( !prIndex ) {
throw X_OpFailure{ instr , "pipeline uses uninitialised program" };
}
progNames[ i ] = programs[ prIndex - 1 ]->name( );
}
pipelines[ plIndex ] = NewOwned< T_ShaderPipeline >(
Globals::Shaders( ).pipeline( progNames , instr.args[ 0 ] + 1 ) );
break;
}
case OP_INIT_PROGRAM:
{
if ( instr.args[ 0 ] >= program.progNames.size( ) ) {
throw X_OpFailure{ instr , "invalid argument" };
}
programs.add( NewOwned< T_ShaderProgram >(
Globals::Shaders( ).program( program.progNames[ instr.args[ 0 ] ] ) ) );
wreg = programs.size( );
break;
}
case OP_INIT_SAMPLER:
{
ensureStack( instr , 3 );
const auto sv( stack.last( ).u );
stack.removeLast( );
if ( sv == 0 || sv > samplers.size( ) ) {
throw X_OpFailure{ instr , "invalid sampler" };
}
const auto samplerIndex( sv - 1 );
if ( !samplers[ samplerIndex ] ) {
samplers[ samplerIndex ] = NewOwned< T_TextureSampler >( );
}
const float max( stack.last( ).f );
stack.removeLast( );
const float min( stack.last( ).f );
stack.removeLast( );
samplers[ samplerIndex ]->sampling( E_TexSampling( ( instr.args[ 0 ] & 4 ) >> 2 ) )
.wrap( E_TexWrap( instr.args[ 1 ] ) )
.lod( min , max );
if ( ( instr.args[ 0 ] & 2 ) == 0 ) {
samplers[ samplerIndex ]->noMipmap( );
} else {
samplers[ samplerIndex ]->mipmap( E_TexSampling( instr.args[ 0 ] & 1 ) );
}
break;
}
case OP_INIT_TEXTURE:
{
ensureStack( instr , 3 + ( instr.args[ 1 ] ? 1 : 0 ) );
const auto sv( stack.last( ).u );
stack.removeLast( );
if ( sv == 0 || sv > textures.size( ) ) {
throw X_OpFailure{ instr , "invalid texture" };
}
const auto index( sv - 1 );
const uint32_t w( stack.last( ).f );
stack.removeLast( );
const uint32_t h( stack.last( ).f );
stack.removeLast( );
const uint32_t lods( instr.args[ 1 ] ? stack.last( ).f : 1 );
if ( instr.args[ 1 ] ) {
stack.removeLast( );
}
textures[ index ] = NewOwned< T_Texture >(
w , h , E_TexType( instr.args[ 0 ] ) , lods );
break;
}
case OP_FB_ATTACH:
{
// instr[ 0 ] = id (0 for depth attachment, 1+ for color attachments)
// instr[ 1 ] = has LOD?
// stack: TOP < TEX ( < LOD if has LOD )
ensureStack( instr , 1 + ( instr.args[ 1 ] ? 1 : 0 ) );
if ( curFb < 0 ) {
throw X_OpFailure{ instr , "no framebuffer selected" };
}
const auto svt( stack.last( ).u );
stack.removeLast( );
if ( svt == 0 || svt > textures.size( ) || !textures[ svt - 1 ] ) {
throw X_OpFailure{ instr , "invalid texture" };
}
const auto& texture( *textures[ svt - 1 ] );
const uint32_t lod( instr.args[ 1 ] ? uint32_t( stack.last( ).f ) : 0 );
if ( instr.args[ 1 ] ) {
stack.removeLast( );
}
framebuffers[ curFb ]->attach( texture , lod , instr.args[ 0 ] );
break;
}
// --------------------------------------------------------------------------------
case OP_USE_TEXTURE:
{
ensureStack( instr , 2 );
const auto svt( stack.last( ).u );
stack.removeLast( );
if ( svt == 0 || svt > textures.size( ) || !textures[ svt - 1 ] ) {
throw X_OpFailure{ instr , "invalid texture" };
}
const auto svs( stack.last( ).u );
stack.removeLast( );
if ( svs == 0 || svs > samplers.size( ) || !samplers[ svs - 1 ] ) {
throw X_OpFailure{ instr , "invalid sampler" };
}
Globals::Textures( ).bind( instr.args[ 0 ] ,
*textures[ svt - 1 ] , *samplers[ svs - 1 ] );
break;
}
case OP_USE_PIPELINE:
{
ensureStack( instr , 1 );
const auto sv( stack.last( ).u );
stack.removeLast( );
if ( sv == 0 || sv > pipelines.size( ) || !pipelines[ sv - 1 ] ) {
throw X_OpFailure{ instr , "invalid pipeline" };
}
pipelines[ sv - 1 ]->enable( );
break;
}
case OP_USE_PROGRAM:
{
ensureStack( instr , 1 );
const auto sv( stack.last( ).u );
stack.removeLast( );
if ( sv == 0 || sv > programs.size( ) || !programs[ sv - 1 ] ) {
throw X_OpFailure{ instr , "invalid program" };
}
programs[ sv - 1 ]->enable( );
break;
}
case OP_FB_TOGGLE:
{
if ( curFb < 0 ) {
throw X_OpFailure{ instr , "no framebuffer selected" };
}
framebuffers[ curFb ]->toggle( instr.args[ 0 ] , instr.args[ 1 ] );
break;
}
case OP_UNIFORMS:
{
ensureStack( instr , 3 + instr.args[ 0 ] );
const auto ss( stack.size( ) );
T_OpValue values[ 4 ];
for ( auto i = 0u ; i <= instr.args[ 0 ] ; i ++ ) {
if ( instr.args[ 1 ] ) {
values[ i ] = uint32_t( stack[ ss - 3 - i ].f );
} else {
values[ i ] = stack[ ss - 3 - i ];
}
}
const auto sv( stack.last( ).u );
if ( sv == 0 || sv > programs.size( ) || !programs[ sv - 1 ] ) {
throw X_OpFailure{ instr , "invalid program" };
}
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[
instr.args[ 0 ] + ( instr.args[ 1 ] ? 4 : 0 ) ] };
func( programs[ sv - 1 ]->id( ) , stack[ ss - 2 ].u , 1 , values );
stack.resize( ss - 3 - instr.args[ 0 ] );
break;
}
case OP_VIEWPORT:
{
ensureStack( instr , 4 );
const auto ss( stack.size( ) );
glViewport( stack[ ss - 1 ].f , stack[ ss - 2 ].f ,
stack[ ss - 3 ].f , stack[ ss - 4 ].f );
stack.resize( ss - 4 );
break;
}
case OP_USE_FRAMEBUFFER:
{
ensureStack( instr , 1 );
const auto sv( stack.last( ).u );
stack.removeLast( );
if ( sv == 0 ) {
glBindFramebuffer( GL_FRAMEBUFFER , 0 );
curFb = -1;
return;
}
const auto index( sv - 1 );
if ( !framebuffers[ index ] ) {
framebuffers[ index ] = NewOwned< T_Rendertarget >( );
}
glBindFramebuffer( GL_FRAMEBUFFER , framebuffers[ index ]->id( ) );
curFb = index;
break;
}
// --------------------------------------------------------------------------------
case OP_FULLSCREEN:
glDrawArrays( GL_TRIANGLE_STRIP , 0 , 4 );
break;
case OP_CLEAR:
{
ensureStack( instr , 4 );
const auto ss( stack.size( ) );
glClearColor( stack[ ss - 1 ].f , stack[ ss - 2 ].f ,
stack[ ss - 3 ].f , stack[ ss - 4 ].f );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
stack.resize( ss - 4 );
break;
}
// --------------------------------------------------------------------------------
case OP_UI_PENTER:
{
T_String const& section( program.uiStrings[ instr.args[ 0 ] ] );
Globals::Profiler( ).start( section );
profiling.add( section );
break;
}
case OP_UI_PEXIT:
Globals::Profiler( ).end( profiling.last( ) );
profiling.removeLast( );
break;
case OP_UI_INPUT_DFT:
initialInputs[ instr.args[ 0 ] ] = values[ instr.args[ 1 ] ].f;
break;
case OP_UI_ODBG:
{
ensureStack( instr , 1 );
const auto svt( stack.last( ).u );
stack.removeLast( );
if ( svt == 0 || svt > textures.size( ) || !textures[ svt - 1 ] ) {
throw X_OpFailure{ instr , "invalid texture" };
}
Globals::ODbg( ).registerTexture( *textures[ svt - 1 ] ,
E_ODbgMode( instr.args[ 0 ] ) ,
program.uiStrings[ instr.args[ 1 ] ] );
break;
}
}
instrPtr ++;
}
}
void T_OpContext::ensureStack(
T_Op const& op ,
const uint32_t min )
{
if ( stack.size( ) < min ) {
throw X_OpFailure{ op , "stack underrun" };
}
}
void T_OpContext::ensureFpuStack(
T_Op const& op ,
const uint32_t minStacked ,
const uint32_t minFree )
{
if ( x87sp < minStacked ) {
throw X_OpFailure{ op , "FPU stack underrun" };
}
if ( 8 - x87sp < minFree ) {
throw X_OpFailure{ op , "FPU stack overflow" };
}
}
void T_OpContext::checkAddress(
T_Op const& op ,
const uint32_t address )
{
if ( address >= values.size( ) ) {
throw X_OpFailure( op , "invalid access" );
}
}