Ticket #3640: preprocessor_include_with_hotload_impl.patch

File preprocessor_include_with_hotload_impl.patch, 16.9 KB (added by Mikita Hradovich, 5 years ago)

Preprocessor #include support with hotloading of included files

  • binaries/data/mods/public/maps/skirmishes/Acropolis

     
    3030            <Contrast>0.96875</Contrast>
    3131            <Saturation>1</Saturation>
    3232            <Bloom>0.186523</Bloom>
    33             <PostEffect>hdr</PostEffect>
     33            <PostEffect>smaa</PostEffect>
    3434        </Postproc>
    3535    </Environment>
    3636    <Camera>
  • binaries/data/mods/public/shaders/effects/postproc/smaa.xml

     
     1<?xml version="1.0" encoding="utf-8"?>
     2<effect>
     3    <technique>
     4        <require shaders="glsl"/>
     5        <pass shader="glsl/edge_detection"/>
     6        <!--<pass shader="glsl/blending_weights"/>-->
     7        <!--<pass shader="glsl/neighborhood_blending"/>-->
     8    </technique>
     9</effect>
     10 No newline at end of file
  • binaries/data/mods/public/shaders/glsl/edge_detection.fs

     
     1#version 120
     2
     3uniform sampler2D renderedTex;
     4
     5varying vec2 v_tex;
     6
     7void main(void)
     8{
     9    gl_FragColor.rgb = texture2D(renderedTex, v_tex).rgb;
     10    gl_FragColor.a = 1.0;
     11}
     12
     13
  • binaries/data/mods/public/shaders/glsl/edge_detection.vs

     
     1#version 110
     2
     3#include "glsl/header.glsl"
     4
     5#define Y X
     6
     7varying vec2 v_tex;
     8
     9attribute vec3 a_vertex;
     10attribute vec2 a_uv0;
     11
     12void main()
     13
     14  vec2 tmp = vec2(Y);
     15  gl_Position = vec4(a_vertex, 1.0); 
     16  v_tex = a_uv0;
     17}
     18 No newline at end of file
  • binaries/data/mods/public/shaders/glsl/edge_detection.xml

     
     1<?xml version="1.0" encoding="utf-8"?>
     2<program type="glsl">
     3    <vertex file="glsl/edge_detection.vs">
     4        <stream name="pos"/>
     5        <stream name="uv0"/>
     6        <attrib name="a_vertex" semantics="gl_Vertex"/>
     7        <attrib name="a_uv0" semantics="gl_MultiTexCoord0"/>
     8    </vertex>
     9    <fragment file="glsl/edge_detection.fs"/>
     10</program>
  • binaries/data/mods/public/shaders/glsl/header.glsl

     
     1#define X 1.0
     2
     3void m()
     4{
     5}
     6 No newline at end of file
  • graphics/ShaderManager.cpp

     
    6060CShaderProgramPtr CShaderManager::LoadProgram(const char* name, const CShaderDefines& defines)
    6161{
    6262    CacheKey key = { name, defines };
    63     std::map<CacheKey, CShaderProgramPtr>::iterator it = m_ProgramCache.find(key);
     63    auto it = m_ProgramCache.find(key);
    6464    if (it != m_ProgramCache.end())
    6565        return it->second;
    6666
     
    265265    else
    266266        program = CShaderProgramPtr(CShaderProgram::ConstructARB(vertexFile, fragmentFile, defines, vertexUniforms, fragmentUniforms, streamFlags));
    267267
     268    // add included file listener
     269    program->RegisterIncludedFilesObserver(std::bind(&CShaderManager::AddHotloadFile, this, std::placeholders::_1, program));
     270
    268271    program->Reload();
    269272
    270273//  m_HotloadFiles[xmlFilename].insert(program); // TODO: should reload somehow when the XML changes
     
    556559    return m_EffectCache.size();
    557560}
    558561
     562void CShaderManager::AddHotloadFile(const VfsPath& filename, CShaderProgramPtr program)
     563{
     564    m_HotloadFiles[filename].insert(program);
     565}
     566
    559567/*static*/ Status CShaderManager::ReloadChangedFileCB(void* param, const VfsPath& path)
    560568{
    561569    return static_cast<CShaderManager*>(param)->ReloadChangedFile(path);
     
    564572Status CShaderManager::ReloadChangedFile(const VfsPath& path)
    565573{
    566574    // Find all shaders using this file
    567     HotloadFilesMap::iterator files = m_HotloadFiles.find(path);
     575    auto files = m_HotloadFiles.find(path);
    568576    if (files != m_HotloadFiles.end())
    569577    {
    570578        // Reload all shaders using this file
    571         for (std::set<std::weak_ptr<CShaderProgram> >::iterator it = files->second.begin(); it != files->second.end(); ++it)
     579        for_each(files->second.begin(), files->second.end(), [](const std::weak_ptr<CShaderProgram> p)
    572580        {
    573             if (std::shared_ptr<CShaderProgram> program = it->lock())
     581            if (auto program = p.lock())
    574582                program->Reload();
    575         }
     583        });
    576584    }
    577585
    578586    // TODO: hotloading changes to shader XML files and effect XML files would be nice
  • graphics/ShaderManager.h

     
    7272     */
    7373    size_t GetNumEffectsLoaded();
    7474
     75    /**
     76    * Associates file with program to be reloaded if file has changed
     77    */
     78    void AddHotloadFile(const VfsPath&, CShaderProgramPtr program);
     79
    7580private:
    7681
    7782    struct CacheKey
  • graphics/ShaderProgram.cpp

     
    432432
    433433        CPreprocessorWrapper preprocessor;
    434434        preprocessor.AddDefines(m_Defines);
     435        preprocessor.RegisterIncludedFilesObserver(IncludedFileObserver);
    435436
    436437#if CONFIG2_GLES
    437438        // GLES defines the macro "GL_ES" in its GLSL preprocessor,
     
    890891{
    891892    ENSURE((m_StreamFlags & ~m_ValidStreams) == 0);
    892893}
     894
     895void CShaderProgram::RegisterIncludedFilesObserver(std::function<void(const VfsPath&)> fileObserver)
     896{
     897    IncludedFileObserver = fileObserver;
     898}
  • graphics/ShaderProgram.h

     
    193193     */
    194194    void AssertPointersBound();
    195195
     196    /**
     197    * Register observer to be notified on each included file
     198    */
     199    void RegisterIncludedFilesObserver(std::function<void(const VfsPath&)>);
     200
    196201protected:
    197202    CShaderProgram(int streamflags);
    198203
     
    203208    void BindClientStates();
    204209    void UnbindClientStates();
    205210    int m_ValidStreams; // which streams have been specified via VertexPointer etc since the last Bind
     211
     212    std::function<void(const VfsPath&)> IncludedFileObserver;
    206213};
    207214
    208215#endif // INCLUDED_SHADERPROGRAM
  • ps/Preprocessor.cpp

     
    3838
    3939#include "ps/CLogger.h"
    4040
     41#include "ps/Filesystem.h" // to handle #include
     42
    4143// Limit max number of macro arguments to this
    4244#define MAX_MACRO_ARGS 16
    4345
     
    224226    return xt;
    225227}
    226228
     229CPreprocessor::Macro *CPreprocessor::Macro::copy()
     230{
     231    Token mainCopy;
     232
     233    // Copy buffer once
     234    char *buffer = new char[this->Body.Length];
     235    strncpy(buffer, this->Body.String, this->Body.Length);
     236
     237    // Copy each subcomponent of Macro
     238    Token body = Token(this->Body.Type, buffer, this->Body.Length);
     239    Token name = Token(this->Name.Type, buffer, this->Name.Length);
     240    Token value = Token(this->Value.Type, buffer + (this->Value.Buffer - this->Body.Buffer), this->Value.Length);
     241
     242    // FIXME: Ignore Args for now
     243
     244    Macro *macroCopy = new Macro(name);
     245    macroCopy->Body = body;
     246    macroCopy->Value = value;
     247
     248    return macroCopy;
     249}
     250
    227251//---------------------------------------------------------------------------//
    228252
    229253static void DefaultError (void *iData, int iLine, const char *iError,
     
    700724            return false;
    701725
    702726        case Token::TK_KEYWORD:
    703         {
     727        {
    704728            // Try to expand the macro
    705             Macro *m = IsDefined (*vt);
     729            Macro *m = IsDefined (*vt);
    706730            if (m != NULL && !m->Expanding)
    707731            {
    708732                Token x = ExpandMacro (*vt);
     
    715739            // Undefined macro, "expand" to 0 (mimic cpp behaviour)
    716740            oValue = 0;
    717741            break;
    718         }
     742        }
    719743        case Token::TK_TEXT:
    720744        case Token::TK_NUMBER:
    721745            if (!vt->GetValue (oValue))
     
    869893    return t;
    870894}
    871895
    872 bool CPreprocessor::HandleDefine (Token &iBody, int iLine)
     896bool CPreprocessor::HandleDefine(Token &iBody, int iLine)
    873897{
    874898    // Create an additional preprocessor to process macro body
    875     CPreprocessor cpp (iBody, iLine);
     899    CPreprocessor cpp(iBody, iLine);
    876900
    877     Token t = cpp.GetToken (false);
     901    Token t = cpp.GetToken(false);
    878902    if (t.Type != Token::TK_KEYWORD)
    879903    {
    880         Error (iLine, "Macro name expected after #define");
     904        Error(iLine, "Macro name expected after #define");
    881905        return false;
    882906    }
    883907
    884908    bool output_enabled = ((EnableOutput & (EnableOutput + 1)) == 0);
    885909    if (!output_enabled)
    886         return true;
     910        return true;
    887911
    888     Macro *m = new Macro (t);
     912    Macro *m = new Macro(t);
    889913    m->Body = iBody;
    890     t = cpp.GetArguments (m->NumArgs, m->Args, false);
     914    t = cpp.GetArguments(m->NumArgs, m->Args, false);
    891915    while (t.Type == Token::TK_WHITESPACE)
    892         t = cpp.GetToken (false);
     916        t = cpp.GetToken(false);
    893917
    894918    switch (t.Type)
    895919    {
    896         case Token::TK_NEWLINE:
    897         case Token::TK_EOS:
    898             // Assign "" to token
    899             t = Token (Token::TK_TEXT, "", 0);
    900             break;
     920    case Token::TK_NEWLINE:
     921    case Token::TK_EOS:
     922        // Assign "" to token
     923        t = Token(Token::TK_TEXT, "", 0);
     924        break;
    901925
    902         case Token::TK_ERROR:
    903             delete m;
    904             return false;
     926    case Token::TK_ERROR:
     927        delete m;
     928        return false;
    905929
    906         default:
    907             t.Type = Token::TK_TEXT;
    908             ENSURE (t.String + t.Length == cpp.Source);
    909             t.Length = cpp.SourceEnd - t.String;
    910             break;
     930    default:
     931        t.Type = Token::TK_TEXT;
     932        ENSURE(t.String + t.Length == cpp.Source);
     933        t.Length = cpp.SourceEnd - t.String;
     934        break;
    911935    }
    912936
    913937    m->Value = t;
     
    9791003    return true;
    9801004}
    9811005
     1006CPreprocessor::Token CPreprocessor::HandleInclude(Token &iBody, int iLine)
     1007{
     1008    CPreprocessor cpp(iBody, iLine);
     1009
     1010    Token t = cpp.GetToken(false);
     1011
     1012    if (t.Type != Token::TK_STRING)
     1013    {
     1014        Error(iLine, "Expecting a file name after #include, got", &t);
     1015        return Token();
     1016    }
     1017
     1018    std::string filename = std::string(t.String).substr(0, t.Length);
     1019    ENSURE(filename.at(0) == '"' && filename.at(t.Length-1) == '"');
     1020    filename = filename.substr(1, t.Length - 2); // remove '"' symbols from the beginning and from the end of token
     1021
     1022    CVFSFile includedFile;
     1023    const VfsPath includedFilePath(L"shaders/" + wstring_from_utf8(filename));
     1024    if (includedFile.Load(g_VFS, includedFilePath) != PSRETURN_OK)
     1025        Error(iLine, "Warning: File not found", &t);
     1026    IncludedFileObserver(includedFilePath);
     1027    const CStr& input = includedFile.GetAsString();
     1028
     1029    size_t len = 0;
     1030    CPreprocessor preprocessor;
     1031    ENSURE(input.size() > 0);
     1032    //preprocessor.AddDefines(m_Defines);
     1033    std::string includedSources = preprocessor.Parse(input.c_str(), input.size(), len);
     1034    char *result = new char[len];
     1035    includedSources.copy(result, len);
     1036
     1037    // Copy macros
     1038    Macro *m = preprocessor.MacroList;
     1039    while (m) {
     1040        Macro *copy = m->copy();
     1041        copy->Next = MacroList;
     1042        MacroList = copy;
     1043        m = m->Next;
     1044    }
     1045
     1046    if (!result)
     1047    {
     1048        Error(iLine, "Problem during preprocessing of included file", &t);
     1049        return Token();
     1050    }
     1051
     1052    do
     1053    {
     1054        t = cpp.GetToken(false);
     1055    } while (t.Type == Token::TK_WHITESPACE ||
     1056        t.Type == Token::TK_COMMENT ||
     1057        t.Type == Token::TK_LINECOMMENT);
     1058
     1059    if (t.Type != Token::TK_EOS)
     1060        Error(iLine, "Warning: Ignoring garbage after directive", &t);
     1061
     1062    return Token(Token::TK_STRING, result, len);;
     1063}
     1064
    9821065CPreprocessor::Token CPreprocessor::ExpandDefined (CPreprocessor *iParent, int iNumArgs, Token *iArgs)
    9831066{
    9841067    if (iNumArgs != 1)
     
    11131196    ((dirlen == sizeof (s) - 1) && (strncmp (directive, s, sizeof (s) - 1) == 0))
    11141197
    11151198    bool rc;
    1116     if (IS_DIRECTIVE ("define"))
    1117         rc = HandleDefine (t, iLine);
     1199    if (IS_DIRECTIVE("define"))
     1200        rc = HandleDefine(t, iLine);
    11181201    else if (IS_DIRECTIVE ("undef"))
    11191202        rc = HandleUnDef (t, iLine);
    11201203    else if (IS_DIRECTIVE ("ifdef"))
     
    11251208        if (rc)
    11261209            EnableOutput ^= 1;
    11271210    }
     1211    else if (IS_DIRECTIVE("include"))
     1212        return HandleInclude(t, iLine);
    11281213    else if (IS_DIRECTIVE ("if"))
    11291214        rc = HandleIf (t, iLine);
    11301215    else if (IS_DIRECTIVE ("else"))
     
    11761261
    11771262void CPreprocessor::Define (const char *iMacroName, const char *iMacroValue)
    11781263{
    1179     Define (iMacroName, strlen(iMacroName), iMacroValue, strlen(iMacroValue));
     1264    Define (iMacroName, strlen(iMacroName), iMacroValue, strlen(iMacroValue));
    11801265}
    11811266
    11821267void CPreprocessor::Define (const char *iMacroName, long iMacroValue)
    11831268{
    1184     Define (iMacroName, strlen(iMacroName), iMacroValue);
     1269    Define (iMacroName, strlen(iMacroName), iMacroValue);
    11851270}
    11861271
    11871272bool CPreprocessor::Undef (const char *iMacroName, size_t iMacroNameLen)
     
    12651350
    12661351                if (output_enabled)
    12671352                    output.AppendNL (Line - old_line - t.CountNL ());
     1353
    12681354                goto NextToken;
    12691355
    12701356            case Token::TK_LINECONT:
     
    13111397    retval.Allocated = 0;
    13121398    return retval.Buffer;
    13131399}
     1400
     1401void CPreprocessor::RegisterIncludedFilesObserver(std::function<void(const VfsPath&)> fileObserver)
     1402{
     1403    IncludedFileObserver = fileObserver;
     1404}
  • ps/Preprocessor.h

     
    3535#ifndef INCLUDED_CPREPROCESSOR
    3636#define INCLUDED_CPREPROCESSOR
    3737
     38#include <functional>
     39
     40// Forward declare VfsPath, which is typedefed Path
     41class Path;
     42typedef Path VfsPath;
     43
    3844/**
    3945 * This is a simplistic C/C++-like preprocessor.
    4046 * It takes an non-zero-terminated string on input and outputs a
     
    200206
    201207        /// Expand the macro value (will not work for functions)
    202208        Token Expand (int iNumArgs, Token *iArgs, Macro *iMacros);
     209
     210        /// Performs deep copy of this macro
     211        Macro *copy();
    203212    };
    204213
    205214    friend class CPreprocessor::Macro;
     
    217226    /// The list of macros defined so far
    218227    Macro *MacroList;
    219228
     229    /// Listener to be notified about new included files
     230    std::function<void(const VfsPath&)> IncludedFileObserver;
     231
    220232    /**
    221233     * Private constructor to re-parse a single token.
    222234     */
     
    278290     */
    279291    bool HandleIfDef (Token &iBody, int iLine);
    280292
     293    /**
     294    * Handle a \#include directive.
     295    * @param iBody
     296    *     The body of the directive (everything after the directive
     297    *     until end of line).
     298    * @param iLine
     299    *     The line where the directive begins (for error reports)
     300    * @return
     301    *     Included text enclosed in a token
     302    */
     303    Token HandleInclude (Token &iBody, int iLine);
     304
    281305    /**
    282306     * Handle an \#if directive.
    283307     * @param iBody
     
    508532     */
    509533    char *Parse (const char *iSource, size_t iLength, size_t &oLength);
    510534
     535    /**
     536    * Register observer to be notified on each included file
     537    */
     538    void RegisterIncludedFilesObserver(std::function<void(const VfsPath&)>);
     539
    511540    /**
    512541     * An error handler function type.
    513542     * The default implementation just drops a note to stderr and
  • ps/PreprocessorWrapper.cpp

     
    8484
    8585    return ret;
    8686}
     87
     88void CPreprocessorWrapper::RegisterIncludedFilesObserver(std::function<void(const VfsPath&)> observer)
     89{
     90    m_Preprocessor.RegisterIncludedFilesObserver(observer);
     91}
  • ps/PreprocessorWrapper.h

     
    3737
    3838    CStr Preprocess(const CStr& input);
    3939
     40    void RegisterIncludedFilesObserver(std::function<void(const VfsPath&)>);
     41
    4042private:
    4143    CPreprocessor m_Preprocessor;
    4244};