• 虚幻4蓝图编译剖析(三)


    编译

    上面基本的术语已经介绍完了,下面我们进入来进入蓝图编译过程分析。蓝图的编译过程都在FKismetCompilerContext::Compile()函数中。它根据编译的类型不同(上文编译选项中提到的只编译Skeleton、只生成字节码、只生成cpp代码等等)会走不同的分支。我们这里以完全编译来讲解。此处为大概的流程,若想看详细的流程,请参照流程图以及代码。

    清除类

    类是就地编译的,这意味着同一个UBlueprintGeneratedClass在每次编译的时候都会被清理,并且会重复利用,这样指向这个类的指针就不用修复了。CleanAndSanitizeClass()把属性和函数放到临时包的一个垃圾(trash)类里面,然后清除类中的所有数据。

    创建类的属性

    CreateClassVariablesFromBlueprint()函数遍历蓝图的NewVariables数组,也包括构造脚本、Timeline等地方来找到该类需要的所有属性,然后创建UProperties。

    创建函数列表

    通过处理事件图表(Event graph)、常规的函数图表、代理、以及接口中来创建函数。

    处理事件图表

    CreateAndProcessUberGraph()函数用来处理事件图表。它把所有的事件图表拷贝到一个大的图中,这个时候节点有机会去做展开操作(expand,如果它需要的话)。然后会为图中的每一个事件节点创建一个函数桩(stub),最终会为当前的事件图表创建一个FKismetFunctionContext,用于把整个事件图表当作一个函数来处理。

    处理函数图表

    常规函数图表的处理是通过ProcessOneFunctionGraph()函数来完成的,它会把图中的每一个节点拷贝到另外一个节点中去,这个时候每个节点有机会展开(expand),最后会为每一个函数创建一个FKismetFunctionContext,用来后面对该函数的编译工作。

    预编译函数

    函数的预编译是通过为每一个FKismetFunctionContext调用PrecompileFunction()来实现的,这个函数主要做以下操作:

    1. 确定执行顺序并计算数据依赖。
    2. 去除那些没有连接的或者无数据依赖的节点。
    3. 为每一个剩下的运行每一个节点处理器(FNodeHandlingFunctor)的RegisterNets()函数,它会为函数中的值创建FBPTerminal。
    4. 创建UFunction对象和相关的属性。

    绑定和链接类

    现在类已经拥有了UFunctions和UProperties,现在可以绑定和链接类了,它包含填充属性链,属性大小 以及函数的列表。这个时候它相关于有了一个类头文件,不包括最终的标记、元数据以及CDO对象。

    编译函数

    接下来就需要通过每一个节点处理器(FNodeHanlingFunctor)的Compile()函数使用AppendStatementForNode()函数来为该节点添加FKismetBlueprintStatement。这个函数也可能会创建FBPTerminal对象只要它们只是局部使用的。

    后编译函数

    PostCompileFunction()是编译函数的最后一个阶段,在所有函数调用了CompileFunction()之后调用,主要是修复交叉引用。

    完成编译类

    为了完成编译该类,编译器会最终设置类标记,并从父类继承标记和元数据,最后会确定所有的事情在编译过程中是正确的。

    后端生成代码

    编译器后端会把函数中的所有语句转换成代码,目前有两个使用的后端:

    1. FKismetCompilerVMBackend 把FKismetCompilerStatement转成字节码,并把它序列化到脚本数组中。

    1. FKismetCppBackend 生成C++代码,只用于调试目的。

    字节码

    字节码的定义在Script.h中的EExprToken枚举中,定义如下,可以看到它有一些比较通用的指令,如EX_Jump EX_JumpIfNot等,也有一些专用的指令比如EX_DynamicCast EX_SetArray。

        
      1 //
      2 
      3 // Evaluatable expression item types.
      4 
      5 //
      6 
      7 enum EExprToken
      8 
      9 {
     10 
     11     // Variable references.
     12 
     13     EX_LocalVariable        = 0x00,    // A local variable.
     14 
     15     EX_InstanceVariable        = 0x01,    // An object variable.
     16 
     17     EX_DefaultVariable        = 0x02, // Default variable for a class context.
     18 
     19     //                        = 0x03,
     20 
     21     EX_Return                = 0x04,    // Return from function.
     22 
     23     //                        = 0x05,
     24 
     25     EX_Jump                    = 0x06,    // Goto a local address in code.
     26 
     27     EX_JumpIfNot            = 0x07,    // Goto if not expression.
     28 
     29     //                        = 0x08,
     30 
     31     EX_Assert                = 0x09,    // Assertion.
     32 
     33     //                        = 0x0A,
     34 
     35     EX_Nothing                = 0x0B,    // No operation.
     36 
     37     //                        = 0x0C,
     38 
     39     //                        = 0x0D,
     40 
     41     //                        = 0x0E,
     42 
     43     EX_Let                    = 0x0F,    // Assign an arbitrary size value to a variable.
     44 
     45     //                        = 0x10,
     46 
     47     //                        = 0x11,
     48 
     49     EX_ClassContext            = 0x12,    // Class default object context.
     50 
     51     EX_MetaCast = 0x13, // Metaclass cast.
     52 
     53     EX_LetBool                = 0x14, // Let boolean variable.
     54 
     55     EX_EndParmValue            = 0x15,    // end of default value for optional function parameter
     56 
     57     EX_EndFunctionParms        = 0x16,    // End of function call parameters.
     58 
     59     EX_Self                    = 0x17,    // Self object.
     60 
     61     EX_Skip                    = 0x18,    // Skippable expression.
     62 
     63     EX_Context                = 0x19,    // Call a function through an object context.
     64 
     65     EX_Context_FailSilent    = 0x1A, // Call a function through an object context (can fail silently if the context is NULL; only generated for functions that don't have output or return values).
     66 
     67     EX_VirtualFunction        = 0x1B,    // A function call with parameters.
     68 
     69     EX_FinalFunction        = 0x1C,    // A prebound function call with parameters.
     70 
     71     EX_IntConst                = 0x1D,    // Int constant.
     72 
     73     EX_FloatConst            = 0x1E,    // Floating point constant.
     74 
     75     EX_StringConst            = 0x1F,    // String constant.
     76 
     77     EX_ObjectConst         = 0x20,    // An object constant.
     78 
     79     EX_NameConst            = 0x21,    // A name constant.
     80 
     81     EX_RotationConst        = 0x22,    // A rotation constant.
     82 
     83     EX_VectorConst            = 0x23,    // A vector constant.
     84 
     85     EX_ByteConst            = 0x24,    // A byte constant.
     86 
     87     EX_IntZero                = 0x25,    // Zero.
     88 
     89     EX_IntOne                = 0x26,    // One.
     90 
     91     EX_True                    = 0x27,    // Bool True.
     92 
     93     EX_False                = 0x28,    // Bool False.
     94 
     95     EX_TextConst            = 0x29, // FText constant
     96 
     97     EX_NoObject                = 0x2A,    // NoObject.
     98 
     99     EX_TransformConst        = 0x2B, // A transform constant
    100 
    101     EX_IntConstByte            = 0x2C,    // Int constant that requires 1 byte.
    102 
    103     EX_NoInterface            = 0x2D, // A null interface (similar to EX_NoObject, but for interfaces)
    104 
    105     EX_DynamicCast            = 0x2E,    // Safe dynamic class casting.
    106 
    107     EX_StructConst            = 0x2F, // An arbitrary UStruct constant
    108 
    109     EX_EndStructConst        = 0x30, // End of UStruct constant
    110 
    111     EX_SetArray                = 0x31, // Set the value of arbitrary array
    112 
    113     EX_EndArray                = 0x32,
    114 
    115     //                        = 0x33,
    116 
    117     EX_UnicodeStringConst = 0x34, // Unicode string constant.
    118 
    119     EX_Int64Const            = 0x35,    // 64-bit integer constant.
    120 
    121     EX_UInt64Const            = 0x36,    // 64-bit unsigned integer constant.
    122 
    123     //                        = 0x37,
    124 
    125     EX_PrimitiveCast        = 0x38,    // A casting operator for primitives which reads the type as the subsequent byte
    126 
    127     //                        = 0x39,
    128 
    129     //                        = 0x3A,
    130 
    131     //                        = 0x3B,
    132 
    133     //                        = 0x3C,
    134 
    135     //                        = 0x3D,
    136 
    137     //                        = 0x3E,
    138 
    139     //                        = 0x3F,
    140 
    141     //                        = 0x40,
    142 
    143     //                        = 0x41,
    144 
    145     EX_StructMemberContext    = 0x42, // Context expression to address a property within a struct
    146 
    147     EX_LetMulticastDelegate    = 0x43, // Assignment to a multi-cast delegate
    148 
    149     EX_LetDelegate            = 0x44, // Assignment to a delegate
    150 
    151     //                        = 0x45,
    152 
    153     //                        = 0x46, // CST_ObjectToInterface
    154 
    155     //                        = 0x47, // CST_ObjectToBool
    156 
    157     EX_LocalOutVariable        = 0x48, // local out (pass by reference) function parameter
    158 
    159     //                        = 0x49, // CST_InterfaceToBool
    160 
    161     EX_DeprecatedOp4A        = 0x4A,
    162 
    163     EX_InstanceDelegate        = 0x4B,    // const reference to a delegate or normal function object
    164 
    165     EX_PushExecutionFlow    = 0x4C, // push an address on to the execution flow stack for future execution when a EX_PopExecutionFlow is executed. Execution continues on normally and doesn't change to the pushed address.
    166 
    167     EX_PopExecutionFlow        = 0x4D, // continue execution at the last address previously pushed onto the execution flow stack.
    168 
    169     EX_ComputedJump            = 0x4E,    // Goto a local address in code, specified by an integer value.
    170 
    171     EX_PopExecutionFlowIfNot = 0x4F, // continue execution at the last address previously pushed onto the execution flow stack, if the condition is not true.
    172 
    173     EX_Breakpoint            = 0x50, // Breakpoint. Only observed in the editor, otherwise it behaves like EX_Nothing.
    174 
    175     EX_InterfaceContext        = 0x51,    // Call a function through a native interface variable
    176 
    177     EX_ObjToInterfaceCast = 0x52,    // Converting an object reference to native interface variable
    178 
    179     EX_EndOfScript            = 0x53, // Last byte in script code
    180 
    181     EX_CrossInterfaceCast    = 0x54, // Converting an interface variable reference to native interface variable
    182 
    183     EX_InterfaceToObjCast = 0x55, // Converting an interface variable reference to an object
    184 
    185     //                        = 0x56,
    186 
    187     //                        = 0x57,
    188 
    189     //                        = 0x58,
    190 
    191     //                        = 0x59,
    192 
    193     EX_WireTracepoint        = 0x5A, // Trace point. Only observed in the editor, otherwise it behaves like EX_Nothing.
    194 
    195     EX_SkipOffsetConst        = 0x5B, // A CodeSizeSkipOffset constant
    196 
    197     EX_AddMulticastDelegate = 0x5C, // Adds a delegate to a multicast delegate's targets
    198 
    199     EX_ClearMulticastDelegate = 0x5D, // Clears all delegates in a multicast target
    200 
    201     EX_Tracepoint            = 0x5E, // Trace point. Only observed in the editor, otherwise it behaves like EX_Nothing.
    202 
    203     EX_LetObj                = 0x5F,    // assign to any object ref pointer
    204 
    205     EX_LetWeakObjPtr        = 0x60, // assign to a weak object pointer
    206 
    207     EX_BindDelegate            = 0x61, // bind object and name to delegate
    208 
    209     EX_RemoveMulticastDelegate = 0x62, // Remove a delegate from a multicast delegate's targets
    210 
    211     EX_CallMulticastDelegate = 0x63, // Call multicast delegate
    212 
    213     EX_LetValueOnPersistentFrame = 0x64,
    214 
    215     EX_ArrayConst            = 0x65,
    216 
    217     EX_EndArrayConst        = 0x66,
    218 
    219     EX_AssetConst            = 0x67,
    220 
    221     EX_CallMath                = 0x68, // static pure function from on local call space
    222 
    223     EX_SwitchValue            = 0x69,
    224 
    225     EX_InstrumentationEvent    = 0x6A, // Instrumentation event
    226 
    227     EX_ArrayGetByRef        = 0x6B,
    228 
    229     EX_Max                    = 0x100,
    230 
    231 };

    拷贝类默认对象(CDO)属性

    使用一个特殊的函数CopyPropertiesForUnrelatedObjects(),编译器把类旧CDO中的值拷贝到新的CDO中。属性是通过带标记的序列化来拷贝的,只要名字是一致的,它们就应该被正确的复制。CDO中的组件会重新实例化。

    重新实例化

    由于类可能已经改变大小或者属性已经添加或删除了,编译器需要重新实例化该类实例化的所有对象。它使用TOjbectIterator来找到该类的实例,创建一个新的,并且使用CopyPropertiesForUnrelatedObjects()函数来把旧实例的数据拷贝到新的实例。详细信息参照FBlueprintCompileReinstancer类。

    编译实例学习

    我在蓝图里面新建了一个继承自Actor的NewBlueprint,它有一个变量StringTest并且实现了一个BeginPlay 事件和一个FunctionTest()函数,这个函数有一个局部变量LocalStringTest。

    下面分别是BeginPlay和FunctionTest的定义:

    为了看到编译的结果,我们需要修改BaseEngine.ini中的设置把CompileDisplaysBinaryBackend设置为true,如果要显示生成的cpp文件,也可以把CompileDisplaysTextBackend设置为true。注意需要重新启动编辑器。点击编译后在OutputLog中得到的结果如下代码所示:

        
      1 BlueprintLog: New page: Compile NewBlueprint
      2 
      3 LogK2Compiler: [function ExecuteUbergraph_NewBlueprint]:
      4 
      5 Label_0x0:
      6 
      7 $4E: Computed Jump, offset specified by expression:
      8 
      9 $0: Local variable named EntryPoint
     10 
     11 Label_0xA:
     12 
     13 $5E: .. debug site ..
     14 
     15 Label_0xB:
     16 
     17 $5A: .. wire debug site ..
     18 
     19 Label_0xC:
     20 
     21 $5E: .. debug site ..
     22 
     23 Label_0xD:
     24 
     25 $1B: Virtual Function named FunctionTest
     26 
     27 $0: Local variable named CallFunc_FunctionTest_OutNewString
     28 
     29 $16: EX_EndFunctionParms
     30 
     31 Label_0x24:
     32 
     33 $5A: .. wire debug site ..
     34 
     35 Label_0x25:
     36 
     37 $5E: .. debug site ..
     38 
     39 Label_0x26:
     40 
     41 $19: Context
     42 
     43 ObjectExpression:
     44 
     45 $20: EX_ObjectConst (000000003022A100:KismetSystemLibrary /Script/Engine.Default__KismetSystemLibrary)
     46 
     47 Skip Bytes: 0x3D
     48 
     49 R-Value Property: (null)
     50 
     51 ContextExpression:
     52 
     53 $1C: Final Function (stack node KismetSystemLibrary::PrintString)
     54 
     55 $17: EX_Self
     56 
     57 $0: Local variable named CallFunc_FunctionTest_OutNewString
     58 
     59 $28: EX_False
     60 
     61 $27: EX_True
     62 
     63 $2F: literal struct LinearColor (serialized size: 16)
     64 
     65 $1E: literal float 0.000000
     66 
     67 $1E: literal float 0.660000
     68 
     69 $1E: literal float 1.000000
     70 
     71 $1E: literal float 1.000000
     72 
     73 $30: EX_EndStructConst
     74 
     75 $1E: literal float 2.000000
     76 
     77 $16: EX_EndFunctionParms
     78 
     79 Label_0x79:
     80 
     81 $5A: .. wire debug site ..
     82 
     83 Label_0x7A:
     84 
     85 $4: Return expression
     86 
     87 $B: EX_Nothing
     88 
     89 Label_0x7C:
     90 
     91 $53: EX_EndOfScript
     92 
     93 LogK2Compiler: [function ReceiveBeginPlay]:
     94 
     95 Label_0x0:
     96 
     97 $1B: Virtual Function named ExecuteUbergraph_NewBlueprint
     98 
     99 $1D: literal int32 10
    100 
    101 $16: EX_EndFunctionParms
    102 
    103 Label_0x13:
    104 
    105 $4: Return expression
    106 
    107 $B: EX_Nothing
    108 
    109 Label_0x15:
    110 
    111 $53: EX_EndOfScript
    112 
    113 LogK2Compiler: [function UserConstructionScript]:
    114 
    115 Label_0x0:
    116 
    117 $5E: .. debug site ..
    118 
    119 Label_0x1:
    120 
    121 $5A: .. wire debug site ..
    122 
    123 Label_0x2:
    124 
    125 $4: Return expression
    126 
    127 $B: EX_Nothing
    128 
    129 Label_0x4:
    130 
    131 $53: EX_EndOfScript
    132 
    133 LogK2Compiler: [function FunctionTest]:
    134 
    135 Label_0x0:
    136 
    137 $5E: .. debug site ..
    138 
    139 Label_0x1:
    140 
    141 $5A: .. wire debug site ..
    142 
    143 Label_0x2:
    144 
    145 $5E: .. debug site ..
    146 
    147 Label_0x3:
    148 
    149 $F: Let (Variable = Expression)
    150 
    151 Variable:
    152 
    153 $0: Local variable named LocalStringTest
    154 
    155 Expression:
    156 
    157 $1F: literal ansi string "Bluepirnt Test: "
    158 
    159 Label_0x27:
    160 
    161 $5A: .. wire debug site ..
    162 
    163 Label_0x28:
    164 
    165 $F: Let (Variable = Expression)
    166 
    167 Variable:
    168 
    169 $0: Local variable named CallFunc_Concat_StrStr_ReturnValue
    170 
    171 Expression:
    172 
    173 $19: Context
    174 
    175 ObjectExpression:
    176 
    177 $20: EX_ObjectConst (0000000030229B00:KismetStringLibrary /Script/Engine.Default__KismetStringLibrary)
    178 
    179 Skip Bytes: 0x1C
    180 
    181 R-Value Property: CallFunc_Concat_StrStr_ReturnValue
    182 
    183 ContextExpression:
    184 
    185 $1C: Final Function (stack node KismetStringLibrary::Concat_StrStr)
    186 
    187 $0: Local variable named LocalStringTest
    188 
    189 $1: Instance variable named StringTest
    190 
    191 $16: EX_EndFunctionParms
    192 
    193 Label_0x6C:
    194 
    195 $5E: .. debug site ..
    196 
    197 Label_0x6D:
    198 
    199 $F: Let (Variable = Expression)
    200 
    201 Variable:
    202 
    203 $48: Local out variable named OutNewString
    204 
    205 Expression:
    206 
    207 $0: Local variable named CallFunc_Concat_StrStr_ReturnValue
    208 
    209 Label_0x88:
    210 
    211 $5A: .. wire debug site ..
    212 
    213 Label_0x89:
    214 
    215 $4: Return expression
    216 
    217 $B: EX_Nothing
    218 
    219 Label_0x8B:
    220 
    221 $53: EX_EndOfScript

    有个需要说明的地方就是,我们可以看到事件ReceiveBeginPlay,如上面我们所说,它具体并没有做什么事情,整个函数的指令被放到了function ExecuteUbergraph_NewBlueprint中,而它所做的事情是调用了 Virtual Function named ExecuteUbergraph_NewBlueprint 并传递了一个int32的值,这个值是在ExecuteUbergraph_NewBlueprint处的偏移值,而在ExecuteUbergraph_NewBlueprint一开始就根据传进来的偏移值进行了无条件jump跳转到相应的位置进行程序的执行。

    总结

    至此,我们我们对蓝图的编译过程有了一个基本的了解,粗略地讲解了蓝图是如何从我们编辑的结果最终编译的过程,接下来的文章我们将介绍虚幻4中蓝图虚拟机的实现,敬请期待。当然由于本人理解能力有限,里面难免有错误的地方,如读者发现还请指正。

    参考文档

    1. https://docs.unrealengine.com/latest/INT/Engine/Blueprints/TechnicalGuide/Compiler/index.html
    2. http://www.cnblogs.com/ghl_carmack/p/5804737.html
    3. 脚本语言入门书籍 Game Scripting Mastery
    4. http://blog.csdn.net/tangl_99/article/details/5600
  • 相关阅读:
    JavaScript getElementByID() not working
    [转] 从此不再惧怕URI编码:JavaScript及C# URI编码详解
    win 8.1 突然没有了声音 -- 解决办法
    升级打怪第一天 -------字符串重复
    Flex布局
    HTML 的全局事件属性
    CSS position 相对定位和绝对定位
    将1100秒转换为分秒格式
    新手小白的上路之旅
    谈谈我对Manacher算法的理解
  • 原文地址:https://www.cnblogs.com/ghl_carmack/p/6014655.html
Copyright © 2020-2023  润新知