Beispiel #1
0
void SortVector::hashAndSortAllTask(GCTaskId*, void *a, void *b)
{
    SortVector *s = (SortVector *)a;
    // Hash the contents of the base object then sort them.
    for (unsigned i = 0; i < 256; i++)
    {
        // Clear the entries in the hash table but not the sharing count.
        s->processObjects[i].objList = ENDOFLIST;
        s->processObjects[i].objCount = 0;
    }
    PolyObject *h = s->baseObject.objList;
    POLYUNSIGNED bytes = OBJ_OBJECT_LENGTH(s->lengthWord)*sizeof(PolyWord);
    while (h != ENDOFLIST)
    {
        PolyObject *next = h->GetForwardingPtr();
        unsigned char hash = 0;
        for (POLYUNSIGNED j = 0; j < bytes; j++)
            hash += h->AsBytePtr()[j];
        h->SetForwardingPtr(s->processObjects[hash].objList);
        s->processObjects[hash].objList = h;
        s->processObjects[hash].objCount++;
        h = next;
    }
    s->SortData();
}
Beispiel #2
0
void MatMod::ModifyObject(TimeValue t, ModContext &mc, ObjectState *os, INode *node)
   {
   Interval valid = FOREVER;
   int id;
   pblock->GetValue(PB_MATID,t,id,valid); 
   id--;
   if (id<0) id = 0;
   if (id>0xffff) id = 0xffff;

   // For version 4 and later, we process patch meshes as they are and pass them on.  Earlier
   // versions converted to TriMeshes (done below).  For adding other new types of objects, add
   // them here!
#ifndef NO_PATCHES
   if(version >= MATMOD_VER4 && os->obj->IsSubClassOf(patchObjectClassID)) {
      PatchObject *patchOb = (PatchObject *)os->obj;
      PatchMesh &pmesh = patchOb->GetPatchMesh(t);
      BOOL useSel = pmesh.selLevel >= PO_PATCH;

      for (int i=0; i<pmesh.getNumPatches(); i++) {
         if (!useSel || pmesh.patchSel[i]) {
            pmesh.setPatchMtlIndex(i,(MtlID)id);
            }
         }
      pmesh.InvalidateGeomCache();  // Do this because there isn't a topo cache in PatchMesh
                  
      patchOb->UpdateValidity(TOPO_CHAN_NUM,valid);      
      }
   else
#endif // NO_PATCHES
   // Process PolyObjects
   if(os->obj->IsSubClassOf(polyObjectClassID)) {
      PolyObject *polyOb = (PolyObject *)os->obj;
      MNMesh &mesh = polyOb->GetMesh();
      BOOL useSel = mesh.selLevel == MNM_SL_FACE;

      for (int i=0; i<mesh.numf; i++) {
         if (!useSel || mesh.f[i].GetFlag(MN_SEL)) {
            mesh.f[i].material = (MtlID)id;
         }
      }
      polyOb->UpdateValidity(TOPO_CHAN_NUM,valid);    
   }
   else  // If it's a TriObject, process it
   if(os->obj->IsSubClassOf(triObjectClassID)) {
      TriObject *triOb = (TriObject *)os->obj;
      DoMaterialSet(triOb, id);
      triOb->UpdateValidity(TOPO_CHAN_NUM,valid);     
      }
   else  // Fallback position: If it can convert to a TriObject, do it!
   if(os->obj->CanConvertToType(triObjectClassID)) {
      TriObject  *triOb = (TriObject *)os->obj->ConvertToType(t, triObjectClassID);
      // Now stuff this into the pipeline!
      os->obj = triOb;

      DoMaterialSet(triOb, id);
      triOb->UpdateValidity(TOPO_CHAN_NUM,valid);     
      }
   else
      return;     // Do nothing if it can't convert to triObject
   }
Beispiel #3
0
bool GetSharing::TestForScan(PolyWord *pt)
{
    PolyObject *obj;

    // This may be a forwarding pointer left over from a minor GC that did
    // not complete or it may be a sharing chain pointer that we've set up.
    while (1)
    {
        PolyWord p = *pt;
        ASSERT(p.IsDataPtr());
        obj = p.AsObjPtr();
        PolyWord *lengthWord = ((PolyWord*)obj) - 1;
        LocalMemSpace *space = gMem.LocalSpaceForAddress(lengthWord);
        if (space == 0)
            return false; // Ignore it if it points to a permanent area

        if (space->bitmap.TestBit(space->wordNo(lengthWord)))
            return false;

        // Wasn't marked - must be a forwarding pointer.
        if (obj->ContainsForwardingPtr())
        {
            obj = obj->GetForwardingPtr();
            *pt = obj;
        }
        else break;
    }

    ASSERT(obj->ContainsNormalLengthWord());

    totalVisited += 1;
    totalSize += obj->Length() + 1;

    return true;
}
Beispiel #4
0
static void CheckAddress(PolyWord *pt)
{
    MemSpace *space = gMem.SpaceForAddress(pt-1);
    if (space == 0)
    {
        Log("Check: Bad pointer %p (no space found)\n", pt);
        ASSERT(space != 0);
    }
    if (space->spaceType == ST_STACK) // This may not have valid length words.
        return;
    PolyObject *obj = (PolyObject*)pt;
    ASSERT(obj->ContainsNormalLengthWord());
    POLYUNSIGNED length = obj->Length();
    if (pt+length > space->top)
    {
        Log("Check: Bad pointer %p (space %p) length %" POLYUFMT "\n", pt, space, length);
        ASSERT(pt+length <= space->top);
    }
    if (space->spaceType == ST_LOCAL)
    {
        LocalMemSpace *lSpace = (LocalMemSpace*)space;
        if (!((pt > lSpace->bottom && pt+length <= lSpace->lowerAllocPtr) ||
            (pt > lSpace->upperAllocPtr && pt+length <= space->top)))
        {
            Log("Check: Bad pointer %p (space %p) length %" POLYUFMT " outside allocated area\n", pt, space, length);
            ASSERT((pt > lSpace->bottom && pt+length <= lSpace->lowerAllocPtr) ||
                (pt > lSpace->upperAllocPtr && pt+length <= space->top));
        }
    }
}
Beispiel #5
0
// Tests if this needs to be scanned.  It marks it if it has not been marked
// unless it has to be scanned.
bool MTGCProcessMarkPointers::TestForScan(PolyWord *pt)
{
    if ((*pt).IsTagged())
        return false;

    // This could contain a forwarding pointer if it points into an
    // allocation area and has been moved by the minor GC.
    // We have to be a little careful.  Another thread could also
    // be following any forwarding pointers here.  However it's safe
    // because they will update it with the same value.
    PolyObject *obj = (*pt).AsObjPtr();
    if (obj->ContainsForwardingPtr())
    {
        obj = FollowForwarding(obj);
        *pt = obj;
    }

    if (gMem.LocalSpaceForAddress(obj) == 0)
        return false; // Ignore it if it points to a permanent area

    POLYUNSIGNED L = obj->LengthWord();
    if (L & _OBJ_GC_MARK)
        return false; // Already marked

    if (debugOptions & DEBUG_GC_DETAIL)
        Log("GC: Mark: %p %" POLYUFMT " %u\n", obj, OBJ_OBJECT_LENGTH(L), GetTypeBits(L));

    if (OBJ_IS_BYTE_OBJECT(L))
    {
        obj->SetLengthWord(L | _OBJ_GC_MARK); // Mark it
        return false; // We've done as much as we need
    }
    return true;
}
Beispiel #6
0
void MTGCProcessMarkPointers::MarkAndTestForScan(PolyWord *pt)
{
    if (TestForScan(pt))
    {
        PolyObject *obj = (*pt).AsObjPtr();
        obj->SetLengthWord(obj->LengthWord() | _OBJ_GC_MARK);
    }
}
Beispiel #7
0
bool PImport::GetValue(PolyWord *result)
{
    int ch = getc(f);
    if (ch == '@')
    {
        /* Address of an object. */
        POLYUNSIGNED obj;
        fscanf(f, "%" POLYUFMT, &obj);
        ASSERT(obj < nObjects);
        *result = objMap[obj];
    }
    else if (ch == '$')
    {
        /* Code address. */
        POLYUNSIGNED obj, offset;
        fscanf(f, "%" POLYUFMT "+%" POLYUFMT, &obj, &offset);
        ASSERT(obj < nObjects);
        PolyObject *q = objMap[obj];
        ASSERT(q->IsCodeObject());
        *result = PolyWord::FromCodePtr((PolyWord(q)).AsCodePtr() + offset); /* The offset is in bytes. */
    }
    else if ((ch >= '0' && ch <= '9') || ch == '-')
    {
        /* Tagged integer. */
        POLYSIGNED j;
        ungetc(ch, f);
        fscanf(f, "%" POLYSFMT, &j);
        /* The assertion may be false if we are porting to a machine
           with a shorter tagged representation. */
        ASSERT(j >= -MAXTAGGED-1 && j <= MAXTAGGED);
        *result = TAGGED(j);
    }
    else if (ch == 'I')
    {
        /* IO entry number. */
        POLYUNSIGNED j;
        fscanf(f, "%" POLYUFMT, &j);
        ASSERT(j < POLY_SYS_vecsize);
        *result = (PolyObject*)&gMem.ioSpace->bottom[j * IO_SPACING];
    }
    else if (ch == 'J')
    {
        /* IO entry number with offset. */
        POLYUNSIGNED j, offset;
        fscanf(f, "%" POLYUFMT "+%" POLYUFMT, &j, &offset);
        ASSERT(j < POLY_SYS_vecsize);
        PolyWord base = (PolyObject*)&gMem.ioSpace->bottom[j * IO_SPACING];
        *result = PolyWord::FromCodePtr(base.AsCodePtr() + offset);
    }
    else
    {
        fprintf(stderr, "Unexpected character in stream");
        return false;
    }
    return true;
}
Beispiel #8
0
void ScanAddress::ScanAddressesInRegion(PolyWord *region, PolyWord *end)
{
    PolyWord *pt = region;
    while (pt < end)
    {
        pt++; // Skip length word.
        // pt actually points AT the object here.
        PolyObject *obj = (PolyObject*)pt;
        if (obj->ContainsForwardingPtr())    /* skip over moved object */
        {
            // We can now get multiple forwarding pointers as a result
            // of applying ShareData repeatedly.  Perhaps we should
            // turn the forwarding pointers back into normal words in
            // an extra pass.
            while (obj->ContainsForwardingPtr())
                obj = obj->GetForwardingPtr();
            ASSERT(obj->ContainsNormalLengthWord());
            CheckObject(obj);
            pt += obj->Length();
        }
        else
        {
            ASSERT(obj->ContainsNormalLengthWord());
            POLYUNSIGNED length = obj->Length();
            if (pt+length > end)
                Crash("Malformed object at %p - length %lu\n", pt, length);
            if (length != 0)
                ScanAddressesInObject(obj);
            pt += length;
        }
    }
}
Beispiel #9
0
Handle real_result(TaskData *mdTaskData, double x)
{
    union db argx;
    
    argx.dble = x;
    
    PolyObject *v = alloc(mdTaskData, DBLE/sizeof(PolyWord), F_BYTE_OBJ);
    /* Copy as words in case the alignment is wrong. */
    for(unsigned i = 0; i < DBLE; i++)
    {
        v->AsBytePtr()[i] = argx.bytes[i];
    }
    return mdTaskData->saveVec.push(v);
}
Beispiel #10
0
void DoCheckPointer (const PolyWord pt)
{
    if (pt == PolyWord::FromUnsigned(0)) return;

    if (OBJ_IS_AN_INTEGER(pt)) return;

    DoCheck (pt);

    if (pt.IsDataPtr())
    {
        PolyObject *obj = pt.AsObjPtr();
        DoCheckObject (obj, obj->LengthWord());
    }
}
Beispiel #11
0
ProcessAddToVector::~ProcessAddToVector()
{
    // Normally the stack will be empty.  However if we have run out of
    // memory and thrown an exception we may well have items left.
    // We have to remove the mark bits otherwise it will mess up any
    // subsequent GC.
    for (unsigned i = 0; i < asp; i++)
    {
        PolyObject *obj = addStack[i];
        if (obj->LengthWord() & _OBJ_GC_MARK)
            obj->SetLengthWord(obj->LengthWord() & (~_OBJ_GC_MARK));
    }

    free(addStack); // Now free the stack
}
Beispiel #12
0
void AFRMod::ModifyObject (TimeValue t, ModContext &mc, ObjectState *os, INode *node) {
	Interval iv = FOREVER;
	float f, p, b;
	int backface;
	Point3 pt1, pt2;
	pblock->GetValue(PB_FALLOFF,t,f,iv);
	pblock->GetValue(PB_PINCH,t,p,iv);
	pblock->GetValue(PB_BUBBLE,t,b,iv);
	pblock->GetValue(PB_BACKFACE,t,backface,iv);
	p1->GetValue(t,&pt1,iv,CTRL_ABSOLUTE);
	p2->GetValue(t,&pt2,iv,CTRL_ABSOLUTE);
	if (f==0.0) {
		os->obj->UpdateValidity(GEOM_CHAN_NUM,iv);	
		return;
	}
	Tab<Point3> normals;
	if (backface) {
		// Need to get vertex normals.
		if (os->obj->IsSubClassOf(triObjectClassID)) {
			TriObject *tobj = (TriObject*)os->obj;
			AverageVertexNormals (tobj->GetMesh(), normals);
		} else if (os->obj->IsSubClassOf (polyObjectClassID)) {
			PolyObject *pobj = (PolyObject *) os->obj;
			MNMesh &mesh = pobj->GetMesh();
			normals.SetCount (mesh.numv);
			Point3 *vn = normals.Addr(0);
			for (int i=0; i<mesh.numv; i++) {
				if (mesh.v[i].GetFlag (MN_DEAD)) vn[i]=Point3(0,0,0);
				else vn[i] = mesh.GetVertexNormal (i);
			}
#ifndef NO_PATCHES
		} else if (os->obj->IsSubClassOf (patchObjectClassID)) {
			PatchObject *pobj = (PatchObject *) os->obj;
			normals.SetCount (pobj->NumPoints ());
			Point3 *vn = normals.Addr(0);
			for (int i=0; i<pobj->NumPoints(); i++) vn[i] = pobj->VertexNormal (i);
#endif
		}
	}
	if (normals.Count()) {
		AFRDeformer deformer(mc,f,p,b,pt1,pt2,&normals);
		os->obj->Deform(&deformer, TRUE);
	} else {
		AFRDeformer deformer(mc,f,p,b,pt1,pt2);
		os->obj->Deform(&deformer, TRUE);
	}	
	os->obj->UpdateValidity(GEOM_CHAN_NUM,iv);	
	}
Beispiel #13
0
IGeometryChecker::ReturnVal FlippedFacesChecker::GeometryCheck(TimeValue t,INode *nodeToCheck, IGeometryChecker::OutputVal &val)
{
	val.mIndex.ZeroCount();
	if(IsSupported(nodeToCheck))
	{
		ObjectState os  = nodeToCheck->EvalWorldState(t);
		Object *obj = os.obj;
	
		if(os.obj->IsSubClassOf(triObjectClassID))
		{
			TriObject *tri = dynamic_cast<TriObject *>(os.obj);
			if(tri&&tri->mesh.numFaces>0)
			{
				tri->mesh.checkNormals(TRUE);
				for(int i=0;i<tri->mesh.numFaces;++i)
				{
					val.mIndex.Append(1,&i);
				}
				return TypeReturned();
			}
			else return IGeometryChecker::eFail;
		}
		else if(os.obj->IsSubClassOf(polyObjectClassID))
		{
			PolyObject *poly = dynamic_cast<PolyObject *>(os.obj);
			if(poly&&poly->GetMesh().numf>0)
			{
				IGeometryChecker::ReturnVal returnval=IGeometryChecker::eFail;
				MNMesh &mnMesh = poly->GetMesh();
				for(int i=0;i<mnMesh.numf;++i)
				{
					val.mIndex.Append(1,&i);
				}
				return TypeReturned();
			}
			else return IGeometryChecker::eFail;
		}
		
		return TypeReturned();
	}
	return IGeometryChecker::eFail;
}
Beispiel #14
0
Handle Real_strc(TaskData *mdTaskData, Handle hDigits, Handle hMode, Handle arg)
{
    double  dx = real_arg(arg);
    int     decpt, sign;
    int     mode = get_C_int(mdTaskData, DEREFWORDHANDLE(hMode));
    int     digits = get_C_int(mdTaskData, DEREFWORDHANDLE(hDigits));
    /* Compute the shortest string which gives the required value. */
    /*  */
    char *chars = poly_dtoa(dx, mode, digits, &decpt, &sign, NULL);
    /* We have to be careful in case an allocation causes a
       garbage collection. */
    PolyWord pStr = C_string_to_Poly(mdTaskData, chars);
    poly_freedtoa(chars);
    Handle ppStr = mdTaskData->saveVec.push(pStr);
    /* Allocate a triple for the results. */
    PolyObject *result = alloc(mdTaskData, 3);
    result->Set(0, DEREFWORDHANDLE(ppStr));
    result->Set(1, TAGGED(decpt));
    result->Set(2, TAGGED(sign));
    return mdTaskData->saveVec.push(result);
}
Beispiel #15
0
// Returns the new address if the argument is the address of an object that
// has moved, otherwise returns the original.
PolyWord SaveFixupAddress::GetNewAddress(PolyWord old)
{
    if (old.IsTagged() || old == PolyWord::FromUnsigned(0) || gMem.IsIOPointer(old.AsAddress()))
        return old; //  Nothing to do.

    // When we are updating addresses in the stack or in code segments we may have
    // code pointers.
    if (old.IsCodePtr())
    {
        // Find the start of the code segment
        PolyObject *oldObject = ObjCodePtrToPtr(old.AsCodePtr());
        // Calculate the byte offset of this value within the code object.
        POLYUNSIGNED offset = old.AsCodePtr() - (byte*)oldObject;
        PolyWord newObject = GetNewAddress(oldObject);
        return PolyWord::FromCodePtr(newObject.AsCodePtr() + offset);
    }

    ASSERT(old.IsDataPtr());

    PolyObject *obj = old.AsObjPtr();
    
    if (obj->ContainsForwardingPtr()) // tombstone is a pointer to a moved object
    {
        PolyObject *newp = obj->GetForwardingPtr();
        ASSERT (newp->ContainsNormalLengthWord());
        return newp;
    }
    
    ASSERT (obj->ContainsNormalLengthWord()); // object is not moved
    return old;
}
Beispiel #16
0
// Returns the new address if the argument is the address of an object that
// has moved, otherwise returns the original.
PolyWord ProcessFixupAddress::GetNewAddress(PolyWord old)
{
    if (old.IsTagged() || old == PolyWord::FromUnsigned(0) || gMem.IsIOPointer(old.AsAddress()))
        return old; //  Nothing to do.

    // When we are updating addresses in the stack or in code segments we may have
    // code pointers.
    if (old.IsCodePtr())
    {
        // Find the start of the code segment
        PolyObject *oldObject = ObjCodePtrToPtr(old.AsCodePtr());
        // Calculate the byte offset of this value within the code object.
        POLYUNSIGNED offset = old.AsCodePtr() - (byte*)oldObject;
        PolyWord newObject = GetNewAddress(oldObject);
        return PolyWord::FromCodePtr(newObject.AsCodePtr() + offset);
    }

    ASSERT(old.IsDataPtr());

    PolyObject *obj = old.AsObjPtr();
    POLYUNSIGNED L = obj->LengthWord();

    // Generally each address will point to an object processed at a lower depth.
    // The exception is if we have a cycle and have assigned the rest of the
    // structure to a higher depth.
    // N.B. We return the original address here but this could actually share
    // with something else and not be retained.
    if (OBJ_IS_DEPTH(L))
        return old;

    if (obj->ContainsForwardingPtr()) // tombstone is a pointer to a shared object
    {
        PolyObject *newp = obj->GetForwardingPtr();
//        ASSERT (newp->ContainsNormalLengthWord());
        return newp;
    }

    ASSERT (obj->ContainsNormalLengthWord()); // object is not shared
    return old;
}
Beispiel #17
0
// Deal with weak objects
void MTGCCheckWeakRef::ScanAddressesInObject(PolyObject *obj, POLYUNSIGNED L)
{
    if (! OBJ_IS_WEAKREF_OBJECT(L)) return;
    ASSERT(OBJ_IS_MUTABLE_OBJECT(L)); // Should be a mutable.
    ASSERT(OBJ_IS_WORD_OBJECT(L)); // Should be a plain object.
    // See if any of the SOME objects contain unreferenced refs.
    POLYUNSIGNED length = OBJ_OBJECT_LENGTH(L);
    PolyWord *baseAddr = (PolyWord*)obj;
    for (POLYUNSIGNED i = 0; i < length; i++)
    {
        PolyWord someAddr = baseAddr[i];
        if (someAddr.IsDataPtr())
        {
            LocalMemSpace *someSpace = gMem.LocalSpaceForAddress(someAddr.AsAddress());
            if (someSpace != 0)
            {
                PolyObject *someObj = someAddr.AsObjPtr();
                // If this is a weak object the SOME value may refer to an unreferenced
                // ref.  If so we have to set this entry to NONE.  For safety we also
                // set the contents of the SOME to TAGGED(0).
                ASSERT(someObj->Length() == 1 && someObj->IsWordObject()); // Should be a SOME node.
                PolyWord refAddress = someObj->Get(0);
                LocalMemSpace *space = gMem.LocalSpaceForAddress(refAddress.AsAddress());
                if (space != 0)
                    // If the ref is permanent it's always there.
                {
                    POLYUNSIGNED new_bitno = space->wordNo(refAddress.AsStackAddr());
                    if (! space->bitmap.TestBit(new_bitno))
                    {
                        // It wasn't marked so it's otherwise unreferenced.
                        baseAddr[i] = TAGGED(0); // Set it to NONE.
                        someObj->Set(0, TAGGED(0)); // For safety.
                        convertedWeak = true;
                    }
                }
            }
        }
    }
}
Beispiel #18
0
BOOL FaceDataExport::nodeEnum(INode* node,Interface *ip) {	
	if(!exportSelected || node->Selected()) {
		ObjectState os = node->EvalWorldState(ip->GetTime());

		IFaceDataMgr *pFDMgr = NULL;
		if (os.obj->IsSubClassOf(triObjectClassID)) {
			TriObject *tobj = (TriObject *)os.obj;
			Mesh* mesh = &tobj->GetMesh();
			pFDMgr = static_cast<IFaceDataMgr*>(mesh->GetInterface( FACEDATAMGR_INTERFACE ));
		} else if (os.obj->IsSubClassOf (polyObjectClassID)) {
			PolyObject *pobj = (PolyObject *)os.obj;
			MNMesh *mesh = &pobj->GetMesh();
			pFDMgr = static_cast<IFaceDataMgr*>(mesh->GetInterface( FACEDATAMGR_INTERFACE ));
		}
		if (pFDMgr == NULL) return FALSE;

		SampleFaceData* SampleDataChan = NULL; 
		IFaceDataChannel* fdc = pFDMgr->GetFaceDataChan( FACE_MAXSAMPLEUSE_CLSID );
		if ( fdc != NULL ) SampleDataChan = dynamic_cast<SampleFaceData*>(fdc);
		if ( SampleDataChan == NULL) {
			fileStream.Printf(_T("Node %s does not have our Face Data\n"),node->GetName());
			return false;
		}

		//OK so We have Face data lets dump it out..
		fileStream.Printf(_T("\nNode %s has %d faces with FaceFloats\n"),node->GetName(), SampleDataChan->Count());
		for(ULONG i=0;i<SampleDataChan->Count();i++) {
			float data = SampleDataChan->data[i];
			fileStream.Printf(_T("Face %d, float %f\n"),i,data);
		}
	}

	// Recurse through this node's children, if any
	for (int c = 0; c < node->NumberOfChildren(); c++) {
		if (!nodeEnum(node->GetChildNode(c), ip)) return FALSE;
	}

	return TRUE;
}
Beispiel #19
0
void EditFaceDataMod::ModifyObject (TimeValue t, ModContext &mc, ObjectState *os, INode *node) {
	if (mDisabled) return;

	EditFaceDataModData *d  = (EditFaceDataModData*)mc.localData;
	if (!d) mc.localData = d = new EditFaceDataModData();

	if (os->obj->IsSubClassOf(triObjectClassID)) {
		// Access the Mesh:
		TriObject *tobj = (TriObject*)os->obj;
		Mesh & mesh = tobj->GetMesh();

		// Apply our modifier's changes:
		d->ApplyChanges (mesh);

		// Update the cache used for display and hit testing:
		if (!d->GetCacheMesh()) d->SetCacheMesh(mesh);

		// Set display flags according to SO level:
		mesh.dispFlags = 0;
		mesh.SetDispFlag (levelDispFlags[selLevel]);

	} else if (os->obj->IsSubClassOf(polyObjectClassID)) {

		// Access the Mesh:
		PolyObject *pobj = (PolyObject*)os->obj;
		MNMesh & mesh = pobj->GetMesh();

		// Apply our modifier's changes:
		d->ApplyChanges (mesh);

		// Update the cache used for display and hit testing:
		if (!d->GetCacheMNMesh()) d->SetCacheMNMesh(mesh);

		// Set display flags according to SO level:
		mesh.dispFlags = 0;
		mesh.SetDispFlag (mnlevelDispFlags[selLevel]);
	}
}
Beispiel #20
0
static void SetBitmaps(LocalMemSpace *space, PolyWord *pt, PolyWord *top)
{
    while (pt < top)
    {
        PolyObject *obj = (PolyObject*)++pt;
        // If it has been copied by a minor collection skip it
        if (obj->ContainsForwardingPtr())
        {
            obj = FollowForwarding(obj);
            ASSERT(obj->ContainsNormalLengthWord());
            pt += obj->Length();
        }
        else
        {
            POLYUNSIGNED L = obj->LengthWord();
            POLYUNSIGNED n = OBJ_OBJECT_LENGTH(L);
            if (L & _OBJ_GC_MARK)
            {
                obj->SetLengthWord(L & ~(_OBJ_GC_MARK));
                POLYUNSIGNED bitno = space->wordNo(pt);
                space->bitmap.SetBits(bitno - 1, n + 1);

                if (OBJ_IS_MUTABLE_OBJECT(L))
                    space->m_marked += n + 1;
                else
                    space->i_marked += n + 1;

                if ((PolyWord*)obj <= space->fullGCLowerLimit)
                    space->fullGCLowerLimit = (PolyWord*)obj-1;

                if (OBJ_IS_WEAKREF_OBJECT(L))
                {
                    // Add this to the limits for the containing area.
                    PolyWord *baseAddr = (PolyWord*)obj;
                    PolyWord *startAddr = baseAddr-1; // Must point AT length word.
                    PolyWord *endObject = baseAddr + n;
                    if (startAddr < space->lowestWeak) space->lowestWeak = startAddr;
                    if (endObject > space->highestWeak) space->highestWeak = endObject;
                }
            }
            pt += n;
        }
    }
}
Beispiel #21
0
void NormalMod::ModifyObject(TimeValue t, ModContext &mc, ObjectState *os, INode *node)
   {
   Interval valid = FOREVER;
   int flip, unify;
   pblock->GetValue(PB_FLIP,t,flip,valid);   
   pblock->GetValue(PB_UNIFY,t,unify,valid); 

   // For version 4 and later, we process patch meshes as they are and pass them on.  Earlier
   // versions converted to TriMeshes (done below).  For adding other new types of objects, add
   // them here!
#ifndef NO_PATCHES
   if(version >= MATMOD_VER4 && os->obj->IsSubClassOf(patchObjectClassID)) {
      PatchObject *patchOb = (PatchObject *)os->obj;
      PatchMesh &pmesh = patchOb->GetPatchMesh(t);
      BOOL useSel = pmesh.selLevel >= PO_PATCH;

      if (unify)
         pmesh.UnifyNormals(useSel);

      if (flip)
         pmesh.FlipPatchNormal(useSel ? -1 : -2);
                  
      patchOb->UpdateValidity(TOPO_CHAN_NUM,valid);      
      }
   else  // If it's a TriObject, process it
#endif // NO_PATCHES
   if(os->obj->IsSubClassOf(triObjectClassID)) {
      TriObject *triOb = (TriObject *)os->obj;
      DoNormalSet(triOb, unify, flip);
      triOb->UpdateValidity(TOPO_CHAN_NUM,valid);     
      }

   // Process PolyObjects
   // note: Since PolyObjects must always have the normals alligned they do not 
   // need to support unify and they do not allow normal flips on selected faces
   else if(os->obj->IsSubClassOf(polyObjectClassID)) {
      PolyObject *pPolyOb = (PolyObject *)os->obj;
      MNMesh& mesh = pPolyOb->GetMesh();


      if (flip) {
         // flip selected faces only if entire elements are selected
         if (mesh.selLevel == MNM_SL_FACE) {
            // sca 12/8/2000: Use MNMesh flipping code instead of the code that was here.
            mesh.FlipElementNormals (MN_SEL);
         } else {
            // Flip the entire object if selected elements were not flipped
            for (int i=0; i<mesh.FNum(); i++) {
               mesh.f[i].SetFlag (MN_WHATEVER, !mesh.f[i].GetFlag(MN_DEAD));
            }
            mesh.FlipElementNormals (MN_WHATEVER);
         }

         // Luna task 747:
         // We cannot support specified normals here at this time.
		 // NOTE this assumes that both the topo and geo channels are to be freed
		 // this means that the modifier needs to also set the channels changed to
		 // geo and topo otherwise we will be deleting a channel we dont own.
         mesh.ClearSpecifiedNormals ();
      }

	  pPolyOb->UpdateValidity(GEOM_CHAN_NUM,valid);      
      pPolyOb->UpdateValidity(TOPO_CHAN_NUM,valid);      
      }

   else  // Fallback position: If it can convert to a TriObject, do it!
   if(os->obj->CanConvertToType(triObjectClassID)) {
      TriObject  *triOb = (TriObject *)os->obj->ConvertToType(t, triObjectClassID);
      // Now stuff this into the pipeline!
      os->obj = triOb;

      DoNormalSet(triOb, unify, flip);
      triOb->UpdateValidity(TOPO_CHAN_NUM,valid);     
      
      }
   else
      return;     // Do nothing if it can't convert to triObject
   }
Beispiel #22
0
void SmoothMod::ModifyObject(TimeValue t, ModContext &mc, ObjectState *os, INode *node) {
   Interval valid = FOREVER;
   int autoSmooth, bits = 0, prevent = 0;
   float thresh = 0.0f;
   pblock->GetValue(sm_autosmooth, t, autoSmooth, valid);
   if (autoSmooth) {
      pblock->GetValue(sm_threshold, t, thresh, valid);
      pblock->GetValue(sm_prevent_indirect, t, prevent, valid);
   } else {
      pblock->GetValue(sm_smoothbits, t, bits, valid);
   }
   // For version 4 and later, we process patch meshes as they are and pass them on.  Earlier
   // versions converted to TriMeshes (done below).  For adding other new types of objects, add
   // them here!
   bool done = false;
#ifndef NO_PATCHES
   if(version >= MATMOD_VER4 && os->obj->IsSubClassOf(patchObjectClassID)) {
      PatchObject *patchOb = (PatchObject *)os->obj;
      PatchMesh &pmesh = patchOb->GetPatchMesh(t);
      BOOL useSel = pmesh.selLevel >= PO_PATCH;

      if (autoSmooth) pmesh.AutoSmooth (thresh, useSel, prevent);
      else {
         for (int i=0; i<pmesh.getNumPatches(); i++) {
            if (!useSel || pmesh.patchSel[i]) pmesh.patches[i].smGroup = (DWORD)bits;
         }
      }
      pmesh.InvalidateGeomCache();  // Do this because there isn't a topo cache in PatchMesh
      patchOb->UpdateValidity(TOPO_CHAN_NUM,valid);
      done = true;
   }
#endif // NO_PATCHES
   if (!done && os->obj->IsSubClassOf (polyObjectClassID)) {
      PolyObject *pPolyOb = (PolyObject *)os->obj;
      MNMesh &mesh = pPolyOb->GetMesh();

      BOOL useSel = (mesh.selLevel == MNM_SL_FACE);
      if (autoSmooth) mesh.AutoSmooth (thresh, useSel, prevent);
      else {
         for (int faceIndex=0; faceIndex<mesh.FNum(); faceIndex++) {
            if (!useSel || mesh.F(faceIndex)->GetFlag(MN_SEL)) {
               mesh.F(faceIndex)->smGroup = (DWORD)bits;
            }
         }
      }

      // Luna task 747
      // We need to rebuild the smoothing-group-based normals in the normalspec, if any:
      if (mesh.GetSpecifiedNormals()) {
         mesh.GetSpecifiedNormals()->SetParent (&mesh);
         mesh.GetSpecifiedNormals()->BuildNormals ();
         mesh.GetSpecifiedNormals()->ComputeNormals ();
      }

      pPolyOb->UpdateValidity(TOPO_CHAN_NUM,valid);
      done = true;
   }

   TriObject *triOb = NULL;
   if (!done) {
      if (os->obj->IsSubClassOf(triObjectClassID)) triOb = (TriObject *)os->obj;
      else {
         // Convert to triobject if we can.
         if(os->obj->CanConvertToType(triObjectClassID)) {
            TriObject  *triOb = (TriObject *)os->obj->ConvertToType(t, triObjectClassID);

            // We'll need to stuff this back into the pipeline:
            os->obj = triOb;

            // Convert validities:
            Interval objValid = os->obj->ChannelValidity (t, TOPO_CHAN_NUM);
            triOb->SetChannelValidity (TOPO_CHAN_NUM, objValid);
            triOb->SetChannelValidity (GEOM_CHAN_NUM,
               objValid & os->obj->ChannelValidity (t, GEOM_CHAN_NUM));
            triOb->SetChannelValidity (TEXMAP_CHAN_NUM,
               objValid & os->obj->ChannelValidity (t, TEXMAP_CHAN_NUM));
            triOb->SetChannelValidity (VERT_COLOR_CHAN_NUM,
               objValid & os->obj->ChannelValidity (t, VERT_COLOR_CHAN_NUM));
            triOb->SetChannelValidity (DISP_ATTRIB_CHAN_NUM,
               objValid & os->obj->ChannelValidity (t, DISP_ATTRIB_CHAN_NUM));
         }
      }
   }

   if (triOb) {   // one way or another, there's a triobject to smooth.
      Mesh & mesh = triOb->GetMesh();
      BOOL useSel = mesh.selLevel == MESH_FACE;
      if (autoSmooth) mesh.AutoSmooth (thresh, useSel, prevent);
      else {
         for (int i=0; i<mesh.getNumFaces(); i++) {
            if (!useSel || mesh.faceSel[i]) mesh.faces[i].smGroup = (DWORD)bits;
         }
      }
      triOb->GetMesh().InvalidateTopologyCache();
      triOb->UpdateValidity(TOPO_CHAN_NUM,valid);     
   }
}
Beispiel #23
0
// Load a saved state file.  Calls itself to handle parent files.
bool StateLoader::LoadFile(bool isInitial, time_t requiredStamp)
{
    LoadRelocate relocate;
    AutoFree<TCHAR*> thisFile(_tcsdup(fileName));

    AutoClose loadFile(_tfopen(fileName, _T("rb")));
    if ((FILE*)loadFile == NULL)
    {
        errorResult = "Cannot open load file";
        errNumber = errno;
        return false;
    }

    SavedStateHeader header;
    // Read the header and check the signature.
    if (fread(&header, sizeof(SavedStateHeader), 1, loadFile) != 1)
    {
        errorResult = "Unable to load header";
        return false;
    }
    if (strncmp(header.headerSignature, SAVEDSTATESIGNATURE, sizeof(header.headerSignature)) != 0)
    {
        errorResult = "File is not a saved state";
        return false;
    }
    if (header.headerVersion != SAVEDSTATEVERSION ||
        header.headerLength != sizeof(SavedStateHeader) ||
        header.segmentDescrLength != sizeof(SavedStateSegmentDescr))
    {
        errorResult = "Unsupported version of saved state file";
        return false;
    }

    // Check that we have the required stamp before loading any children.
    // If a parent has been overwritten we could get a loop.
    if (! isInitial && header.timeStamp != requiredStamp)
    {
        // Time-stamps don't match.
        errorResult = "The parent for this saved state does not match or has been changed";
        return false;
    }

    // Have verified that this is a reasonable saved state file.  If it isn't a
    // top-level file we have to load the parents first.
    if (header.parentNameEntry != 0)
    {
        size_t toRead = header.stringTableSize-header.parentNameEntry;
        if (MAXPATHLEN < toRead) toRead = MAXPATHLEN;

        if (header.parentNameEntry >= header.stringTableSize /* Bad entry */ ||
            fseek(loadFile, header.stringTable + header.parentNameEntry, SEEK_SET) != 0 ||
            fread(fileName, 1, toRead, loadFile) != toRead)
        {
            errorResult = "Unable to read parent file name";
            return false;
        }
        fileName[toRead] = 0; // Should already be null-terminated, but just in case.

        if (! LoadFile(false, header.parentTimeStamp))
            return false;

        // Check the parent time stamp.
        ASSERT(hierarchyDepth > 0 && hierarchyTable[hierarchyDepth-1] != 0);
    }
    else // Top-level file
    {
        if (header.parentTimeStamp != exportTimeStamp)
        {
            // Time-stamp does not match executable.
            errorResult = 
                    "Saved state was exported from a different executable or the executable has changed";
            return false;
        }

        // Any existing spaces at this level or greater must be turned
        // into local spaces.  We may have references from the stack to objects that
        // have previously been imported but otherwise these spaces are no longer
        // needed.
        gMem.DemoteImportSpaces();
        // Clean out the hierarchy table.
        for (unsigned h = 0; h < hierarchyDepth; h++)
        {
            delete(hierarchyTable[h]);
            hierarchyTable[h] = 0;
        }
        hierarchyDepth = 0;
    }

    // Now have a valid, matching saved state.
    // Load the segment descriptors.
    relocate.nDescrs = header.segmentDescrCount;
    relocate.descrs = new SavedStateSegmentDescr[relocate.nDescrs];

    if (fseek(loadFile, header.segmentDescr, SEEK_SET) != 0 ||
        fread(relocate.descrs, sizeof(SavedStateSegmentDescr), relocate.nDescrs, loadFile) != relocate.nDescrs)
    {
        errorResult = "Unable to read segment descriptors";
        return false;
    }

    // Read in and create the new segments first.  If we have problems,
    // in particular if we have run out of memory, then it's easier to recover.  
    for (unsigned i = 0; i < relocate.nDescrs; i++)
    {
        SavedStateSegmentDescr *descr = &relocate.descrs[i];
        MemSpace *space =
            descr->segmentIndex == 0 ? gMem.IoSpace() : gMem.SpaceForIndex(descr->segmentIndex);

        if (descr->segmentData == 0)
        { // No data - just an entry in the index.
            if (space == NULL/* ||
                descr->segmentSize != (size_t)((char*)space->top - (char*)space->bottom)*/)
            {
                errorResult = "Mismatch for existing memory space";
                return false;
            }
        }
        else if ((descr->segmentFlags & SSF_OVERWRITE) == 0)
        { // New segment.
            if (space != NULL)
            {
                errorResult = "Segment already exists";
                return false;
            }
            // Allocate memory for the new segment.
            size_t actualSize = descr->segmentSize;
            PolyWord *mem  =
                (PolyWord*)osMemoryManager->Allocate(actualSize,
                                PERMISSION_READ|PERMISSION_WRITE|PERMISSION_EXEC);
            if (mem == 0)
            {
                errorResult = "Unable to allocate memory";
                return false;
            }
            if (fseek(loadFile, descr->segmentData, SEEK_SET) != 0 ||
                fread(mem, descr->segmentSize, 1, loadFile) != 1)
            {
                errorResult = "Unable to read segment";
                osMemoryManager->Free(mem, descr->segmentSize);
                return false;
            }
            // Fill unused space to the top of the area.
            gMem.FillUnusedSpace(mem+descr->segmentSize/sizeof(PolyWord),
                (actualSize-descr->segmentSize)/sizeof(PolyWord));
            // At the moment we leave all segments with write access.
            unsigned mFlags =
                (descr->segmentFlags & SSF_WRITABLE ? MTF_WRITEABLE : 0) |
                (descr->segmentFlags & SSF_NOOVERWRITE ? MTF_NO_OVERWRITE : 0) |
                (descr->segmentFlags & SSF_BYTES ? MTF_BYTES : 0);
            PermanentMemSpace *newSpace =
                gMem.NewPermanentSpace(mem, actualSize / sizeof(PolyWord), mFlags,
                        descr->segmentIndex, hierarchyDepth+1);
            if (newSpace->isMutable && newSpace->byteOnly)
            {
                ClearWeakByteRef cwbr;
                cwbr.ScanAddressesInRegion(newSpace->bottom, newSpace->topPointer);
            }
        }
    }

    // Now read in the mutable overwrites and relocate.

    for (unsigned j = 0; j < relocate.nDescrs; j++)
    {
        SavedStateSegmentDescr *descr = &relocate.descrs[j];
        MemSpace *space =
            descr->segmentIndex == 0 ? gMem.IoSpace() : gMem.SpaceForIndex(descr->segmentIndex);
        ASSERT(space != NULL); // We should have created it.
        if (descr->segmentFlags & SSF_OVERWRITE)
        {
            if (fseek(loadFile, descr->segmentData, SEEK_SET) != 0 ||
                fread(space->bottom, descr->segmentSize, 1, loadFile) != 1)
            {
                errorResult = "Unable to read segment";
                return false;
            }
        }

        // Relocation.
        if (descr->segmentData != 0)
        {
            // Adjust the addresses in the loaded segment.
            for (PolyWord *p = space->bottom; p < space->top; )
            {
                p++;
                PolyObject *obj = (PolyObject*)p;
                POLYUNSIGNED length = obj->Length();
                relocate.RelocateObject(obj);
                p += length;
            }
        }

        // Process explicit relocations.
        // If we get errors just skip the error and continue rather than leave
        // everything in an unstable state.
        if (descr->relocations)
        {
            if (fseek(loadFile, descr->relocations, SEEK_SET) != 0)
            {
                errorResult = "Unable to read relocation segment";
            }
            for (unsigned k = 0; k < descr->relocationCount; k++)
            {
                RelocationEntry reloc;
                if (fread(&reloc, sizeof(reloc), 1, loadFile) != 1)
                {
                    errorResult = "Unable to read relocation segment";
                }
                MemSpace *toSpace =
                    reloc.targetSegment == 0 ? gMem.IoSpace() : gMem.SpaceForIndex(reloc.targetSegment);
                if (toSpace == NULL)
                {
                    errorResult = "Unknown space reference in relocation";
                    continue;
                }
                byte *setAddress = (byte*)space->bottom + reloc.relocAddress;
                byte *targetAddress = (byte*)toSpace->bottom + reloc.targetAddress;
                if (setAddress >= (byte*)space->top || targetAddress >= (byte*)toSpace->top)
                {
                    errorResult = "Bad relocation";
                    continue;
                }
                ScanAddress::SetConstantValue(setAddress, PolyWord::FromCodePtr(targetAddress), reloc.relKind);
            }
        }
    }

    // Add an entry to the hierarchy table for this file.
    if (! AddHierarchyEntry(thisFile, header.timeStamp))
        return false;

    return true; // Succeeded
}
Beispiel #24
0
// Called by the root thread to actually save the state and write the file.
void SaveRequest::Perform()
{
    // Check that we aren't overwriting our own parent.
    for (unsigned q = 0; q < newHierarchy-1; q++) {
        if (sameFile(hierarchyTable[q]->fileName, fileName))
        {
            errorMessage = "File being saved is used as a parent of this file";
            errCode = 0;
            return;
        }
    }

    SaveStateExport exports;
    // Open the file.  This could quite reasonably fail if the path is wrong.
    exports.exportFile = _tfopen(fileName, _T("wb"));
    if (exports.exportFile == NULL)
    {
        errorMessage = "Cannot open save file";
        errCode = errno;
        return;
    }

    // Scan over the permanent mutable area copying all reachable data that is
    // not in a lower hierarchy into new permanent segments.
    CopyScan copyScan(newHierarchy);
    copyScan.initialise(false);
    bool success = true;
    try {
        for (unsigned i = 0; i < gMem.npSpaces; i++)
        {
            PermanentMemSpace *space = gMem.pSpaces[i];
            if (space->isMutable && ! space->noOverwrite && ! space->byteOnly)
                copyScan.ScanAddressesInRegion(space->bottom, space->top);
        }
    }
    catch (MemoryException &)
    {
        success = false;
    }

    // Copy the areas into the export object.  Make sufficient space for
    // the largest possible number of entries.
    exports.memTable = new memoryTableEntry[gMem.neSpaces+gMem.npSpaces+1];
    exports.ioMemEntry = 0;
    // The IO vector.
    unsigned memTableCount = 0;
    MemSpace *ioSpace = gMem.IoSpace();
    exports.memTable[0].mtAddr = ioSpace->bottom;
    exports.memTable[0].mtLength = (char*)ioSpace->top - (char*)ioSpace->bottom;
    exports.memTable[0].mtFlags = 0;
    exports.memTable[0].mtIndex = 0;
    memTableCount++;

    // Permanent spaces at higher level.  These have to have entries although
    // only the mutable entries will be written.
    for (unsigned w = 0; w < gMem.npSpaces; w++)
    {
        PermanentMemSpace *space = gMem.pSpaces[w];
        if (space->hierarchy < newHierarchy)
        {
            memoryTableEntry *entry = &exports.memTable[memTableCount++];
            entry->mtAddr = space->bottom;
            entry->mtLength = (space->topPointer-space->bottom)*sizeof(PolyWord);
            entry->mtIndex = space->index;
            if (space->isMutable)
            {
                entry->mtFlags = MTF_WRITEABLE;
                if (space->noOverwrite) entry->mtFlags |= MTF_NO_OVERWRITE;
                if (space->byteOnly) entry->mtFlags |= MTF_BYTES;
            }
            else
                entry->mtFlags = MTF_EXECUTABLE;
        }
    }
    unsigned permanentEntries = memTableCount; // Remember where new entries start.

    // Newly created spaces.
    for (unsigned i = 0; i < gMem.neSpaces; i++)
    {
        memoryTableEntry *entry = &exports.memTable[memTableCount++];
        PermanentMemSpace *space = gMem.eSpaces[i];
        entry->mtAddr = space->bottom;
        entry->mtLength = (space->topPointer-space->bottom)*sizeof(PolyWord);
        entry->mtIndex = space->index;
        if (space->isMutable)
        {
            entry->mtFlags = MTF_WRITEABLE;
            if (space->noOverwrite) entry->mtFlags |= MTF_NO_OVERWRITE;
            if (space->byteOnly) entry->mtFlags |= MTF_BYTES;
        }
        else
            entry->mtFlags = MTF_EXECUTABLE;
    }

    exports.memTableEntries = memTableCount;
    exports.ioSpacing = IO_SPACING;

    // Update references to moved objects.
    SaveFixupAddress fixup;
    for (unsigned l = 0; l < gMem.nlSpaces; l++)
    {
        LocalMemSpace *space = gMem.lSpaces[l];
        fixup.ScanAddressesInRegion(space->bottom, space->lowerAllocPtr);
        fixup.ScanAddressesInRegion(space->upperAllocPtr, space->top);
    }
    GCModules(&fixup);

    // Update the global memory space table.  Old segments at the same level
    // or lower are removed.  The new segments become permanent.
    // Try to promote the spaces even if we've had a failure because export
    // spaces are deleted in ~CopyScan and we may have already copied
    // some objects there.
    if (! gMem.PromoteExportSpaces(newHierarchy) || ! success)
    {
        errorMessage = "Out of Memory";
        errCode = ENOMEM;
        return;
    }
    // Remove any deeper entries from the hierarchy table.
    while (hierarchyDepth > newHierarchy-1)
    {
        hierarchyDepth--;
        delete(hierarchyTable[hierarchyDepth]);
        hierarchyTable[hierarchyDepth] = 0;
    }

    // Write out the file header.
    SavedStateHeader saveHeader;
    memset(&saveHeader, 0, sizeof(saveHeader));
    saveHeader.headerLength = sizeof(saveHeader);
    strncpy(saveHeader.headerSignature,
        SAVEDSTATESIGNATURE, sizeof(saveHeader.headerSignature));
    saveHeader.headerVersion = SAVEDSTATEVERSION;
    saveHeader.segmentDescrLength = sizeof(SavedStateSegmentDescr);
    if (newHierarchy == 1)
        saveHeader.parentTimeStamp = exportTimeStamp;
    else
    {
        saveHeader.parentTimeStamp = hierarchyTable[newHierarchy-2]->timeStamp;
        saveHeader.parentNameEntry = sizeof(TCHAR); // Always the first entry.
    }
    saveHeader.timeStamp = time(NULL);
    saveHeader.segmentDescrCount = exports.memTableEntries; // One segment for each space.
    // Write out the header.
    fwrite(&saveHeader, sizeof(saveHeader), 1, exports.exportFile);

    // We need a segment header for each permanent area whether it is
    // actually in this file or not.
    SavedStateSegmentDescr *descrs = new SavedStateSegmentDescr [exports.memTableEntries];

    for (unsigned j = 0; j < exports.memTableEntries; j++)
    {
        memoryTableEntry *entry = &exports.memTable[j];
        memset(&descrs[j], 0, sizeof(SavedStateSegmentDescr));
        descrs[j].relocationSize = sizeof(RelocationEntry);
        descrs[j].segmentIndex = (unsigned)entry->mtIndex;
        descrs[j].segmentSize = entry->mtLength; // Set this even if we don't write it.
        descrs[j].originalAddress = entry->mtAddr;
        if (entry->mtFlags & MTF_WRITEABLE)
        {
            descrs[j].segmentFlags |= SSF_WRITABLE;
            if (entry->mtFlags & MTF_NO_OVERWRITE)
                descrs[j].segmentFlags |= SSF_NOOVERWRITE;
            if (j < permanentEntries && (entry->mtFlags & MTF_NO_OVERWRITE) == 0)
                descrs[j].segmentFlags |= SSF_OVERWRITE;
            if (entry->mtFlags & MTF_BYTES)
                descrs[j].segmentFlags |= SSF_BYTES;
        }
    }
    // Write out temporarily. Will be overwritten at the end.
    saveHeader.segmentDescr = ftell(exports.exportFile);
    fwrite(descrs, sizeof(SavedStateSegmentDescr), exports.memTableEntries, exports.exportFile);

    // Write out the relocations and the data.
    for (unsigned k = 1 /* Not IO area */; k < exports.memTableEntries; k++)
    {
        memoryTableEntry *entry = &exports.memTable[k];
        // Write out the contents if this is new or if it is a normal, overwritable
        // mutable area.
        if (k >= permanentEntries ||
            (entry->mtFlags & (MTF_WRITEABLE|MTF_NO_OVERWRITE)) == MTF_WRITEABLE)
        {
            descrs[k].relocations = ftell(exports.exportFile);
            // Have to write this out.
            exports.relocationCount = 0;
            // Create the relocation table.
            char *start = (char*)entry->mtAddr;
            char *end = start + entry->mtLength;
            for (PolyWord *p = (PolyWord*)start; p < (PolyWord*)end; )
            {
                p++;
                PolyObject *obj = (PolyObject*)p;
                POLYUNSIGNED length = obj->Length();
                // Most relocations can be computed when the saved state is
                // loaded so we only write out the difficult ones: those that
                // occur within compiled code.
                //  exports.relocateObject(obj);
                if (length != 0 && obj->IsCodeObject())
                    machineDependent->ScanConstantsWithinCode(obj, &exports);
                p += length;
            }
            descrs[k].relocationCount = exports.relocationCount;
            // Write out the data.
            descrs[k].segmentData = ftell(exports.exportFile);
            fwrite(entry->mtAddr, entry->mtLength, 1, exports.exportFile);
       }
    }

    // If this is a child we need to write a string table containing the parent name.
    if (newHierarchy > 1)
    {
        saveHeader.stringTable = ftell(exports.exportFile);
        _fputtc(0, exports.exportFile); // First byte of string table is zero
        _fputts(hierarchyTable[newHierarchy-2]->fileName, exports.exportFile);
        _fputtc(0, exports.exportFile); // A terminating null.
        saveHeader.stringTableSize = (_tcslen(hierarchyTable[newHierarchy-2]->fileName) + 2)*sizeof(TCHAR);
    }

    // Rewrite the header and the segment tables now they're complete.
    fseek(exports.exportFile, 0, SEEK_SET);
    fwrite(&saveHeader, sizeof(saveHeader), 1, exports.exportFile);
    fwrite(descrs, sizeof(SavedStateSegmentDescr), exports.memTableEntries, exports.exportFile);

    // Add an entry to the hierarchy table for this file.
    (void)AddHierarchyEntry(fileName, saveHeader.timeStamp);

    delete[](descrs);
}
Beispiel #25
0
void ProcessAddToVector::ProcessRoot(PolyObject *root)
{
    // Mark the initial object
    AddObjectsToDepthVectors(root);

    // Process the stack until it's empty.
    while (asp != 0)
    {
        // Pop it from the stack.
        PolyObject *obj = addStack[asp-1];

        if (obj->IsCodeObject())
        {
            /* There's a problem sharing code objects if they have relative calls/jumps
               in them to other code.  The code of two functions may be identical (e.g.
               they both call functions 100 bytes ahead) and so they will appear the
               same but if the functions they jump to are different they are actually
               different.  For that reason we don't share code segments.  DCJM 4/1/01 */
            asp--; // Pop it because we'll process it completely
            ScanAddressesInObject(obj);
            // If it's local set the depth with the value zero.
            if (obj->LengthWord() & _OBJ_GC_MARK)
            {
                obj->SetLengthWord(obj->LengthWord() & (~_OBJ_GC_MARK));
                m_parent->AddToVector(0, obj->LengthWord() & (~_OBJ_GC_MARK), obj);
                obj->SetLengthWord(OBJ_SET_DEPTH(0)); // Now scanned
            }
        }

        else if ((obj->LengthWord() & _OBJ_GC_MARK) && ! obj->IsMutable())
        {
            POLYUNSIGNED depth = 0;
            POLYUNSIGNED length = obj->Length();
            PolyWord *pt = (PolyWord*)obj;
            unsigned osp = asp;

            while (length != 0 && osp == asp)
            {
                POLYUNSIGNED d = AddObjectsToDepthVectors(*pt);
                if (d > depth) depth = d;
                pt++;
                length--;
            }

            if (osp == asp)
            {
                // We've finished it
                asp--; // Pop this item.
                depth++; // One more for this object
                obj->SetLengthWord(obj->LengthWord() & (~_OBJ_GC_MARK));
                m_parent->AddToVector(depth, obj->LengthWord() & (~_OBJ_GC_MARK), obj);
                obj->SetLengthWord(OBJ_SET_DEPTH(depth));
            }
        }

        else
        {
            POLYUNSIGNED length = obj->Length();
            PolyWord *pt = (PolyWord*)obj;
            unsigned osp = asp;

            while (length != 0)
            {
                if (! (*pt).IsTagged())
                {
                    // If we've already pushed an address break now
                    if (osp != asp) break;
                    // Process the address and possibly push it
                    AddObjectsToDepthVectors(*pt);
                }
                pt++;
                length--;
            }

            if (length == 0)
            {
                // We've finished it
                if (osp != asp)
                {
                    ASSERT(osp == asp-1);
                    addStack[osp-1] = addStack[osp];
                }
                asp--; // Pop this item.
                if (obj->LengthWord() & _OBJ_GC_MARK)
                    obj->SetLengthWord(OBJ_SET_DEPTH(0));
            }
        }
    }
}
Beispiel #26
0
// We use _OBJ_GC_MARK to detect when we have visited a cell but not yet
// computed the depth.  We have to be careful that this bit is removed
// before we finish in the case that we run out of memory and throw an
// exception.  PushToStack may throw the exception if the stack needs to
// grow.
POLYUNSIGNED ProcessAddToVector::AddObjectsToDepthVectors(PolyWord old)
{
    // If this is a tagged integer or an IO pointer that's simply a constant.
    if (old.IsTagged() || old == PolyWord::FromUnsigned(0))
        return 0;

    MemSpace *space = gMem.SpaceForAddress(old.AsAddress());
    if (space == 0 || space->spaceType == ST_IO)
        return 0;

    PolyObject *obj = old.AsObjPtr();
    POLYUNSIGNED L = obj->LengthWord();

    if (OBJ_IS_DEPTH(L)) // tombstone contains genuine depth or 0.
        return OBJ_GET_DEPTH(L);

    if (obj->LengthWord() & _OBJ_GC_MARK)
        return 0; // Marked but not yet scanned. Circular structure.

    ASSERT (OBJ_IS_LENGTH(L));

    if (obj->IsMutable())
    {
        // Mutable data in the local or permanent areas
        if (! obj->IsByteObject())
        {
            // Add it to the vector so we will update any addresses it contains.
            m_parent->AddToVector(0, L, old.AsObjPtr());
            // and follow any addresses to try to merge those.
            PushToStack(obj);
            obj->SetLengthWord(L | _OBJ_GC_MARK); // To prevent rescan
        }
        return 0; // Level is zero
    }

    if (space->spaceType == ST_PERMANENT &&
             ((PermanentMemSpace*)space)->hierarchy == 0)
    {
        // Immutable data in the permanent area can't be merged
        // because it's read only.  We need to follow the addresses
        // because they may point to mutable areas containing data
        // that can be.  A typical case is the root function pointing
        // at the global name table containing new declarations.
        Bitmap *bm = &((PermanentMemSpace*)space)->shareBitmap;
        if (! bm->TestBit((PolyWord*)obj - space->bottom))
        {
            bm->SetBit((PolyWord*)obj - space->bottom);
            if (! obj->IsByteObject())
                PushToStack(obj);
        }
        return 0;
    }

    /* There's a problem sharing code objects if they have relative calls/jumps
       in them to other code.  The code of two functions may be identical (e.g.
       they both call functions 100 bytes ahead) and so they will appear the
       same but if the functions they jump to are different they are actually
       different.  For that reason we don't share code segments.  DCJM 4/1/01 */
    if (obj->IsCodeObject())
    {
        // We want to update addresses in the code segment.
        m_parent->AddToVector(0, L, old.AsObjPtr());
        PushToStack(obj);
        obj->SetLengthWord(L | _OBJ_GC_MARK); // To prevent rescan

        return 0;
    }

    // Byte objects always have depth 1 and can't contain addresses.
    if (obj->IsByteObject())
    {
        m_parent->AddToVector (1, L, old.AsObjPtr());// add to vector at correct depth
        obj->SetLengthWord(OBJ_SET_DEPTH(1));
        return 1;
    }

    ASSERT(OBJ_IS_WORD_OBJECT(L)); // That leaves immutable data objects.
    PushToStack(obj);
    obj->SetLengthWord(L | _OBJ_GC_MARK); // To prevent rescan

    return 0;
}
Beispiel #27
0
// Merge cells with the same contents.
POLYUNSIGNED DepthVector::MergeSameItems()
{
    DepthVector *v = this;

    POLYUNSIGNED  N = v->nitems;
    Item *itemVec = v->vector;
    POLYUNSIGNED  n = 0;
    POLYUNSIGNED  i = 0;

    while (i < N)
    {
        PolyObject *bestShare = 0; // Candidate to share.
        MemSpace *bestSpace = 0;

        POLYUNSIGNED j;
        for (j = i; j < N; j++)
        {
            ASSERT (OBJ_IS_DEPTH(itemVec[i].pt->LengthWord()));
            // Search for identical objects.  Don't bother to compare it with itself.
            if (i != j && CompareItems (& itemVec[i], & itemVec[j]) != 0) break;
            // The order of sharing is significant.
            // Choose an object in the permanent memory if that is available.
            // This is necessary to retain the invariant that no object in
            // the permanent memory points to an object in the temporary heap.
            // (There may well be pointers to this object elsewhere in the permanent
            // heap).
            // Choose the lowest hierarchy value for preference since that
            // may reduce the size of saved state when resaving already saved
            // data.
            // If we can't find a permanent space choose a space that isn't
            // an allocation space.  Otherwise we could break the invariant
            // that immutable areas never point into the allocation area.
            MemSpace *space = gMem.SpaceForAddress(itemVec[j].pt);
            if (bestSpace == 0)
            {
                bestShare = itemVec[j].pt;
                bestSpace = space;
            }
            else if (bestSpace->spaceType == ST_PERMANENT)
            {
                // Only update if the current space is also permanent and a lower hierarchy
                if (space->spaceType == ST_PERMANENT &&
                        ((PermanentMemSpace *)space)->hierarchy < ((PermanentMemSpace *)bestSpace)->hierarchy)
                {
                    bestShare = itemVec[j].pt;
                    bestSpace = space;
                }
            }
            else if (bestSpace->spaceType == ST_LOCAL)
            {
                // Update if the current space is not an allocation space
                if (space->spaceType != ST_LOCAL || ! ((LocalMemSpace*)space)->allocationSpace)
                {
                    bestShare = itemVec[j].pt;
                    bestSpace = space;
                }
            }
        }
        POLYUNSIGNED k = j; // Remember the first object that didn't match.
        //.For each identical object set all but the one we want to point to
        // the shared object.
        for (j = i; j < k; j++)
        {
            ASSERT (OBJ_IS_DEPTH(itemVec[j].pt->LengthWord()));
            if (itemVec[j].pt == bestShare)
            {
                // This is the common object.
                bestShare->SetLengthWord(itemVec[j].L); // restore genuine length word
                ASSERT (OBJ_IS_LENGTH(bestShare->LengthWord()));
            }
            else
            {
                itemVec[j].pt->SetForwardingPtr(bestShare); /* an indirection */
                ASSERT (itemVec[j].pt->ContainsForwardingPtr());
                n++;
            }
        }
        ASSERT(! OBJ_IS_DEPTH(itemVec[i].pt->LengthWord()));
        i = k;
    }

    return n;
}
IGeometryChecker::ReturnVal OverlappedUVWFacesChecker::GeometryCheck(TimeValue t,INode *nodeToCheck, IGeometryChecker::OutputVal &val)
{
	val.mIndex.ZeroCount();
	if(IsSupported(nodeToCheck))
	{
		UVWChannel uvmesh;
		ObjectState os  = nodeToCheck->EvalWorldState(t);
		Object *obj = os.obj;
		if(os.obj->IsSubClassOf(triObjectClassID))
		{
			TriObject *tri = dynamic_cast<TriObject *>(os.obj);
			if(tri)
			{
				BitArray arrayOfFaces;
				arrayOfFaces.SetSize(tri->mesh.numFaces);
				arrayOfFaces.ClearAll();
				IGeometryChecker::ReturnVal returnval=IGeometryChecker::eFail;
				int numChannels = tri->mesh.getNumMaps();
				for(int i=0;i<numChannels;++i)
				{
					if(tri->mesh.mapSupport(i))
					{
						uvmesh.DeleteData();
						uvmesh.SetWithMesh(&tri->mesh,i);
						if(uvmesh.GetNumOfFaces()>1)
						{
							//okay now run it with that uvmesh
							returnval = GeometryCheckWithUVMesh(uvmesh,t, nodeToCheck,arrayOfFaces);
							if(returnval ==IGeometryChecker::eFail)
								return returnval;
						}
					}
				}
				if(arrayOfFaces.IsEmpty()==false) //we have overlapping faces
				{
					int localsize= arrayOfFaces.GetSize();
					for(int i=0;i<localsize;++i)
					{
						if(arrayOfFaces[i])
							val.mIndex.Append(1,&i);
					}
				}
				return returnval;
			}
			else return IGeometryChecker::eFail;
		}
		else if(os.obj->IsSubClassOf(polyObjectClassID))
		{
			PolyObject *poly = dynamic_cast<PolyObject *>(os.obj);
			if(poly)
			{
				BitArray arrayOfFaces;
				arrayOfFaces.SetSize(poly->GetMesh().numf);
				arrayOfFaces.ClearAll();
				IGeometryChecker::ReturnVal returnval=IGeometryChecker::eFail;
				int numChannels= poly->GetMesh().MNum();//do this!
				for(int i=0;i<numChannels;++i)
				{
					if(poly->GetMesh().M(i))
					{
						uvmesh.DeleteData();
						uvmesh.SetWithMesh(&poly->GetMesh(),i);
						if(uvmesh.GetNumOfFaces()>1)
						{
							//okay now run it with that uvmesh
							returnval = GeometryCheckWithUVMesh(uvmesh,t, nodeToCheck,arrayOfFaces);
							if(returnval ==IGeometryChecker::eFail)
								return returnval;
						}
					}
				}
				if(arrayOfFaces.IsEmpty()==false) //we have overlapping faces
				{
					int localsize= arrayOfFaces.GetSize();
					for(int i=0;i<localsize;++i)
					{
						if(arrayOfFaces[i])
							val.mIndex.Append(1,&i);
					}
				}
				return returnval;
			}
			else return IGeometryChecker::eFail;
		}
		
	}
	return IGeometryChecker::eFail;
}
Beispiel #29
0
// This is called via ScanAddressesInRegion to process the permanent mutables.  It is
// also called from ScanObjectAddress to process root addresses.
// It processes all the addresses reachable from the object.
void RecursiveScan::ScanAddressesInObject(PolyObject *obj, POLYUNSIGNED lengthWord)
{
    if (OBJ_IS_BYTE_OBJECT(lengthWord))
    {
        Completed(obj);
        return;
    }

    while (true)
    {
        ASSERT (OBJ_IS_LENGTH(lengthWord));

        // Get the length and base address.  N.B.  If this is a code segment
        // these will be side-effected by GetConstSegmentForCode.
        POLYUNSIGNED length = OBJ_OBJECT_LENGTH(lengthWord);
        PolyWord *baseAddr = (PolyWord*)obj;

        if (OBJ_IS_CODE_OBJECT(lengthWord))
        {
            // It's better to process the whole code object in one go.
            ScanAddress::ScanAddressesInObject(obj, lengthWord);
            length = 0; // Finished
        }
        ASSERT(! OBJ_IS_BYTE_OBJECT(lengthWord)); // Check - remove this later

        // else it's a normal object,

        // If there are only two addresses in this cell that need to be
        // followed we follow them immediately and treat this cell as done.
        // If there are more than two we push the address of this cell on
        // the stack, follow the first address and then rescan it.  That way
        // list cells are processed once only but we don't overflow the
        // stack by pushing all the addresses in a very large vector.
        PolyWord *endWord = baseAddr + length;
        PolyObject *firstWord = 0;
        PolyObject *secondWord = 0;

        while (baseAddr != endWord)
        {
            PolyWord wordAt = *baseAddr;

            if (wordAt.IsDataPtr() && wordAt != PolyWord::FromUnsigned(0))
            {
                // Normal address.  We can have words of all zeros at least in the
                // situation where we have a partially constructed code segment where
                // the constants at the end of the code have not yet been filled in.
                if (TestForScan(baseAddr)) // Test value at baseAddr (may side-effect it)
                {
                    PolyObject *wObj = (*baseAddr).AsObjPtr();
                    if (wObj->IsByteObject())
                    {
                        // Can do this now - don't need to push it
                        MarkAsScanning(wObj);
                        Completed(wObj);
                    }
                    else if (firstWord == 0)
                    {
                        firstWord = wObj;
                        // We mark the word immediately.  We can have
                        // two words in an object that are the same
                        // and we don't want to process it again.
                        MarkAsScanning(firstWord);
                    }
                    else if (secondWord == 0)
                        secondWord = wObj;
                    else break;  // More than two words.
                }
            }
            else if (wordAt.IsCodePtr())
            {
                // If we're processing the constant area of a code segment this could
                // be a code address.
                PolyObject *oldObject = ObjCodePtrToPtr(wordAt.AsCodePtr());
                // Calculate the byte offset of this value within the code object.
                POLYUNSIGNED offset = wordAt.AsCodePtr() - (byte*)oldObject;
                wordAt = oldObject;
                bool test = TestForScan(&wordAt);
                // TestForScan may side-effect the word.
                PolyObject *newObject = wordAt.AsObjPtr();
                wordAt = PolyWord::FromCodePtr((byte*)newObject + offset);
                if (wordAt != *baseAddr)
                    *baseAddr = wordAt;
                if (test)
                {
                    if (firstWord == 0)
                    {
                        firstWord = newObject;
                        MarkAsScanning(firstWord);
                    }
                    else if (secondWord == 0)
                        secondWord = newObject;
                    else break;
                }
            }
            baseAddr++;
        }

        if (baseAddr == endWord)
        {
            // We have done everything except possibly firstWord and secondWord.
            Completed(obj);
            if (secondWord != 0)
            {
                MarkAsScanning(secondWord);
                // Put this on the stack.  If this is a list node we will be
                // pushing the tail.
                PushToStack(secondWord);
            }
        }
        else // Put this back on the stack while we process the first word
            PushToStack(obj);

        if (firstWord != 0)
            // Process it immediately.
            obj = firstWord;
        else if (StackIsEmpty())
            return;
        else
            obj = PopFromStack();

        lengthWord = obj->LengthWord();
    }
}
Beispiel #30
0
// This is called via ScanAddressesInRegion to process the permanent mutables.  It is
// also called from ScanObjectAddress to process root addresses.
// It processes all the addresses reachable from the object.
void MTGCProcessMarkPointers::ScanAddressesInObject(PolyObject *obj, POLYUNSIGNED lengthWord)
{
    if (OBJ_IS_BYTE_OBJECT(lengthWord))
        return;

    while (true)
    {
        ASSERT (OBJ_IS_LENGTH(lengthWord));

        // Get the length and base address.  N.B.  If this is a code segment
        // these will be side-effected by GetConstSegmentForCode.
        POLYUNSIGNED length = OBJ_OBJECT_LENGTH(lengthWord);

        if (OBJ_IS_WEAKREF_OBJECT(lengthWord))
        {
            // Special case.  
            ASSERT(OBJ_IS_MUTABLE_OBJECT(lengthWord)); // Should be a mutable.
            ASSERT(OBJ_IS_WORD_OBJECT(lengthWord)); // Should be a plain object.
            // We need to mark the "SOME" values in this object but we don't mark
            // the references contained within the "SOME".
            PolyWord *baseAddr = (PolyWord*)obj;
            // Mark every word but ignore the result.
            for (POLYUNSIGNED i = 0; i < length; i++)
                (void)MarkAndTestForScan(baseAddr+i);
            // We've finished with this.
            length = 0;
        }

        else if (OBJ_IS_CODE_OBJECT(lengthWord))
        {
            // It's better to process the whole code object in one go.
            ScanAddress::ScanAddressesInObject(obj, lengthWord);
            length = 0; // Finished
        }

        // else it's a normal object,

        // If there are only two addresses in this cell that need to be
        // followed we follow them immediately and treat this cell as done.
        // If there are more than two we push the address of this cell on
        // the stack, follow the first address and then rescan it.  That way
        // list cells are processed once only but we don't overflow the
        // stack by pushing all the addresses in a very large vector.
        PolyWord *baseAddr = (PolyWord*)obj;
        PolyWord *endWord = baseAddr + length;
        PolyObject *firstWord = 0;
        PolyObject *secondWord = 0;
        PolyWord *restartAddr = 0;

        if (obj == largeObjectCache[locPtr].base)
        {
            baseAddr = largeObjectCache[locPtr].current;
            ASSERT(baseAddr > (PolyWord*)obj && baseAddr < ((PolyWord*)obj)+length);
            if (locPtr == 0) locPtr = LARGECACHE_SIZE-1; else locPtr--;
        }

        while (baseAddr != endWord)
        {
            PolyWord wordAt = *baseAddr;

            if (wordAt.IsDataPtr() && wordAt != PolyWord::FromUnsigned(0))
            {
                // Normal address.  We can have words of all zeros at least in the
                // situation where we have a partially constructed code segment where
                // the constants at the end of the code have not yet been filled in.
                if (TestForScan(baseAddr))
                {
                    if (firstWord == 0)
                        firstWord = baseAddr->AsObjPtr();
                    else if (secondWord == 0)
                    {
                        // If we need to rescan because there are three or more words to do
                        // this is the place we need to restart (or the start of the cell if it's
                        // small).
                        restartAddr = baseAddr;
                        secondWord = baseAddr->AsObjPtr();
                    }
                    else break;  // More than two words.
                }
            }
            else if (wordAt.IsCodePtr())
            {
                // If we're processing the constant area of a code segment this could
                // be a code address.
                // Check that this is actually an address.  If we have had a bad pointer
                // earlier we may treat some length fields as values.
                ASSERT(gMem.SpaceForAddress(wordAt.AsCodePtr()) != 0);
                PolyObject *oldObject = ObjCodePtrToPtr(wordAt.AsCodePtr());
                // Calculate the byte offset of this value within the code object.
                POLYUNSIGNED offset = wordAt.AsCodePtr() - (byte*)oldObject;
                wordAt = oldObject;
                bool test = TestForScan(&wordAt);
                // If we've changed it because we had a left-over forwarding pointer
                // we need to update the original.
                PolyObject *newObject = wordAt.AsObjPtr();
                wordAt = PolyWord::FromCodePtr((byte*)newObject + offset);
                if (wordAt != *baseAddr)
                    *baseAddr = wordAt;
                if (test)
                {
                    if (firstWord == 0)
                        firstWord = newObject;
                    else if (secondWord == 0)
                    {
                        restartAddr = baseAddr;
                        secondWord = newObject;
                    }
                    else break;
                }
            }
            baseAddr++;
        }

        if (baseAddr != endWord)
            // Put this back on the stack while we process the first word
            PushToStack(obj, length < largeObjectSize ? 0 : restartAddr, length);
        else if (secondWord != 0)
        {
            // Mark it now because we will process it.
            secondWord->SetLengthWord(secondWord->LengthWord() | _OBJ_GC_MARK);
            // Put this on the stack.  If this is a list node we will be
            // pushing the tail.
            PushToStack(secondWord);
        }

        if (firstWord != 0)
        {
            // Mark it and process it immediately.
            firstWord->SetLengthWord(firstWord->LengthWord() | _OBJ_GC_MARK);
            obj = firstWord;
        }
        else if (msp == 0)
        {
            markStack[msp] = 0; // Really finished
            return;
        }
        else
        {
            // Clear the item above the top.  This really is finished.
            if (msp < MARK_STACK_SIZE) markStack[msp] = 0;
            // Pop the item from the stack but don't overwrite it yet.
            // This allows another thread to steal it if there really
            // is nothing else to do.  This is only really important
            // for large objects.
            obj = markStack[--msp]; // Pop something.
        }

        lengthWord = obj->LengthWord();
    }
}