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.
810 lines
21 KiB
C++
810 lines
21 KiB
C++
#include "externals.hh"
|
|
|
|
#include "common.hh"
|
|
#include "c-sync.hh"
|
|
|
|
#include "ui.hh"
|
|
#include "ui-odbg.hh"
|
|
#include "ui-opemu.hh"
|
|
#include "ui-profiling.hh"
|
|
#include "ui-rendertarget.hh"
|
|
#include "ui-utilities.hh"
|
|
|
|
using namespace ops;
|
|
using namespace ebcl;
|
|
|
|
|
|
/*= 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 ];
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
struct T_RunGuard
|
|
{
|
|
T_OpContext& context;
|
|
T_RunGuard( T_OpContext& ctx ) noexcept : context( ctx ) {}
|
|
|
|
~T_RunGuard( )
|
|
{
|
|
while ( !context.profiling.empty( ) ) {
|
|
UI::Profiler( ).end( context.profiling.last( ) );
|
|
context.profiling.removeLast( );
|
|
}
|
|
glUseProgram( 0 );
|
|
glBindProgramPipeline( 0 );
|
|
glBindFramebuffer( GL_FRAMEBUFFER , 0 );
|
|
UI::Textures( ).reset( );
|
|
}
|
|
};
|
|
}
|
|
|
|
void T_OpContext::run(
|
|
const E_RunTarget target ,
|
|
const float time ,
|
|
const float width ,
|
|
const float height )
|
|
{
|
|
assert( !aborted );
|
|
|
|
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 );
|
|
installOverrides.clear( );
|
|
|
|
while ( !stack.empty( ) ) {
|
|
auto const& instr{ program.ops[ instrPtr ] };
|
|
|
|
#ifdef INVASIVE_TRACES
|
|
GL_ASSERT( );
|
|
T_StringBuilder sb;
|
|
sb << "\nEXECUTE " << 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 << "\nWREG: " << wreg.f << '/' << wreg.u << '\n' << '\0';
|
|
printf( "%s" , sb.data( ) );
|
|
|
|
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" );
|
|
}
|
|
#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 ] + 1 );
|
|
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_GET_INPUT:
|
|
ensureFpuStack( instr , 0 , 1 );
|
|
x87stack[ x87sp ++ ] = Common::Sync( ).inputs( )[ 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( ).toString( );
|
|
}
|
|
|
|
pipelines[ plIndex ] = NewOwned< T_ShaderPipeline >(
|
|
UI::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 >(
|
|
UI::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" };
|
|
}
|
|
|
|
UI::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 ];
|
|
// printf( "OP_UNIFORMS %d %d" , instr.args[ 0 ] , instr.args[ 1 ] );
|
|
for ( auto i = 0u ; i <= instr.args[ 0 ] ; i ++ ) {
|
|
if ( instr.args[ 1 ] ) {
|
|
values[ i ] = uint32_t( stack[ ss - 3 - i ].f );
|
|
// printf( " %d" , values[ i ].i );
|
|
} else {
|
|
values[ i ] = stack[ ss - 3 - i ];
|
|
// printf( " %f" , values[ i ].f );
|
|
}
|
|
}
|
|
|
|
const auto sv( stack.last( ).u );
|
|
if ( sv == 0 || sv > programs.size( ) || !programs[ sv - 1 ] ) {
|
|
throw X_OpFailure{ instr , "invalid program" };
|
|
}
|
|
// printf( " -> %s %d\n" , programs[ sv - 1 ]->name( ).toOSString( ).data( ) , stack[ ss - 2 ].u );
|
|
|
|
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( int32_t( stack[ ss - 1 ].f ) ,
|
|
int32_t( stack[ ss - 2 ].f ) ,
|
|
int32_t( stack[ ss - 3 ].f ) ,
|
|
int32_t( 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;
|
|
break;
|
|
}
|
|
|
|
const auto index( sv - 1 );
|
|
if ( !framebuffers[ index ] ) {
|
|
framebuffers[ index ] = NewOwned< T_Rendertarget >( );
|
|
}
|
|
glBindFramebuffer( GL_FRAMEBUFFER , framebuffers[ index ]->id( ) );
|
|
curFb = index;
|
|
break;
|
|
}
|
|
|
|
case OP_IMAGE:
|
|
{
|
|
ensureStack( instr , 3 + ( instr.args[ 1 ] ? 1 : 0 ) );
|
|
|
|
const auto ss( stack.size( ) );
|
|
uint32_t texId = stack[ ss - 1 ].u ,
|
|
unit = stack[ ss - 2 ].f ,
|
|
level = stack[ ss - 3 ].f ,
|
|
layer = ( instr.args[ 1 ] ? stack[ ss - 4 ].f : 0 );
|
|
|
|
if ( texId == 0 || texId > textures.size( ) || !textures[ texId - 1 ] ) {
|
|
throw X_OpFailure{ instr , "invalid texture" };
|
|
}
|
|
const auto& texture( *textures[ texId - 1 ] );
|
|
|
|
if ( instr.args[ 0 ] == 0 ) {
|
|
throw X_OpFailure{ instr , "invalid access mode" };
|
|
}
|
|
static const GLenum AccessModes_[] = {
|
|
GL_READ_ONLY , GL_WRITE_ONLY , GL_READ_WRITE
|
|
};
|
|
|
|
glBindImageTexture( unit , texture.id( ) , level ,
|
|
instr.args[ 1 ] != 0 , layer ,
|
|
AccessModes_[ instr.args[ 0 ] - 1 ] ,
|
|
texture.internalFormat( ) );
|
|
|
|
stack.resize( ss - 3 - ( instr.args[ 1 ] ? 1 : 0 ) );
|
|
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_COMPUTE:
|
|
{
|
|
ensureStack( instr , 3 );
|
|
const auto ss( stack.size( ) );
|
|
const auto cpl{ UI::Shaders( ).currentPipeline( ) };
|
|
const auto pid{ cpl.valid( ) ? cpl.program( E_ShaderType::COMPUTE ) : 0 };
|
|
if ( pid ) {
|
|
int32_t wg[ 3 ];
|
|
glGetProgramiv( pid , GL_COMPUTE_WORK_GROUP_SIZE , wg );
|
|
|
|
int32_t ds[ 3 ];
|
|
for ( auto i = 0u ; i < 3u ; i ++ ) {
|
|
const auto sv{ uint32_t( stack[ ss - 1 - i ].f ) };
|
|
const auto m{ sv % wg[ i ] };
|
|
ds[ i ] = ( sv / wg[ i ] ) + ( m ? 1 : 0 );
|
|
}
|
|
glDispatchCompute( ds[ 0 ] , ds[ 1 ] , ds[ 2 ] );
|
|
}
|
|
stack.resize( ss - 3 );
|
|
break;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------
|
|
|
|
case OP_UI_PENTER:
|
|
{
|
|
T_String const& section( program.uiStrings[ instr.args[ 0 ] ] );
|
|
UI::Profiler( ).start( section );
|
|
profiling.add( section );
|
|
break;
|
|
}
|
|
|
|
case OP_UI_PEXIT:
|
|
UI::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_INPUT_OVR:
|
|
if ( !installOverrides ) {
|
|
installOverrides = NewOwned< T_SyncOverrideSection >( "*" );
|
|
}
|
|
installOverrides->merge( *( program.overrides[ instr.args[ 0 ] ] ) );
|
|
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" };
|
|
}
|
|
UI::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" );
|
|
}
|
|
}
|
|
|