Jump to content

  • Log In with Google      Sign In   
  • Create Account

Interested in a FREE copy of HTML5 game maker Construct 2?

We'll be giving away three Personal Edition licences in next Tuesday's GDNet Direct email newsletter!

Sign up from the right-hand sidebar on our homepage and read Tuesday's newsletter for details!


We're also offering banner ads on our site from just $5! 1. Details HERE. 2. GDNet+ Subscriptions HERE. 3. Ad upload HERE.


AgentC

Member Since 16 May 2005
Offline Last Active Yesterday, 07:39 AM

Topics I've Started

tempVariables assertion with indexed unsafe reference

21 June 2014 - 12:58 PM

Hi,

for some time I've been unable to migrate to newer versions of AngelScript due to a tempVariables assertion I'm getting in debug mode (at least VS2008). I finally managed to make a stand-alone reproduction case. The example contains a lot of missing/stub functionality that would crash if actually run, but please don't mind that smile.png

 

The assertion I'm getting is: tempVariables.Exists(offset), file ..\..\source\as_compiler.cpp, line 4840, using the newest SVN revision. The code is also using the scriptarray add-on.

 

As far as I understand, it's the assigning to an unsafely referenced value type, that has been acquired with a nested indexing, that causes the assert.

#include <angelscript.h>
#include <string>

#include "scriptarray.h"

using namespace std;

class String
{
public:
    bool Empty() const { return true; }
};

static String StringFactory(asUINT length, const char* s)
{
    return String();
}

static void ConstructString(String* ptr)
{
    new(ptr) String();
}

static void ConstructStringCopy(const String& str, String* ptr)
{
    new(ptr) String(str);
}

static void DestructString(String* ptr)
{
    ptr->~String();
}

class ShortStringHash
{
public:
    ShortStringHash() {}
    ShortStringHash(const String& str) {}
};

static void ConstructShortStringHash(ShortStringHash* ptr)
{
    new(ptr) ShortStringHash();
}

class Variant
{
};

static void ConstructVariant(Variant* ptr)
{
    new(ptr) Variant();
}

static void ConstructVariantCopy(const Variant& variant, Variant* ptr)
{
    new(ptr) Variant(variant);
}

static void DestructVariant(Variant* ptr)
{
    ptr->~Variant();
}

class VariantMap
{
public:
    Variant& operator [] (const ShortStringHash& key) { return data; }
    Variant data;
};

static void ConstructVariantMap(VariantMap* ptr)
{
    new(ptr) VariantMap();
}

static void ConstructVariantMapCopy(const VariantMap& map, VariantMap* ptr)
{
    new(ptr) VariantMap(map);
}

static void DestructVariantMap(VariantMap* ptr)
{
    ptr->~VariantMap();
}

static Variant& VariantMapAt(const String& key, VariantMap& map)
{
    return map[key];
}

static Variant& VariantMapAtHash(ShortStringHash key, VariantMap& map)
{
    return map[key];
}

static CScriptArray* VariantMapGetKeys(const VariantMap& map)
{
    return 0;
}

class UIElement
{
public:
    void AddRef() {}
    void ReleaseRef() {}
    
    VariantMap vars;
};

static VariantMap& UIElementGetVars(UIElement* ptr)
{
    return const_cast<VariantMap&>(ptr->vars);
}

static String GetVariableName(const ShortStringHash& key)
{
    return String();
}

void MessageCallback(const asSMessageInfo* msg)
{
    printf("%s:%d,%d %s\n", msg->section, msg->row, msg->col, msg->message);
}

int main(int argc, char** argv)
{
    asIScriptEngine* engine = asCreateScriptEngine(ANGELSCRIPT_VERSION);

    engine->SetEngineProperty(asEP_USE_CHARACTER_LITERALS, true);
    engine->SetEngineProperty(asEP_ALLOW_UNSAFE_REFERENCES, true);
    engine->SetEngineProperty(asEP_ALLOW_IMPLICIT_HANDLE_TYPES, true);
    engine->SetEngineProperty(asEP_BUILD_WITHOUT_LINE_CUES, true);
    engine->SetMessageCallback(asFUNCTION(MessageCallback), 0, asCALL_CDECL);
    
    engine->RegisterObjectType("Array<class T>", 0, asOBJ_REF | asOBJ_TEMPLATE);
    engine->RegisterObjectBehaviour("Array<T>", asBEHAVE_FACTORY, "Array<T>@ f(int& in)", asFUNCTIONPR(CScriptArray::Create, (asIObjectType*), CScriptArray*), asCALL_CDECL);
    engine->RegisterObjectBehaviour("Array<T>", asBEHAVE_FACTORY, "Array<T>@ f(int& in, uint)", asFUNCTIONPR(CScriptArray::Create, (asIObjectType*, asUINT), CScriptArray*), asCALL_CDECL);
    engine->RegisterObjectBehaviour("Array<T>", asBEHAVE_FACTORY, "Array<T>@ f(int& in, uint, const T& in)", asFUNCTIONPR(CScriptArray::Create, (asIObjectType*, asUINT, void *), CScriptArray*), asCALL_CDECL);
    engine->RegisterObjectBehaviour("Array<T>", asBEHAVE_LIST_FACTORY, "Array<T>@ f(int&in type, int&in list) {repeat T}", asFUNCTIONPR(CScriptArray::Create, (asIObjectType*, void*), CScriptArray*), asCALL_CDECL);
    engine->RegisterObjectBehaviour("Array<T>", asBEHAVE_ADDREF, "void f()", asMETHOD(CScriptArray,AddRef), asCALL_THISCALL);
    engine->RegisterObjectBehaviour("Array<T>", asBEHAVE_RELEASE, "void f()", asMETHOD(CScriptArray,Release), asCALL_THISCALL);
    engine->RegisterObjectMethod("Array<T>", "Array<T>& opAssign(const Array<T>& in)", asMETHOD(CScriptArray, operator=), asCALL_THISCALL);
    engine->RegisterObjectMethod("Array<T>", "T& opIndex(uint)", asMETHODPR(CScriptArray, At, (unsigned), void*), asCALL_THISCALL);
    engine->RegisterObjectMethod("Array<T>", "const T& opIndex(uint) const", asMETHODPR(CScriptArray, At, (unsigned), void*), asCALL_THISCALL);
    engine->RegisterObjectMethod("Array<T>", "uint get_length() const", asMETHOD(CScriptArray, GetSize), asCALL_THISCALL);
    
    engine->RegisterObjectType("String", sizeof(String), asOBJ_VALUE | asOBJ_APP_CLASS_CDAK);
    engine->RegisterStringFactory("String", asFUNCTION(StringFactory), asCALL_CDECL);
    engine->RegisterObjectBehaviour("String", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructString), asCALL_CDECL_OBJLAST);
    engine->RegisterObjectBehaviour("String", asBEHAVE_CONSTRUCT, "void f(const String&in)", asFUNCTION(ConstructStringCopy), asCALL_CDECL_OBJLAST);
    engine->RegisterObjectBehaviour("String", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(DestructString), asCALL_CDECL_OBJLAST);
    engine->RegisterObjectMethod("String", "String& opAssign(const String&in)", asMETHODPR(String, operator =, (const String&), String&), asCALL_THISCALL);
    engine->RegisterObjectMethod("String", "bool get_empty() const", asMETHOD(String, Empty), asCALL_THISCALL);
    
    engine->RegisterObjectType("ShortStringHash", sizeof(ShortStringHash), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_CAK);
    engine->RegisterObjectBehaviour("ShortStringHash", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructShortStringHash), asCALL_CDECL_OBJLAST);
    
    engine->RegisterObjectType("Variant", sizeof(Variant), asOBJ_VALUE | asOBJ_APP_CLASS_CDAK);
    engine->RegisterObjectBehaviour("Variant", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructVariant), asCALL_CDECL_OBJLAST);
    engine->RegisterObjectBehaviour("Variant", asBEHAVE_CONSTRUCT, "void f(const Variant&in)", asFUNCTION(ConstructVariantCopy), asCALL_CDECL_OBJLAST);
    engine->RegisterObjectBehaviour("Variant", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(DestructVariant), asCALL_CDECL_OBJLAST);
    engine->RegisterObjectMethod("Variant", "Variant& opAssign(const Variant&in)", asMETHODPR(Variant, operator =, (const Variant&), Variant&), asCALL_THISCALL);
    
    engine->RegisterObjectType("VariantMap", sizeof(VariantMap), asOBJ_VALUE | asOBJ_APP_CLASS_CDAK);
    engine->RegisterObjectBehaviour("VariantMap", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructVariantMap), asCALL_CDECL_OBJLAST);
    engine->RegisterObjectBehaviour("VariantMap", asBEHAVE_CONSTRUCT, "void f(const VariantMap&in)", asFUNCTION(ConstructVariantMapCopy), asCALL_CDECL_OBJLAST);
    engine->RegisterObjectBehaviour("VariantMap", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(DestructVariantMap), asCALL_CDECL_OBJLAST);
    engine->RegisterObjectMethod("VariantMap", "Array<ShortStringHash>@ get_keys() const", asFUNCTION(VariantMapGetKeys), asCALL_CDECL_OBJLAST);
    engine->RegisterObjectMethod("VariantMap", "Variant& opIndex(const String&in)", asFUNCTION(VariantMapAt), asCALL_CDECL_OBJLAST);
    engine->RegisterObjectMethod("VariantMap", "const Variant& opIndex(const String&in) const", asFUNCTION(VariantMapAt), asCALL_CDECL_OBJLAST);
    engine->RegisterObjectMethod("VariantMap", "Variant& opIndex(ShortStringHash)", asFUNCTION(VariantMapAtHash), asCALL_CDECL_OBJLAST);
    engine->RegisterObjectMethod("VariantMap", "const Variant& opIndex(ShortStringHash) const", asFUNCTION(VariantMapAtHash), asCALL_CDECL_OBJLAST);
    
    engine->RegisterObjectType("UIElement", 0, asOBJ_REF);
    engine->RegisterObjectBehaviour("UIElement", asBEHAVE_ADDREF, "void f()", asMETHODPR(UIElement, AddRef, (), void), asCALL_THISCALL);
    engine->RegisterObjectBehaviour("UIElement", asBEHAVE_RELEASE, "void f()", asMETHODPR(UIElement, ReleaseRef, (), void), asCALL_THISCALL);
    engine->RegisterObjectMethod("UIElement", "VariantMap& get_vars()", asFUNCTION(UIElementGetVars), asCALL_CDECL_OBJLAST);
    
    engine->RegisterGlobalFunction("String GetVariableName(const ShortStringHash&in)", asFUNCTION(GetVariableName), asCALL_CDECL);
    
    asIScriptModule* module = engine->GetModule("Test", asGM_ALWAYS_CREATE);
    
    string script =
        "UIElement@ element;\n"
        "VariantMap internalVars;\n"
        "void Test()\n"
        "{\n"
        "    Array<ShortStringHash> keys = element.vars.keys;\n"
        "    for (uint i = 0; i < keys.length; ++i)\n"
        "    {\n"
        "        String name = GetVariableName(keys[i]);\n"
        "        if (name.empty)\n"
        "            internalVars[keys[i]] = element.vars[keys[i]];\n"
        "    }\n"
        "}\n\n";
    printf("%s", script.c_str());
    
    module->AddScriptSection("Test", script.c_str());
    module->Build();
    
    return 0;
}

Old school 3D engines

13 February 2014 - 09:20 AM

I'd be interested to read about the very old school (pre-Wolfenstein) 3D engines used in DOS-era simulation games such as LHX Attack Chopper, for example about the data / acceleration structures they used to represent the 3D worlds. Any resources you know of? A cursory Google search did not reveal much.

 

I remember programming filled 3D graphics in the DOS days using tutorials like the PC-GPE, but at the time my understanding of things like matrices, frustum culling, proper clipping or acceleration structures were pretty much nonexistent; if I had multiple objects to draw I'd just store them to a flat array.

 

Nowadays it's common to use a quadtree or octree, but when dealing with 16-bit segmented memory, it wouldn't be that straightforward.


Assert when casting void return value to an object handle

02 November 2013 - 09:40 AM

Hi,

in the Urho3D engine we saw the following assert trigger when in script, the return value of a void function was mistakenly attempted to be cast into an object handle:

    asASSERT(ctx->type.dataType.IsReference());

in as_compiler.cpp, function asCCompiler::ConvertToVariable(asSExprContext *ctx), around line 10974.

 

To fix, I added the following check for void to asCCompiler::CompileConversion(asCScriptNode *node, asSExprContext *ctx) around line 8150:

    bool conversionOK = false;

    if( !expr.type.isConstant && expr.type.dataType.GetTokenType() != ttVoid )

which may not be the best way to fix, but seemed to work.


Automatically grouping objects for culling, drawcall generation etc.?

08 September 2013 - 02:52 PM

Some background: right now, my engine uses a loose octree for culling. Naturally, whole octree subhierarchies can be rejected if outside the view frustum, but the objects that are found to be visible, are treated as individual entities almost to the end of the CPU part of the rendering pipeline. Only when drawcalls are finally formed of the visible scene, then drawcalls using the same material, same vertex buffer, the same lights influencing them etc. are combined into instancing groups, which basically list the GPU state to use + transform matrices for all instances.

 

Using this system, hardware instancing happens automatically when possible, so the user of the engine doesn't have to care about it at all. However, when dealing with high object counts, it is somewhat wasteful to treat the objects as individuals so far up the pipeline, and "re-detect" each frame that these objects indeed belong to the same instancing group.

 

I was wondering, is someone here using some kind of dynamic grouping system to form groups of objects automatically at runtime, for example let's say there's 5 tree objects next to each other (with same material, VB..) their bounding boxes would be combined into a group bounding box, and they would be treated as a group from there onwards, until something changes which invalidates the group (for example one of them changes LOD, or a dynamic light starts shining on one of them.)

 

What I'm afraid is that the management overhead for the groups (namely, having to check that they remain valid once they're formed) would consume more CPU than just bruteforcing the objects as individuals right to the end of the pipeline.

 

Any ideas or hints?


Assert in as_compiler.cpp, temp variables

19 February 2013 - 10:26 AM

Hi,

for some of the latest AngelScript versions I've been getting an assert in as_compiler.cpp, function CompileStatementBlock, asASSERT( tempVariables.GetLength() == 0; ) . This still persists in the latest SVN revision.

 

I seem to be able to trigger it (at least) with the following if statement, where RayQueryResult is a value type, Drawable is a reference type, and String is a value type string adapted from the AngelScript addons.

 

 

RayQueryResult res;
if (res.drawable.typename == "AnimatedModel") // compiling this triggers the assert
{
  Print("is animated");
}

 

If I split up the if statement as follows, it will not assert:

 

 

RayQueryResult res;
Drawable@ dr = res.drawable;
if (dr.typename == "AnimatedModel")
{
  Print("is animated");
}

 

 

The relevant class and function registrations should be:

 

 

engine->RegisterObjectType("String", sizeof(String), asOBJ_VALUE | asOBJ_APP_CLASS_CDAK);
engine->RegisterStringFactory("String", asFUNCTION(StringFactory), asCALL_CDECL);
engine->RegisterObjectMethod("String", "bool opEquals(const String&in) const", asMETHODPR(String, operator ==, (const String&) const, bool), asCALL_THISCALL);
 
engine->RegisterObjectType("Drawable", 0, asOBJ_REF);
engine->RegisterObjectMethod("Drawable", "const String& get_typeName() const", asMETHODPR(Drawable, GetTypeName, () const, const String&), asCALL_THISCALL);
 
engine->RegisterObjectType("RayQueryResult", sizeof(RayQueryResult), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_C);
engine->RegisterObjectBehaviour("RayQueryResult", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructRayQueryResult), asCALL_CDECL_OBJLAST);
engine->RegisterObjectMethod("RayQueryResult", "Drawable@+ get_drawable() const", asFUNCTION(RayQueryResultGetDrawable), asCALL_CDECL_OBJLAST);

PARTNERS