diff --git a/Makefile b/Makefile index 0eca841..ca05cc8 100644 --- a/Makefile +++ b/Makefile @@ -28,6 +28,7 @@ COMMON = \ c-camera.cc \ c-filewatcher.cc \ c-project.cc \ + c-shaders.cc \ c-utilities.cc \ c-undo.cc \ \ diff --git a/c-shaders.cc b/c-shaders.cc new file mode 100644 index 0000000..38a15b6 --- /dev/null +++ b/c-shaders.cc @@ -0,0 +1,172 @@ +#include "externals.hh" +#include "c-shaders.hh" + + +namespace { + +const std::map< std::string , E_ShaderInput > InputTypes_( ([] { + std::map< std::string , E_ShaderInput > t; + t.emplace( "chunk" , E_ShaderInput::CHUNK ); + t.emplace( "library" , E_ShaderInput::LIBRARY ); + t.emplace( "lib" , E_ShaderInput::LIBRARY ); + t.emplace( "vertex" , E_ShaderInput::VERTEX ); + t.emplace( "fragment" , E_ShaderInput::FRAGMENT ); + t.emplace( "compute" , E_ShaderInput::COMPUTE ); + t.emplace( "geo" , E_ShaderInput::GEOMETRY ); + t.emplace( "geometry" , E_ShaderInput::GEOMETRY ); + return t; +})()); + +const std::regex PreprocDirective_( "^\\s*//!\\s*([a-z]+(\\s+([^\\s]+))*)\\s*$" ); + +} // namespace + + +/*============================================================================*/ +namespace { + +// Input reader state and functions, used when loading a source file +struct T_InputReader_ +{ + using T_Tokens_ = std::vector< std::string >; + + FILE* const file; + T_ShaderInput& input; + uint32_t line{ 0 }; + + char* buffer{ nullptr }; + ssize_t readCount; + + std::string accumulator{ }; + uint32_t accumLines{ 0 }; + + T_InputReader_( FILE* const file , + T_ShaderInput& input ) + : file( file ) , input( input ) + { } + ~T_InputReader_( ); + + void read( ); + + void handleDirective( T_Tokens_ const& tokens ); + + void error( T_String const& err ); + void nl( ); + void addAccumulated( ); +}; + +/*----------------------------------------------------------------------------*/ + +T_InputReader_::~T_InputReader_( ) +{ + if ( buffer ) { + free( buffer ); + } + fclose( file ); +} + +void T_InputReader_::read( ) +{ + size_t bsz( 0 ); + while ( ( readCount = getline( &buffer , &bsz , file ) ) != -1 ) { + line ++; + std::cmatch match; + if ( std::regex_match( buffer , match , PreprocDirective_ ) ) { + const T_Tokens_ tokens( ([](std::string const& a) { + using stri = std::istream_iterator< std::string >; + std::istringstream iss( a ); + return T_Tokens_{ stri( iss ) , stri( ) }; + })( match[ 1 ].str( ) ) ); + assert( tokens.size( ) >= 1 ); + handleDirective( tokens ); + } else { + accumulator += buffer; + accumLines ++; + } + } + addAccumulated( ); +} + +/*----------------------------------------------------------------------------*/ + +void T_InputReader_::handleDirective( + T_Tokens_ const& tokens ) +{ + auto const& directive( tokens[ 0 ] ); + + if ( directive == "include" ) { + if ( tokens.size( ) != 2 ) { + nl( ); + error( "invalid arguments" ); + return; + } + addAccumulated( ); + auto& ck( input.chunks ); + ck.addNew( E_ShaderInputChunk::INCLUDE , tokens[ 1 ].c_str( ) , 1 ); + + } else if ( directive == "type" ) { + nl( ); + if ( tokens.size( ) != 2 ) { + error( "invalid arguments" ); + return; + } + auto pos( InputTypes_.find( tokens[ 1 ] ) ); + if ( pos == InputTypes_.end( ) ) { + error( "unknown type" ); + } else { + input.type = pos->second; + } + + } else { + nl( ); + error( "unknown directive" ); + } +} + +/*----------------------------------------------------------------------------*/ + +void T_InputReader_::error( + T_String const& err ) +{ + input.errors.addNew( line , err ); +} + +void T_InputReader_::nl( ) +{ + accumLines ++; + accumulator += '\n'; +} + +void T_InputReader_::addAccumulated( ) +{ + if ( accumLines ) { + auto& ck( input.chunks ); + ck.addNew( E_ShaderInputChunk::CODE , + accumulator.c_str( ) , accumLines ); + accumulator = {}; + accumLines = 0; + } +} + +} // namespace + + +/*= T_ShaderInput ============================================================*/ + +bool T_ShaderInput::load( + T_String const& path ) +{ + type = E_ShaderInput::CHUNK; + chunks.clear( ); + errors.clear( ); + + FILE* const file{ fopen( (char const*) path.toOSString( ).data( ) , "r" ) }; + if ( !file ) { + return false; + } + + T_InputReader_ reader( file , *this ); + reader.read( ); + + return true; +} diff --git a/c-shaders.hh b/c-shaders.hh new file mode 100644 index 0000000..5ea3741 --- /dev/null +++ b/c-shaders.hh @@ -0,0 +1,98 @@ +#pragma once +#ifndef REAL_BUILD +# include "externals.hh" +#endif + + +/*= COMMON PROPERTIES ========================================================*/ + +// Type of shader +enum class E_ShaderType { + VERTEX , FRAGMENT , GEOMETRY , + COMPUTE , + __COUNT__ +}; + +// Errors in shader code - the errors it represents may come from either +// the input loader, the shader loader or the driver. +struct T_ShaderError +{ + T_String source; + uint32_t line; + T_String error; + + T_ShaderError( ) = default; + + T_ShaderError( T_String source , + const uint32_t line , + T_String error ) + : source( std::move( source ) ) , line( line ) , + error( std::move( error ) ) + { } + + T_ShaderError( T_String source , + const uint32_t line , + T_StringBuilder& error ) + : source( std::move( source ) ) , line( line ) , + error( std::move( error ) ) + { } +}; + + +/*= INPUT FILES ==============================================================*/ + +// Type of input chunk +enum class E_ShaderInputChunk { + CODE , + INCLUDE , +}; + +// Input chunk data +struct T_ShaderInputChunk +{ + E_ShaderInputChunk type; + T_String text; + uint32_t lines; + + T_ShaderInputChunk( ) = default; + T_ShaderInputChunk( + const E_ShaderInputChunk type , + T_String text , + const uint32_t lines ) + : type( type ) , text( std::move( text ) ) , lines( lines ) + { } +}; + +// Input file type +enum class E_ShaderInput { + CHUNK , // Chunk that may be repeated + LIBRARY , // Library (will only be loaded once) + + // "Main" shader source files + VERTEX , FRAGMENT , GEOMETRY , + COMPUTE +}; + +// Preprocessing errors +struct T_ShaderInputError +{ + uint32_t line; + T_String error; + + T_ShaderInputError( + const uint32_t line , + T_String error ) + : line( line ) , error( std::move( error ) ) + { } +}; + +// Source file +struct T_ShaderInput +{ + E_ShaderInput type = E_ShaderInput::CHUNK; + T_Array< T_ShaderInputChunk > chunks; + T_Array< T_ShaderInputError > errors; + + bool load( T_String const& path ); +}; +using P_ShaderInput = T_OwnPtr< T_ShaderInput >; diff --git a/ui-shaders.cc b/ui-shaders.cc index 72dc13d..e5aabe7 100644 --- a/ui-shaders.cc +++ b/ui-shaders.cc @@ -12,22 +12,8 @@ namespace { -const std::regex PreprocDirective_( "^\\s*//!\\s*([a-z]+(\\s+([^\\s]+))*)\\s*$" ); const std::regex GLSLErrorNv_( "^[0-9]*\\(([0-9]+).*$" ); -const std::map< std::string , E_ShaderInput > InputTypes_( ([] { - std::map< std::string , E_ShaderInput > t; - t.emplace( "chunk" , E_ShaderInput::CHUNK ); - t.emplace( "library" , E_ShaderInput::LIBRARY ); - t.emplace( "lib" , E_ShaderInput::LIBRARY ); - t.emplace( "vertex" , E_ShaderInput::VERTEX ); - t.emplace( "fragment" , E_ShaderInput::FRAGMENT ); - t.emplace( "compute" , E_ShaderInput::COMPUTE ); - t.emplace( "geo" , E_ShaderInput::GEOMETRY ); - t.emplace( "geometry" , E_ShaderInput::GEOMETRY ); - return t; -})()); - const GLenum ProgramTypes_[] = { GL_VERTEX_SHADER , GL_FRAGMENT_SHADER , @@ -45,154 +31,8 @@ const GLbitfield PipelineStages_[] = { static_assert( sizeof( PipelineStages_ ) / sizeof( GLbitfield ) == size_t( E_ShaderType::__COUNT__ ) , "missing pipeline stage constants" ); - -/*============================================================================*/ - -// Input reader state and functions, used when loading a source file -struct T_InputReader_ -{ - using T_Tokens_ = std::vector< std::string >; - - FILE* const file; - T_ShaderInput& input; - uint32_t line{ 0 }; - - char* buffer{ nullptr }; - ssize_t readCount; - - std::string accumulator{ }; - uint32_t accumLines{ 0 }; - - T_InputReader_( FILE* const file , - T_ShaderInput& input ) - : file( file ) , input( input ) - { } - ~T_InputReader_( ); - - void read( ); - - void handleDirective( T_Tokens_ const& tokens ); - - void error( T_String const& err ); - void nl( ); - void addAccumulated( ); -}; - -/*----------------------------------------------------------------------------*/ - -T_InputReader_::~T_InputReader_( ) -{ - if ( buffer ) { - free( buffer ); - } - fclose( file ); } -void T_InputReader_::read( ) -{ - size_t bsz( 0 ); - while ( ( readCount = getline( &buffer , &bsz , file ) ) != -1 ) { - line ++; - std::cmatch match; - if ( std::regex_match( buffer , match , PreprocDirective_ ) ) { - const T_Tokens_ tokens( ([](std::string const& a) { - using stri = std::istream_iterator< std::string >; - std::istringstream iss( a ); - return T_Tokens_{ stri( iss ) , stri( ) }; - })( match[ 1 ].str( ) ) ); - assert( tokens.size( ) >= 1 ); - handleDirective( tokens ); - } else { - accumulator += buffer; - accumLines ++; - } - } - addAccumulated( ); -} - -/*----------------------------------------------------------------------------*/ - -void T_InputReader_::handleDirective( - T_Tokens_ const& tokens ) -{ - auto const& directive( tokens[ 0 ] ); - - if ( directive == "include" ) { - if ( tokens.size( ) != 2 ) { - nl( ); - error( "invalid arguments" ); - return; - } - addAccumulated( ); - auto& ck( input.chunks ); - ck.addNew( E_ShaderInputChunk::INCLUDE , tokens[ 1 ].c_str( ) , 1 ); - - } else if ( directive == "type" ) { - nl( ); - if ( tokens.size( ) != 2 ) { - error( "invalid arguments" ); - return; - } - auto pos( InputTypes_.find( tokens[ 1 ] ) ); - if ( pos == InputTypes_.end( ) ) { - error( "unknown type" ); - } else { - input.type = pos->second; - } - - } else { - nl( ); - error( "unknown directive" ); - } -} - -/*----------------------------------------------------------------------------*/ - -void T_InputReader_::error( - T_String const& err ) -{ - input.errors.addNew( line , err ); -} - -void T_InputReader_::nl( ) -{ - accumLines ++; - accumulator += '\n'; -} - -void T_InputReader_::addAccumulated( ) -{ - if ( accumLines ) { - auto& ck( input.chunks ); - ck.addNew( E_ShaderInputChunk::CODE , - accumulator.c_str( ) , accumLines ); - accumulator = {}; - accumLines = 0; - } -} - -} // namespace - - -/*= T_ShaderInput ============================================================*/ - -bool T_ShaderInput::load( - T_String const& path ) -{ - type = E_ShaderInput::CHUNK; - chunks.clear( ); - errors.clear( ); - - FILE* const file{ fopen( (char const*) path.toOSString( ).data( ) , "r" ) }; - if ( !file ) { - return false; - } - - T_InputReader_ reader( file , *this ); - reader.read( ); - - return true; -} /*= T_ShaderPogram ===========================================================*/ diff --git a/ui-shaders.hh b/ui-shaders.hh index 571a30e..21bbf22 100644 --- a/ui-shaders.hh +++ b/ui-shaders.hh @@ -1,100 +1,8 @@ #pragma once +#include "c-shaders.hh" #include "c-filewatcher.hh" -/*= INPUT FILES ==============================================================*/ - -// Type of input chunk -enum class E_ShaderInputChunk { - CODE , - INCLUDE , -}; - -// Input chunk data -struct T_ShaderInputChunk -{ - E_ShaderInputChunk type; - T_String text; - uint32_t lines; - - T_ShaderInputChunk( ) = default; - T_ShaderInputChunk( - const E_ShaderInputChunk type , - T_String text , - const uint32_t lines ) - : type( type ) , text( std::move( text ) ) , lines( lines ) - { } -}; - -// Input file type -enum class E_ShaderInput { - CHUNK , // Chunk that may be repeated - LIBRARY , // Library (will only be loaded once) - - // "Main" shader source files - VERTEX , FRAGMENT , GEOMETRY , - COMPUTE -}; - -// Preprocessing errors -struct T_ShaderInputError -{ - uint32_t line; - T_String error; - - T_ShaderInputError( - const uint32_t line , - T_String error ) - : line( line ) , error( std::move( error ) ) - { } -}; - -// Source file -struct T_ShaderInput -{ - E_ShaderInput type = E_ShaderInput::CHUNK; - T_Array< T_ShaderInputChunk > chunks; - T_Array< T_ShaderInputError > errors; - - bool load( T_String const& path ); -}; -using P_ShaderInput = T_OwnPtr< T_ShaderInput >; - - -// Type of shader -enum class E_ShaderType { - VERTEX , FRAGMENT , GEOMETRY , - COMPUTE , - __COUNT__ -}; - - -// Errors in shader code - the errors it represents may come from either -// the input loader, the shader loader or the driver. -struct T_ShaderError -{ - T_String source; - uint32_t line; - T_String error; - - T_ShaderError( ) = default; - - T_ShaderError( T_String source , - const uint32_t line , - T_String error ) - : source( std::move( source ) ) , line( line ) , - error( std::move( error ) ) - { } - - T_ShaderError( T_String source , - const uint32_t line , - T_StringBuilder& error ) - : source( std::move( source ) ) , line( line ) , - error( std::move( error ) ) - { } -}; - - struct T_ShaderManager; struct T_ShaderProgram