Sfoglia il codice sorgente

Added compiled AngelScript bytecode support.
Removed unnecessary nullcheck from View.
TODO: merge ScriptCompiler & ScriptAPIDumper.

Lasse Öörni 12 anni fa
parent
commit
74c01e7da5

+ 2 - 1
Bin/Data/Scripts/Editor/AttributeEditor.as

@@ -743,6 +743,7 @@ void InitResourcePicker()
     Array<String> imageFilters = {"*.png", "*.jpg"};
     Array<String> textureFilters = {"*.dds", "*.png", "*.jpg", "*.bmp", "*.ktx", "*.pvr"};
     Array<String> soundFilters = {"*.wav","*.ogg"};
+    Array<String> scriptFilters = {"*.as", "*.asc"};
     Array<String> materialFilters = {"*.xml", "*.material"};
     resourcePickers.Push(ResourcePicker("Animation", "*.ani"));
     resourcePickers.Push(ResourcePicker("Image", imageFilters));
@@ -750,7 +751,7 @@ void InitResourcePicker()
     resourcePickers.Push(ResourcePicker("Material", materialFilters));
     resourcePickers.Push(ResourcePicker("Texture2D", textureFilters));
     resourcePickers.Push(ResourcePicker("TextureCube", "*.xml"));
-    resourcePickers.Push(ResourcePicker("ScriptFile", "*.as"));
+    resourcePickers.Push(ResourcePicker("ScriptFile", scriptFilters));
     resourcePickers.Push(ResourcePicker("XMLFile", "*.xml"));
     resourcePickers.Push(ResourcePicker("Sound", soundFilters));
     sceneResourcePath = AddTrailingSlash(fileSystem.programDir + "Data");

+ 1 - 0
CMakeLists.txt

@@ -176,6 +176,7 @@ if (NOT IOS)
     add_subdirectory (Tools/PackageTool)
     add_subdirectory (Tools/RampGenerator)
     add_subdirectory (Tools/ScriptAPIDumper)
+    add_subdirectory (Tools/ScriptCompiler)
     add_subdirectory (Tools/DocConverter)
 endif ()
 

+ 21 - 0
Docs/Reference.dox

@@ -1490,6 +1490,19 @@ ScriptAPIDumper [output file]
 
 The output file can be used to replace the 'ScriptAPI.dox' file in the 'Docs' directory. If the output file name is not provided then the script API would be dumped to standard output (console) instead.
 
+
+\section Tools_ScriptCompiler ScriptCompiler
+
+Compiles AngelScript file(s) to binary bytecode for faster loading.
+
+Usage:
+
+\verbatim
+ScriptCompiler <input file> [resource path for includes]
+\endverbatim
+
+The output files are saved with the extension .asc (compiled AngelScript.) Binary files are not automatically loaded instead of the text format (.as) script files, instead resource requests and resource references in objects need to point to the compiled files. In a final build of an application it may be convenient to simply replace the text format script files with the compiled scripts.
+
 \section Tools_DocConverter DocConverter
 
 Automatically converts the Urho3D Doxygen pages to Google Code wiki format.
@@ -1648,6 +1661,14 @@ uint       Bytecode size
 byte[]     Bytecode
 \endverbatim
 
+\section FileFormats_Script Compiled AngelScript (.asc)
+
+\verbatim
+byte[4]    Identifier "ASBC"
+?          Bytecode, produced by AngelScript serializer
+\endverbatim
+
+
 \page CodingConventions Coding conventions
 
 - Indent style is Allman (BSD) -like, ie. brace on the next line from a control statement, indented on the same level. In switch-case statements the cases are on the same indent level as the switch statement.

+ 7 - 10
Engine/Graphics/View.cpp

@@ -1409,17 +1409,14 @@ void View::SetTextures(const RenderPathCommand& command)
         }
         
         // Bind a texture from the resource system
-        if (cache)
+        Texture2D* texture = cache->GetResource<Texture2D>(command.textureNames_[i]);
+        if (texture)
+            graphics_->SetTexture(i, texture);
+        else
         {
-            Texture2D* texture = cache->GetResource<Texture2D>(command.textureNames_[i]);
-            if (texture)
-                graphics_->SetTexture(i, texture);
-            else
-            {
-                // If requesting a texture fails, clear the texture name to prevent redundant attempts
-                RenderPathCommand& cmdWrite = const_cast<RenderPathCommand&>(command);
-                cmdWrite.textureNames_[i] = String::EMPTY;
-            }
+            // If requesting a texture fails, clear the texture name to prevent redundant attempts
+            RenderPathCommand& cmdWrite = const_cast<RenderPathCommand&>(command);
+            cmdWrite.textureNames_[i] = String::EMPTY;
         }
     }
 }

+ 83 - 1
Engine/Script/ScriptFile.cpp

@@ -39,6 +39,59 @@
 namespace Urho3D
 {
 
+/// Helper class for saving AngelScript bytecode.
+class ByteCodeSerializer : public asIBinaryStream
+{
+public:
+    /// Construct.
+    ByteCodeSerializer(Serializer& dest) :
+        dest_(dest)
+    {
+    }
+    
+    /// Read from stream (no-op).
+    virtual void Read(void* ptr, asUINT size)
+    {
+        // No-op, can not read from a Serializer
+    }
+    
+    /// Write to stream.
+    virtual void Write(const void* ptr, asUINT size)
+    {
+        dest_.Write(ptr, size);
+    }
+    
+private:
+    /// Destination stream.
+    Serializer& dest_;
+};
+
+/// Helper class for loading AngelScript bytecode.
+class ByteCodeDeserializer : public asIBinaryStream
+{
+public:
+    /// Construct.
+    ByteCodeDeserializer(Deserializer& source) :
+        source_(source)
+    {
+    }
+    
+    /// Read from stream.
+    virtual void Read(void* ptr, asUINT size)
+    {
+        source_.Read(ptr, size);
+    }
+    
+    /// Write to stream (no-op).
+    virtual void Write(const void* ptr, asUINT size)
+    {
+    }
+    
+private:
+    /// Source stream.
+    Deserializer& source_;
+};
+
 OBJECTTYPESTATIC(ScriptFile);
 
 ScriptFile::ScriptFile(Context* context) :
@@ -76,7 +129,25 @@ bool ScriptFile::Load(Deserializer& source)
         return false;
     }
     
-    // Add the initial section and check for includes
+    // Check if this file is precompiled bytecode
+    if (source.ReadFileID() == "ASBC")
+    {
+        if (scriptModule_->LoadByteCode(&ByteCodeDeserializer(source)) >= 0)
+        {
+            LOGINFO("Loaded script module " + GetName() + " from bytecode");
+            compiled_ = true;
+            // Map script module to script resource with userdata
+            scriptModule_->SetUserData(this);
+            
+            return true;
+        }
+        else
+            return false;
+    }
+    else
+        source.Seek(0);
+    
+    // Not bytecode: add the initial section and check for includes
     if (!AddScriptSection(engine, source))
         return false;
     
@@ -279,6 +350,17 @@ asIScriptObject* ScriptFile::CreateObject(const String& className)
     return obj;
 }
 
+bool ScriptFile::SaveByteCode(Serializer& dest)
+{
+    if (compiled_)
+    {
+        dest.WriteFileID("ASBC");
+        return scriptModule_->SaveByteCode(&ByteCodeSerializer(dest), true) >= 0;
+    }
+    else
+        return false;
+}
+
 asIScriptFunction* ScriptFile::GetFunction(const String& declaration)
 {
     if (!compiled_)

+ 2 - 0
Engine/Script/ScriptFile.h

@@ -70,6 +70,8 @@ public:
     bool Execute(asIScriptObject* object, asIScriptFunction* method, const VariantVector& parameters = Variant::emptyVariantVector, bool unprepare = true);
     /// Create a script object.
     asIScriptObject* CreateObject(const String& className);
+    /// Save the script bytecode. Return true if successful.
+    bool SaveByteCode(Serializer& dest);
     
     /// Return script module.
     asIScriptModule* GetScriptModule() const { return scriptModule_; }

+ 1 - 1
Tools/ScriptAPIDumper/ScriptAPIDumper.cpp

@@ -51,7 +51,7 @@ int main(int argc, char** argv)
     SharedPtr<Context> context(new Context());
     SharedPtr<Engine> engine(new Engine(context));
     if (!engine->InitializeScripting())
-        ErrorExit("Unabled to initialize script engine. The application will now exit.");
+        ErrorExit("Unable to initialize script engine. The application will now exit.");
     
     if (!outputFile.Empty())
     {

+ 16 - 0
Tools/ScriptCompiler/CMakeLists.txt

@@ -0,0 +1,16 @@
+# Define target name
+set (TARGET_NAME ScriptCompiler)
+
+# Define source files
+file (GLOB CPP_FILES *.cpp)
+file (GLOB H_FILES *.h)
+set (SOURCE_FILES ${CPP_FILES} ${H_FILES})
+
+# Define dependency libs
+set (LIBS ../../Engine/Container ../../Engine/Core ../../Engine/Engine ../../Engine/IO ../../Engine/Math ../../Engine/Resource ../../Engine/Script)
+
+# Setup target
+if (APPLE)
+    set (CMAKE_EXE_LINKER_FLAGS "-framework AudioUnit -framework Carbon -framework Cocoa -framework CoreAudio -framework ForceFeedback -framework IOKit -framework OpenGL")
+endif ()
+setup_executable ()

+ 111 - 0
Tools/ScriptCompiler/ScriptCompiler.cpp

@@ -0,0 +1,111 @@
+//
+// Copyright (c) 2008-2013 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "Context.h"
+#include "Engine.h"
+#include "File.h"
+#include "FileSystem.h"
+#include "Log.h"
+#include "ProcessUtils.h"
+#include "ResourceCache.h"
+#include "ScriptFile.h"
+
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+#include "DebugNew.h"
+
+using namespace Urho3D;
+
+void CompileScript(Context* context, const String& fileName);
+
+int main(int argc, char** argv)
+{
+    #ifdef WIN32
+    const Vector<String>& arguments = ParseArguments(GetCommandLineW());
+    #else
+    const Vector<String>& arguments = ParseArguments(argc, argv);
+    #endif
+    
+    String outputFile;
+    if (arguments.Size() < 1)
+        ErrorExit("Usage: ScriptCompiler <input file> [resource path for includes]");
+    else
+        outputFile = arguments[0];
+    
+    SharedPtr<Context> context(new Context());
+    SharedPtr<Engine> engine(new Engine(context));
+    context->RegisterSubsystem(new FileSystem(context));
+    context->RegisterSubsystem(new ResourceCache(context));
+    context->RegisterSubsystem(new Log(context));
+    
+    Log* log = context->GetSubsystem<Log>();
+    log->SetLevel(LOG_WARNING);
+    log->SetTimeStamp(false);
+    
+    if (!engine->InitializeScripting())
+        ErrorExit("Unable to initialize script engine. The application will now exit.");
+    
+    String path, file, extension;
+    SplitPath(outputFile, path, file, extension);
+    
+    ResourceCache* cache = context->GetSubsystem<ResourceCache>();
+    
+    // Add resource path to be able to resolve includes
+    if (arguments.Size() > 1)
+        cache->AddResourceDir(arguments[1]);
+    else
+        cache->AddResourceDir(cache->GetPreferredResourceDir(path));
+    
+    if (!file.StartsWith("*"))
+        CompileScript(context, outputFile);
+    else
+    {
+        Vector<String> scriptFiles;
+        context->GetSubsystem<FileSystem>()->ScanDir(scriptFiles, path, file + extension, SCAN_FILES, false);
+        for (unsigned i = 0; i < scriptFiles.Size(); ++i)
+            CompileScript(context, path + scriptFiles[i]);
+    }
+    
+    return EXIT_SUCCESS;
+}
+
+void CompileScript(Context* context, const String& fileName)
+{
+    PrintLine("Compiling script file " + fileName);
+    
+    File inFile(context, fileName, FILE_READ);
+    if (!inFile.IsOpen())
+        ErrorExit("Failed to open script file " + fileName);
+    
+    ScriptFile script(context);
+    if (!script.Load(inFile))
+        ErrorExit("Failed to compile script file " + fileName + ": " + context->GetSubsystem<Log>()->GetLastMessage());
+    
+    String outFileName = ReplaceExtension(fileName, ".asc");
+    File outFile(context, outFileName, FILE_WRITE);
+    if (!outFile.IsOpen())
+        ErrorExit("Failed to open output file " + fileName);
+    
+    script.SaveByteCode(outFile);
+}