From 7db090fb5dfafdaaeb193d1c3fbde0754086474a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emmanuel=20Beno=C3=AEt?= Date: Sun, 24 Dec 2017 11:10:38 +0100 Subject: [PATCH] Scripting - Image unit binding --- .vim.local/syntax/demo-srd.vim | 2 +- c-opast.cc | 21 +++++++-- c-opast.hh | 64 +++++++++++++++++++++++++ c-opcomp.cc | 11 +++++ c-opopt.cc | 26 +++++++++++ c-opparser.cc | 85 ++++++++++++++++++++++++++++++++++ c-ops.cc | 1 + c-ops.hh | 1 + ui-opemu.cc | 31 +++++++++++++ ui-texture.cc | 39 ++++++++-------- ui-texture.hh | 5 ++ 11 files changed, 262 insertions(+), 24 deletions(-) diff --git a/.vim.local/syntax/demo-srd.vim b/.vim.local/syntax/demo-srd.vim index ae9796e..824d0d2 100644 --- a/.vim.local/syntax/demo-srd.vim +++ b/.vim.local/syntax/demo-srd.vim @@ -43,7 +43,7 @@ syn keyword srdKWDefAsset program pipeline input framebuffer texture sampler syn keyword srdKWDebug profiling odbg ui-overrides syn keyword srdKWState use-texture use-framebuffer use-program use-pipeline -syn keyword srdKWState uniforms uniforms-i viewport main-output +syn keyword srdKWState uniforms uniforms-i viewport main-output image syn keyword srdKWDraw clear compute fullscreen diff --git a/c-opast.cc b/c-opast.cc index 8801fde..d60708e 100644 --- a/c-opast.cc +++ b/c-opast.cc @@ -158,9 +158,9 @@ A_Node* opast::ASTVisitorBrowser( break; } - // Clear instruction, compute instruction - case A_Node::OP_CLEAR: - case A_Node::OP_COMPUTE: + // Instructions that match the common case, in which chidren are + // listed directly + case A_Node::OP_CLEAR: case A_Node::OP_COMPUTE: { const auto nArgs( node.size( ) ); if ( child < nArgs ) { @@ -169,6 +169,21 @@ A_Node* opast::ASTVisitorBrowser( break; } + // Instructions that may have undefined children at the end of + // their lists; we need to find the first non-NULL child in the + // list and start from there. + case A_Node::OP_IMAGE: + { + auto nArgs( node.size( ) ); + while ( nArgs > 0 && !node.child( nArgs - 1 ) ) { + nArgs --; + } + if ( child < nArgs ) { + return node.child( nArgs - child - 1 ).get( ); + } + break; + } + // Conditional instruction & associated technical nodes case A_Node::OP_COND: { diff --git a/c-opast.hh b/c-opast.hh index 5178e73..8ac4a21 100644 --- a/c-opast.hh +++ b/c-opast.hh @@ -29,6 +29,7 @@ class A_Node OP_COND , // Conditional instruction OP_FRAMEBUFFER ,// Define framebuffer OP_FULLSCREEN , // Draw a fullscreen quad + OP_IMAGE , // Image unit binding OP_INPUT , // Input declaration OP_LOCALS , // Declare local variables OP_MAINOUT , // Select the output buffer @@ -1289,6 +1290,69 @@ class T_ViewportInstrNode : public A_InstructionNode { return (T_ArgumentNode&) *children_[ int( p ) ]; } }; +// Image unit binding +class T_ImageInstrNode : public A_InstructionNode +{ + public: + enum class E_AccessMode + { + READ , + WRITE + }; + using T_AccessMode = T_Flags< E_AccessMode >; + + enum E_Parameter { + P_UNIT , P_LEVEL , P_LAYER + }; + + private: + T_AccessMode accessMode_ = { }; + T_String id_; + ebcl::T_SRDLocation idLocation_; + + public: + T_ImageInstrNode( T_InstrListNode& parent ) noexcept + : A_InstructionNode( OP_IMAGE , parent ) + { + children_.add( P_ArgumentNode{} ); + children_.add( P_ArgumentNode{} ); + children_.add( P_ArgumentNode{} ); + } + + //---------------------------------------------------------------------- + + T_String const& id( ) const noexcept + { return id_; } + ebcl::T_SRDLocation const& idLocation( ) const noexcept + { return idLocation_; } + void id( T_String const& nid , + ebcl::T_SRDLocation const& location ) noexcept + { id_ = nid; idLocation_ = location; } + + //---------------------------------------------------------------------- + + T_AccessMode accessMode( ) const noexcept + { return accessMode_; } + void accessMode( T_AccessMode mode ) noexcept + { assert( mode ); accessMode_ = mode; } + + //---------------------------------------------------------------------- + + void setParameter( const E_Parameter p , + P_ExpressionNode value ) noexcept + { + if ( value ) { + children_[ int( p ) ] = NewOwned< T_ArgumentNode >( + *this , std::move( value ) ); + } + } + + bool hasParameter( const E_Parameter p ) const noexcept + { return bool( children_[ int( p ) ] ); } + T_ArgumentNode& parameter( const E_Parameter p ) const noexcept + { return (T_ArgumentNode&) *children_[ int( p ) ]; } +}; + /*= EXPRESSIONS ==============================================================*/ diff --git a/c-opcomp.cc b/c-opcomp.cc index 23b5d6c..75c0788 100644 --- a/c-opcomp.cc +++ b/c-opcomp.cc @@ -584,6 +584,17 @@ bool T_CompilerImpl_::compileNode( } 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 ); diff --git a/c-opopt.cc b/c-opopt.cc index ccb1599..c553b52 100644 --- a/c-opopt.cc +++ b/c-opopt.cc @@ -644,6 +644,12 @@ void BUDCVisitor_( true , od , root ); break; + case A_Node::OP_IMAGE: + BUDCAddRecord_( n , + dynamic_cast< T_ImageInstrNode& >( n ).id( ) , + true , od , root ); + break; + case A_Node::OP_PIPELINE: { auto& pln{ dynamic_cast< T_PipelineInstrNode& >( n ) }; BUDCAddRecord_( n , pln.id( ) , false , od , root ); @@ -2134,6 +2140,26 @@ bool opopt::InlineFunctions( break; } + case A_Node::OP_IMAGE: { + auto& n{ (T_ImageInstrNode&) node }; + if ( src.isArgument( n.id( ) ) ) { + const auto argIndex{ src.getLocalIndex( n.id( ) ) }; + auto const& callArg{ call.argument( argIndex ) }; + auto const& caValue{ callArg.expression( ) }; + assert( caValue.type( ) == A_Node::EXPR_ID ); + auto const& repId{ ( (T_IdentifierExprNode&) caValue ).id( ) }; + oData.logger( [&]() { + T_StringBuilder sb; + sb << "Replacing identifier " << n.id( ) + << " at " << n.idLocation( ) + << " with identifier " << repId; + return sb; + } , 6 ); + n.id( repId , caValue.location( ) ); + } + break; + } + case A_Node::OP_PIPELINE: { auto& n{ (T_PipelineInstrNode&) node }; const auto np{ n.size( ) }; diff --git a/c-opparser.cc b/c-opparser.cc index 663e164..d19cc78 100644 --- a/c-opparser.cc +++ b/c-opparser.cc @@ -47,6 +47,7 @@ struct T_ParserImpl_ USE_PIPELINE , USE_PROGRAM , USE_TEXTURE , + IMAGE , VIEWPORT , }; @@ -79,6 +80,7 @@ struct T_ParserImpl_ add( "use-pipeline" , E_InstrType::USE_PIPELINE ); add( "use-program" , E_InstrType::USE_PROGRAM ); add( "use-texture" , E_InstrType::USE_TEXTURE ); + add( "image" , E_InstrType::IMAGE ); add( "viewport" , E_InstrType::VIEWPORT ); return temp; @@ -287,6 +289,7 @@ struct T_ParserImpl_ T_SRDList const& input , T_UseInstrNode::E_Type type ) noexcept; M_DPARSER_( UseTexture ); + M_DPARSER_( Image ); M_DPARSER_( Viewport ); @@ -786,6 +789,14 @@ bool T_ParserImpl_::checkIdentifiers( ) noexcept return true; } + case A_Node::OP_IMAGE: + { + auto& instr( dynamic_cast< T_ImageInstrNode& >( n ) ); + checkIdentifier( instr.id( ) , instr.idLocation( ) , cfi , + E_DataType::TEXTURE ); + return true; + } + case A_Node::OP_ODBG: { auto& t( dynamic_cast< T_OutputDebugInstrNode& >( n ) ); @@ -1084,6 +1095,7 @@ void T_ParserImpl_::parseInstructions( M_CASE_( USE_PIPELINE , UsePipeline ); M_CASE_( USE_PROGRAM , UseProgram ); M_CASE_( USE_TEXTURE , UseTexture ); + M_CASE_( IMAGE , Image ); M_CASE_( VIEWPORT , Viewport ); case E_InstrType::MAINOUT: @@ -1934,6 +1946,79 @@ M_INSTR_( UseTexture ) /*----------------------------------------------------------------------------*/ +M_INSTR_( Image ) +{ + // (image [{read|write|rw}] + // [(layer ( ) }; + instr.location( ) = input[ 0 ].location( ); + instr.id( input[ 2 ].stringValue( ) , input[ 2 ].location( ) ); + instr.setParameter( T_ImageInstrNode::P_UNIT , + parseExpression( instr , input[ 1 ] ) ); + instr.setParameter( T_ImageInstrNode::P_LEVEL , + parseExpression( instr , input[ 3 ] ) ); + + using E_AM_ = T_ImageInstrNode::E_AccessMode; + using T_AM_ = T_ImageInstrNode::T_AccessMode; + T_AM_ mode{ E_AM_::READ , E_AM_::WRITE }; + + uint32_t nck = 4; + if ( nck < input.size( ) && input[ nck ].type( ) == E_SRDTokenType::WORD ) { + auto const& v{ input[ nck ].stringValue( ) }; + if ( v == "read" ) { + mode = E_AM_::READ; + } else if ( v == "write" ) { + mode = E_AM_::WRITE; + } else if ( v != "rw" ) { + errors.addNew( "invalid access mode" , + input[ nck ].location( ) ); + } + nck ++; + } + instr.accessMode( mode ); + + if ( nck < input.size( ) && input[ nck ].type( ) == E_SRDTokenType::LIST ) { + auto const& lst{ input[ nck ].list( ) }; + if ( lst.size( ) < 2 || lst[ 0 ].type( ) != E_SRDTokenType::WORD + || lst[ 0 ].stringValue( ) != "layer" ) { + errors.addNew( "invalid layer specification" , + lst.size( ) ? lst[ 0 ].location( ) + : input[ nck].location( ) ); + } else { + instr.setParameter( T_ImageInstrNode::P_LAYER , + parseExpression( instr , lst[ 1 ] ) ); + if ( lst.size( ) > 2 ) { + errors.addNew( "too many arguments" , + lst[ 2 ].location( ) ); + } + } + nck ++; + } + + if ( nck < input.size( ) ) { + errors.addNew( "too many arguments" , input[ nck ].location( ) ); + } +} + +/*----------------------------------------------------------------------------*/ + M_INSTR_( Viewport ) { auto& instr{ instructions.add< T_ViewportInstrNode >( ) }; diff --git a/c-ops.cc b/c-ops.cc index 5bc7030..fcc6372 100644 --- a/c-ops.cc +++ b/c-ops.cc @@ -135,6 +135,7 @@ static T_KeyValueTable< E_OpType , T_OpInfo > OpInfoTable_{ ([]() { infos.add( E_OpType::OP_USE_TEXTURE , T_OpInfo{ "use-texture" , 1 , OpStackMain{ -2 } } ); infos.add( E_OpType::OP_UNIFORMS , T_OpInfo{ "uniforms" , 2 , OpStackMain{ -2 } } ); infos.add( E_OpType::OP_VIEWPORT , T_OpInfo{ "viewport" , 0 , OpStackMain{ -4 } } ); + infos.add( E_OpType::OP_IMAGE , T_OpInfo{ "image" , 2 , OpStackMain{ -3 } } ); // infos.add( E_OpType::OP_FULLSCREEN , T_OpInfo{ "fullscreen" } ); infos.add( E_OpType::OP_CLEAR , T_OpInfo{ "clear" , 0 , OpStackMain{ -4 } } ); diff --git a/c-ops.hh b/c-ops.hh index 37e66fd..28c100a 100644 --- a/c-ops.hh +++ b/c-ops.hh @@ -66,6 +66,7 @@ enum E_OpType OP_USE_TEXTURE , OP_UNIFORMS , OP_VIEWPORT , + OP_IMAGE , // OP_FULLSCREEN , OP_COMPUTE , diff --git a/ui-opemu.cc b/ui-opemu.cc index 39fe31f..6885502 100644 --- a/ui-opemu.cc +++ b/ui-opemu.cc @@ -661,6 +661,37 @@ void T_OpContext::run( break; } + case OP_IMAGE: + { + ensureStack( instr , 3 + ( instr.args[ 1 ] ? 1 : 0 ) ); + + const auto ss( stack.size( ) ); + uint32_t texId = stack[ ss - 1 ].f , + 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 ] ] , + texture.internalFormat( ) ); + + stack.resize( ss - 3 - ( instr.args[ 1 ] ? 1 : 0 ) ); + break; + } + // -------------------------------------------------------------------------------- case OP_FULLSCREEN: diff --git a/ui-texture.cc b/ui-texture.cc index 309d8aa..70f1461 100644 --- a/ui-texture.cc +++ b/ui-texture.cc @@ -20,42 +20,41 @@ T_Texture::T_Texture( glGenTextures( 1 , &id_ ); glBindTexture( GL_TEXTURE_2D , id_ ); - GLenum ifmt , fmt , dt; switch ( type ) { case E_TexType::RGBA8: - ifmt = GL_RGBA8; - fmt = GL_RGBA; - dt = GL_UNSIGNED_BYTE; + ifmt_ = GL_RGBA8; + fmt_ = GL_RGBA; + dt_ = GL_UNSIGNED_BYTE; break; case E_TexType::RGBA16F: - ifmt = GL_RGBA16F; - fmt = GL_RGBA; - dt = GL_FLOAT; + ifmt_ = GL_RGBA16F; + fmt_ = GL_RGBA; + dt_ = GL_FLOAT; break; case E_TexType::RGB8: - ifmt = GL_RGB8; - fmt = GL_RGB; - dt = GL_UNSIGNED_BYTE; + ifmt_ = GL_RGB8; + fmt_ = GL_RGB; + dt_ = GL_UNSIGNED_BYTE; break; case E_TexType::RGB16F: - ifmt = GL_RGB16F; - fmt = GL_RGB; - dt = GL_FLOAT; + ifmt_ = GL_RGB16F; + fmt_ = GL_RGB; + dt_ = GL_FLOAT; break; case E_TexType::R8: - ifmt = GL_R8; - fmt = GL_RED; - dt = GL_UNSIGNED_BYTE; + ifmt_ = GL_R8; + fmt_ = GL_RED; + dt_ = GL_UNSIGNED_BYTE; break; case E_TexType::R16F: - ifmt = GL_R16F; - fmt = GL_RED; - dt = GL_FLOAT; + ifmt_ = GL_R16F; + fmt_ = GL_RED; + dt_ = GL_FLOAT; break; } @@ -72,7 +71,7 @@ T_Texture::T_Texture( printf( "init %p txid %d lv %d sz %dx%d\n" , this , id_ , i , w , h ); #endif - glTexImage2D( GL_TEXTURE_2D , i , ifmt , w , h , 0 , fmt , dt , nullptr ); + glTexImage2D( GL_TEXTURE_2D , i , ifmt_ , w , h , 0 , fmt_ , dt_ , nullptr ); w >>= 1; h >>= 1; assert( w && h ); diff --git a/ui-texture.hh b/ui-texture.hh index 09f9022..4448dcb 100644 --- a/ui-texture.hh +++ b/ui-texture.hh @@ -31,8 +31,13 @@ struct T_Texture uint32_t width( ) const noexcept { return width_; } uint32_t height( ) const noexcept { return height_; } + uint32_t format( ) const noexcept { return fmt_; } + uint32_t internalFormat( ) const noexcept { return ifmt_; } + uint32_t dataType_( ) const noexcept { return dt_; } + private: GLuint id_; + uint32_t fmt_ , ifmt_ , dt_; uint32_t levels_ , width_ , height_; int32_t debugIndex_; };