2012-01-19 9 views
5

OK, quindi attualmente sto lavorando a un gioco e ho riscontrato un problema di memoria dopo aver rifatto il codice oggi.C++, perché ricevo violazioni di accesso dopo aver modificato gli oggetti appena assegnati?

Utilizza un progetto basato sui componenti e stavo modificando il modo in cui i componenti sono stati assegnati e distribuiti alle entità. Originariamente alcuni componenti venivano allocati come variabili membro all'interno delle entità, ma ora voglio averli allocati altrove e passati all'entità tramite puntatore.

È possibile vedere come ho implementato questo di seguito con un codice di esempio dal mio progetto. In sostanza, faccio scorrere tutte le entità e allocare i componenti per loro. Il problema è che sto colpendo una violazione di accesso alla prima riga di "avvio" di "instanceObject" sulla sesta iterazione e non ho idea del perché. Usando il debugger, non sembra che nessuna delle variabili punti ad un indirizzo non valido.

Ecco cosa sto facendo per creare entità e componenti.

for (unsigned int i = 0; i < 512; ++i) { 
    InstanceObject* _pInstanceObject = new InstanceObject; 

    // Initialize temporary variables 
    XMFLOAT3 _position, _rotation; 
    float _angle = (i/512.0f) * (2.0f * XM_PI), 
      _scale = (float)pResourceManager->numberGenerator.GetInt(50, 5); 

    _position.x = 100000.0f * cos(_angle) + pResourceManager->numberGenerator.GetInt(50000, -25000); 
    _position.y =(float) pResourceManager->numberGenerator.GetInt(50000, -25000); 
    _position.z = 100000.0f * sin(_angle) + pResourceManager->numberGenerator.GetInt(50000, -25000); 

    _rotation.x = (XM_PI * 2) * (pResourceManager->numberGenerator.GetInt(100, 0)/100.0f); 
    _rotation.y = (XM_PI * 2) * (pResourceManager->numberGenerator.GetInt(100, 0)/100.0f); 
    _rotation.z = (XM_PI * 2) * (pResourceManager->numberGenerator.GetInt(100, 0)/100.0f); 

    // Set component's state using the temporary variables. 
    _pInstanceObject->StartUp(&_position, 
     &_rotation, 
     &XMFLOAT3(_scale, _scale, _scale), 
     &XMFLOAT3(0.0f, 0.0f, 1.0f), 
     &XMFLOAT3(1.0f, 0.0f, 0.0f), 
     &XMFLOAT3(0.0f, 1.0f, 0.0f) 
     ); 

    // Hand pointer of the component to entity. 
    // Entity will handle deallocating component 
} 

E qui è il codice pertinente dal componente.

class InstanceObject { 
private: 
    XMVECTOR anteriorAxis, 
     lateralAxis, 
     normalAxis, 
     position, 
     rotationQuaternion, 
     scale; 

    XMMATRIX translationMatrix, 
     rotationMatrix, 
     scaleMatrix; 
    void SetAnteriorAxis(const XMFLOAT3 *_anteriorAxis) { anteriorAxis = XMLoadFloat3(_anteriorAxis); } 
    void SetLateralAxis(const XMFLOAT3 *_lateralAxis) { lateralAxis = XMLoadFloat3(_lateralAxis); } 
    void SetNormalAxis(const XMFLOAT3 *_normalAxis)  { normalAxis = XMLoadFloat3(_normalAxis); } 
public: 
    InstanceObject(void) { } 
    InstanceObject(const InstanceObject& _object) : anteriorAxis(_object.anteriorAxis), lateralAxis(_object.lateralAxis), 
     normalAxis(_object.normalAxis), position(_object.position), rotationQuaternion(_object.rotationQuaternion), scale(_object.scale), 
     translationMatrix(_object.translationMatrix), rotationMatrix(_object.rotationMatrix), scaleMatrix(_object.scaleMatrix) {} 
    ~InstanceObject(void) { } 

    bool StartUp(const XMFLOAT3 *_position, const XMFLOAT3 *_rotation, const XMFLOAT3 *_scale, 
     const XMFLOAT3 *_lookAxis, const XMFLOAT3 *_strafeAxis, const XMFLOAT3 *_upAxis); 

    void SetPosition(const XMFLOAT3* _position) { position = XMLoadFloat3(_position); } 
    void SetRotationQuaternion(const XMFLOAT3 *_rotation) { rotationQuaternion = XMQuaternionRotationRollPitchYaw(_rotation->x, _rotation->y, _rotation->z); } 
    void SetScale(const XMFLOAT3 *_scale) { scale = XMLoadFloat3(_scale); } 
} 

bool InstanceObject::StartUp(const XMFLOAT3 *_position, const XMFLOAT3 *_rotation, const XMFLOAT3 *_scale, 
     const XMFLOAT3 *_lookAxis, const XMFLOAT3 *_strafeAxis, const XMFLOAT3 *_upAxis) { 
    SetPosition(_position); 
    SetRotationQuaternion(_rotation); 
    SetScale(_scale); 
    SetAnteriorAxis(_lookAxis); 
    SetLateralAxis(_strafeAxis); 
    SetNormalAxis(_upAxis); 

    return true; 
} 

Qualche idea di cosa potrebbe causare questo comportamento e come dovrei risolverlo?

+1

C'è una cosa che non è nel codice che potrebbe sicuramente causare problemi, il costruttore di XMLoadFloat3 (o è una funzione?) Che accetta un XMFLOAT3 * (non memorizza quel puntatore, vero?) –

+1

La copia costruttore di 'InstanceObject' è proprio come quello che verrebbe generato gratuitamente dal compilatore. Potresti semplicemente cancellarlo e avresti lo stesso comportamento. –

+0

Non vedo perché lo farebbe. Carica una serie di 3 float in un XMVECTOR che è un tipo di dati speciale che utilizza un registro SIMD o qualcosa del genere. (http://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.loading.xmloadfloat3%28v=vs.85%29.aspx) – KlashnikovKid

risposta

7

Credo che il problema è che il XMVECTOR nella tua classe InstanceObject deve essere di 16 byte allineati, e il nuovo operatore vinto' lo garantisco per te È possibile aggiungere un controllo rapido al codice per confermare: verificare se il puntatore IstanceObject & 0xF non è zero nell'iterazione in cui si blocca.

In questo caso è possibile scrivere un allocatore personalizzato che garantisca il corretto allineamento e utilizzi il nuovo posizionamento.

Sembra essere un problema abbastanza comune con XMVECTOR quando utilizzato come membro (qui c'è uno Connect bug report esistente su questo problema, ci sono alcune pagine Web se si esegue una ricerca).

Se si desidera solo per risolvere il problema rapidamente per farvi andare avanti con altre cose, è possibile aggiungere un operatore di statica new e delete alla dichiarazione di classe implementato qualcosa di simile al seguente frammento:

void* InstanceObject::operator new(size_t size) 
{ 
    // _aligned_malloc is a Microsoft specific method (include malloc.h) but its 
    // straightforward to implement if you want to be portable by over-allocating 
    // and adjusting the address 
    void *result = _aligned_malloc(size, 16); 
    if(result) 
     return result; 

    throw std::bad_alloc(); 
} 

void InstanceObject::operator delete(void* p) 
{ 
    if(p) _aligned_free(p); 
} 

Se il InstanceObject condividi una vita comune è possibile sostituire l'uso di _aligned_malloc con l'allocatore di arena allineato e rendere il delete un no-op per migliorare l'efficienza.

+0

'new' dovrebbe restituire la memoria correttamente allineata per l'oggetto, ma forse il compilatore non tratta i tipi di SSE correttamente rispetto a questo. Non sarebbe la prima volta. –

+0

Finora la risposta sembra corretta. L'iterazione "cattiva" non ottiene un puntatore allineato a 16 byte. Fare una pausa dal guardare tutto questo giorno e poi testare un allocatore personalizzato. Potrei semplicemente dichiarare le variabili membro XMVECTOR con la macro __declspec (align (16))? Non ho mai avuto bisogno o scritto un allocatore personalizzato prima. – KlashnikovKid

+1

Sì! Sei un risparmiatore di vita! Questo ha fatto il trucco. Grazie. – KlashnikovKid

0

Sembra che le strutture XMFLOAT3 siano allocate nello stack, non nello heap. Verrebbero ripuliti quando le loro variabili andrebbero fuori dal campo di applicazione.

Prova allocazione vostre strutture sul mucchio, invece:

XMFLOAT3* _position = new XMFLOAT3; 
+0

Non penso che questo farà la differenza. Gli XMFLOAT3 vengono immediatamente caricati in XMVECTOR. A meno che non ci sia un effetto collaterale funzionale di cui non sono a conoscenza con il caricamento di XMFLOATs. – KlashnikovKid

4

Stai prendendo i indirizzi di provvisori! Questo non è nemmeno permesso!

// Set component's state using the temporary variables. 
_pInstanceObject->StartUp(&_position, 
    &_rotation, 
    &XMFLOAT3(_scale, _scale, _scale), // not valid 
    &XMFLOAT3(0.0f, 0.0f, 1.0f), // not valid 
    &XMFLOAT3(1.0f, 0.0f, 0.0f), // not valid 
    &XMFLOAT3(0.0f, 1.0f, 0.0f) // not valid 
    ); 

suggerisco che si alza i livelli di attenzione alle impostazioni del compilatore e abilitare tanto conformità standard possibile. Le cose si semplificano molto quando il tuo compilatore ti dice cosa stai facendo male.

Come soluzione, si potrebbe provare semplicemente passando gli oggetti per valore:

_pInstanceObject->StartUp(_position, 
    _rotation, 
    XMFLOAT3(_scale, _scale, _scale), 
    XMFLOAT3(0.0f, 0.0f, 1.0f), 
    XMFLOAT3(1.0f, 0.0f, 0.0f), 
    XMFLOAT3(0.0f, 1.0f, 0.0f) 
    ); 


bool StartUp(XMFLOAT3 _position, XMFLOAT3 _rotation, XMFLOAT3 _scale, 
    XMFLOAT3 _lookAxis, XMFLOAT3 _strafeAxis, XMFLOAT3 _upAxis); 
+0

Non passare 'XMFLOAT3 const e' essere più efficiente? – lapk

+0

@AzzA: forse, forse no. Si noti che la funzione lo memorizzerà nella classe. Non oso prevedere come un compilatore non nascosto possa ottimizzare questo. –

+0

Sono abbastanza sicuro che questo non è il problema. Ho sempre usato questo approccio nel caricare XMVECTOR. Finché l'XMFLOAT3 è intatto nello stack quando viene caricato, va bene. Detto questo provo a passare di valore senza fortuna. – KlashnikovKid

Problemi correlati