From c05ab54ca7a5596fe4ab2cac7af13dbab6ff26e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emmanuel=20Beno=C3=AEt?= Date: Tue, 14 Nov 2017 17:35:13 +0100 Subject: [PATCH] Finished implementation of the emulator It's probably full of bugs so meh --- control.hh | 12 +++ opcomp.cc | 26 ++++- ops.cc | 271 +++++++++++++++++++++++++++++++++++++++++++++++- rendertarget.cc | 61 +++++++++-- rendertarget.hh | 7 +- shaders.cc | 32 ++++++ shaders.hh | 15 ++- 7 files changed, 404 insertions(+), 20 deletions(-) diff --git a/control.hh b/control.hh index ca73896..4cd7e3e 100644 --- a/control.hh +++ b/control.hh @@ -2,6 +2,8 @@ #include "sync.hh" #include "opast.hh" +struct T_Rendertarget; + namespace ops { using namespace ebcl; @@ -25,6 +27,7 @@ enum E_OpType OP_STORE , OP_SLOAD , OP_CONST , + OP_OFFSET , // OP_FP_LOAD , OP_FP_STORE , @@ -49,6 +52,7 @@ enum E_OpType OP_FP_EXP , OP_FP_LN , // + OP_GEN_ASSETS , OP_INIT_PIPELINE , OP_INIT_PROGRAM , OP_INIT_SAMPLER , @@ -166,7 +170,15 @@ struct T_OpContext double x87stack[ 8 ]; // x87 FPU emulation stack uint32_t x87sp; // x87 FPU emulation stack pointer + // Allocated resources + T_Array< T_OwnPtr< T_Rendertarget > > framebuffers; + T_Array< T_OwnPtr< T_ShaderProgram > > programs; + T_Array< T_OwnPtr< T_ShaderPipeline > > pipelines; + T_Array< T_OwnPtr< T_TextureSampler > > samplers; + T_Array< T_OwnPtr< T_Texture > > textures; + T_Array< T_String > profiling; // Profiling sections that have been started + int32_t curFb{ -1 }; // Index of current framebuffer enum E_RunTarget { R_INIT , diff --git a/opcomp.cc b/opcomp.cc index 706a4a1..3fd0aec 100644 --- a/opcomp.cc +++ b/opcomp.cc @@ -334,6 +334,28 @@ bool T_CompilerImpl_::compileNode( } 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 ); @@ -448,7 +470,7 @@ bool T_CompilerImpl_::compileNode( pn.pLocation( i ) ); } processIdentifier( funcIndex , pn.id( ) , pn.location( ) ); - addInstruction( OP_INIT_PIPELINE , pn.size( ) , pn.location( ) ); + addInstruction( OP_INIT_PIPELINE , pn.size( ) - 1 , pn.location( ) ); sdMain -= pn.size( ); } break; @@ -519,7 +541,7 @@ bool T_CompilerImpl_::compileNode( sdMain --; } if ( id ) { - addInstruction( OP_FB_TOGGLE , { id , 1 } , fan.location( ) ); + addInstruction( OP_FB_TOGGLE , { id - 1 , 1 } , fan.location( ) ); } } break; diff --git a/ops.cc b/ops.cc index b092a52..d107ac3 100644 --- a/ops.cc +++ b/ops.cc @@ -1,8 +1,9 @@ #include "externals.hh" #include "control.hh" #include "globals.hh" -#include "sync.hh" #include "profiling.hh" +#include "rendertarget.hh" +#include "sync.hh" using namespace ops; using namespace ebcl; @@ -98,6 +99,7 @@ static T_KeyValueTable< E_OpType , T_OpInfo > OpInfoTable_{ ([]() { infos.add( E_OpType::OP_STORE , T_OpInfo{ "store" , 1 } ); infos.add( E_OpType::OP_SLOAD , T_OpInfo{ "load-stack" , 1 } ); infos.add( E_OpType::OP_CONST , T_OpInfo{ "const" , 1 } ); + infos.add( E_OpType::OP_OFFSET , T_OpInfo{ "offset" , 1 } ); // 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 } } ); @@ -122,6 +124,7 @@ static T_KeyValueTable< E_OpType , T_OpInfo > OpInfoTable_{ ([]() { 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 } ); infos.add( E_OpType::OP_INIT_PIPELINE , T_OpInfo{ "pipeline" , 1 , OpStackMain{ -1 } } ); infos.add( E_OpType::OP_INIT_PROGRAM , T_OpInfo{ "program" , 1 } ); infos.add( E_OpType::OP_INIT_TEXTURE , T_OpInfo{ "texture" , 2 , OpStackMain{ -3 } } ); @@ -228,6 +231,12 @@ T_OpContext::T_OpContext( values.resize( ts ); initialInputs.resize( program.inputs.size( ) ); + framebuffers.resize( program.nFramebuffers ); + pipelines.resize( program.nPipelines ); + programs.resize( program.nPrograms ); + samplers.resize( program.nSamplers ); + textures.resize( program.nTextures ); + for ( auto i = 0u ; i < nc ; i ++ ) { values[ i + 3 ] = program.constants[ nc ]; } @@ -324,6 +333,7 @@ void T_OpContext::run( break; case OP_CONST: + case OP_OFFSET: wreg = instr.args[ 0 ]; break; @@ -505,11 +515,216 @@ void T_OpContext::run( // -------------------------------------------------------------------------------- - // TODO resource init + 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 lods( instr.args[ 1 ] ? stack.last( ).f : 1 ); + if ( instr.args[ 1 ] ) { + stack.removeLast( ); + } + const uint32_t h( stack.last( ).f ); + stack.removeLast( ); + const uint32_t w( stack.last( ).f ); + 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; + } // -------------------------------------------------------------------------------- - // TODO resource usage + 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" }; + } + if ( instr.args[ 0 ] == 0 ) { + throw X_OpFailure{ instr , "invalid argument" }; + } + framebuffers[ curFb ]->toggle( instr.args[ 0 ] + 1 , instr.args[ 1 ] ); + break; + } case OP_UNIFORMS: { @@ -518,10 +733,23 @@ void T_OpContext::run( const auto ss( stack.size( ) ); T_OpValue values[ 4 ]; for ( auto i = 0u ; i <= instr.args[ 0 ] ; i ++ ) { - values[ i ] = stack[ ss - 2 - i ]; + values[ i ] = stack[ ss - 3 - i ]; } - // FIXME can't actually finish this, I'm stupid. + 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 ]->id( ) , stack[ ss - 2 ].u , 1 , values ); + stack.resize( ss - 3 - instr.args[ 0 ] ); break; } @@ -535,6 +763,26 @@ void T_OpContext::run( 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: @@ -571,6 +819,19 @@ void T_OpContext::run( 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 ++; diff --git a/rendertarget.cc b/rendertarget.cc index d40b5ea..fcaa883 100644 --- a/rendertarget.cc +++ b/rendertarget.cc @@ -7,8 +7,8 @@ T_RendertargetSetup::T_RendertargetSetup( ) { } T_RendertargetSetup& T_RendertargetSetup::add( - __rw__ T_Texture& texture , - __rd__ const uint32_t level ) + T_Texture& texture , + const uint32_t level ) { checkAttachment( texture , level ); colorAttachments_.add( T_Attachment_{ texture , level } ); @@ -17,7 +17,7 @@ T_RendertargetSetup& T_RendertargetSetup::add( T_RendertargetSetup& T_RendertargetSetup::depth( - __rw__ T_Texture& texture ) + T_Texture& texture ) { assert( !depthAttachment_ ); checkAttachment( texture , 0 ); @@ -27,8 +27,8 @@ T_RendertargetSetup& T_RendertargetSetup::depth( void T_RendertargetSetup::checkAttachment( - __rw__ T_Texture const& texture , - __rd__ const uint32_t level ) + T_Texture const& texture , + const uint32_t level ) { const uint32_t tw( texture.width( ) >> level ) , th( texture.height( ) >> level ); @@ -87,16 +87,38 @@ T_Rendertarget T_RendertargetSetup::create( ) /*============================================================================*/ +T_Rendertarget::T_Rendertarget( ) +{ + glGenFramebuffers( 1 , &id_ ); + nCats_ = width_ = height_ = 0; +} + +void T_Rendertarget::attach( + T_Texture const& texture , + const uint32_t lod , + const uint32_t id ) +{ + const uint32_t attId( id ? ( GL_COLOR_ATTACHMENT0 + id - 1 ) : GL_DEPTH_ATTACHMENT ); + glFramebufferTexture( GL_FRAMEBUFFER , + attId , + texture.id( ) , + lod ); + if ( width_ == 0 ) { + width_ = texture.width( ) >> lod; + height_ = texture.width( ) >> lod; + } +} + T_Rendertarget::T_Rendertarget( - __rd__ const uint32_t id , - __rd__ const uint32_t nCats , - __rd__ const uint32_t width , - __rd__ const uint32_t height ) + const uint32_t id , + const uint32_t nCats , + const uint32_t width , + const uint32_t height ) : id_( id ) , nCats_( nCats ) , width_( width ) , height_( height ) { } T_Rendertarget::T_Rendertarget( - __rw__ T_Rendertarget&& other ) noexcept + T_Rendertarget&& other ) noexcept : id_( 0 ) , nCats_( other.nCats_ ) , width_( other.width_ ) , height_( other.height_ ) { @@ -104,7 +126,7 @@ T_Rendertarget::T_Rendertarget( } T_Rendertarget& T_Rendertarget::operator =( - __rw__ T_Rendertarget&& other ) noexcept + T_Rendertarget&& other ) noexcept { swap( id_ , other.id_ ); swap( nCats_ , other.nCats_ ); @@ -148,6 +170,23 @@ bool T_Rendertarget::activate( ) return id_ != 0; } +void T_Rendertarget::toggle( + const uint32_t id , + const bool active ) +{ + assert( id > 0 ); + const auto realId( GL_COLOR_ATTACHMENT0 + id - 1 ); + const int32_t idx( buffers_.indexOf( realId ) ); + if ( active && idx < 0 ) { + buffers_.add( realId ); + } else if ( !active && idx >= 0 ) { + buffers_.removeSwap( idx ); + } else { + return; + } + glDrawBuffers( buffers_.size( ) , &buffers_[ 0 ] ); +} + void T_Rendertarget::MainOutput( ) { auto const& dspSize( ImGui::GetIO( ).DisplaySize ); diff --git a/rendertarget.hh b/rendertarget.hh index 25c1308..f719ac7 100644 --- a/rendertarget.hh +++ b/rendertarget.hh @@ -49,9 +49,9 @@ struct T_RendertargetSetup struct T_Rendertarget { - T_Rendertarget( ) = delete; T_Rendertarget( T_Rendertarget const& ) = delete; + T_Rendertarget( ); T_Rendertarget( const GLuint id , const uint32_t nCats , @@ -64,6 +64,11 @@ struct T_Rendertarget ~T_Rendertarget( ); + void attach( T_Texture const& texture , + uint32_t lod , + uint32_t id ); + void toggle( uint32_t id , bool active ); + bool activate( ); GLuint id( ) const { return id_; } diff --git a/shaders.cc b/shaders.cc index 612f139..151ccac 100644 --- a/shaders.cc +++ b/shaders.cc @@ -700,6 +700,38 @@ T_ShaderPipeline T_ShaderManager::pipeline( return T_ShaderPipeline{ p.idString }; } +T_ShaderPipeline T_ShaderManager::pipeline( + T_String const* shaderNames , + uint32_t count ) +{ + if ( count < 1 || count > 5 ) { + return {}; + } + + const T_String id( ([shaderNames,count] () { + T_StringBuilder sb; + for ( auto i = 0u ; i < count ; i ++ ) { + sb << shaderNames[ i ] << '|'; + } + sb << '\0'; + return T_String{ std::move( sb.data( ) ) }; + } )() ); + + auto const* existing( pipelines_.get( id ) ); + if ( existing ) { + return T_ShaderPipeline{ existing->idString }; + } + + pipelines_.add( T_Pipeline_{ id , shaderNames , count } ); + auto& p( *pipelines_.get( id ) ); + for ( auto i = 0u ; i < count ; i ++ ) { + loadProgram( p.idString , shaderNames[ i ] ); + } + initPipeline( p ); + + return T_ShaderPipeline{ p.idString }; +} + /*----------------------------------------------------------------------------*/ void T_ShaderManager::update( ) diff --git a/shaders.hh b/shaders.hh index 40bdf7f..1ae2ea8 100644 --- a/shaders.hh +++ b/shaders.hh @@ -181,6 +181,9 @@ struct T_ShaderManager T_ShaderProgram program( T_String const& name ); T_ShaderPipeline pipeline( std::initializer_list< T_String > shaders ); + T_ShaderPipeline pipeline( + T_String const* shaderNames , + uint32_t count ); void update( ); @@ -194,12 +197,22 @@ struct T_ShaderManager T_String idString; uint32_t references = 0; GLuint id = 0; - T_Array< T_String > programs{ 8 }; + T_Array< T_String > programs{ 6 }; T_Pipeline_( T_String const& id , std::initializer_list< T_String > shaders ) : idString{ id } , programs{ shaders } {} + + T_Pipeline_( T_String const& id , + T_String const* const shaderNames , + const uint32_t count ) noexcept + : idString{ id } + { + for ( auto i = 0u ; i < count ; i ++ ) { + programs.add( shaderNames[ i ] ); + } + } }; struct T_Program_