diff --git a/MUNGA_L4/L4PARTICLES.cpp b/MUNGA_L4/L4PARTICLES.cpp index 234905d..dc22eec 100644 --- a/MUNGA_L4/L4PARTICLES.cpp +++ b/MUNGA_L4/L4PARTICLES.cpp @@ -249,14 +249,51 @@ void ParticleEmitter::Execute() } } +// +// Drop everything bound to the device we were last given. +// +// Null-safe, and it clears what it drops. Neither was true before: this +// runs on the device-lost path ahead of a Reset, where a texture that +// never loaded (a missing VIDEO\particles.png is enough) left one of +// these NULL and took the Reset down with it, and a released pointer +// left in place is a dangling one the moment anything looks again. +// void ParticleEngine::Destroy() { - mVertBuffer->Release(); - mParticleTexture->Release(); + if (mVertBuffer != NULL) + { + mVertBuffer->Release(); + mVertBuffer = NULL; + } + if (mParticleTexture != NULL) + { + mParticleTexture->Release(); + mParticleTexture = NULL; + } + // + // The paint paths test this before touching anything, so clearing it + // makes the gap between a Destroy and the next Initialize safe. + // + mDevice = NULL; } void ParticleEngine::Initialize(LPDIRECT3DDEVICE9 device) { + // + // Whatever is still held belongs to the PREVIOUS device, and holding + // it kept that device alive. A fresh renderer is built per mission, + // so a new device used to arrive here while the old one's vertex + // buffer (D3DPOOL_DEFAULT) and texture still referenced it - + // ~DPLRenderer's release never reached zero and the whole device + // survived the race that made it, back buffer and depth buffer and + // all. That is one leaked render target per race. + // + // The device-lost path already released before re-initialising; this + // is the same contract for the case where the device is not lost but + // replaced. + // + Destroy(); + mDevice = device; memset(mInstalledEffects, 0, sizeof(mInstalledEffects)); diff --git a/MUNGA_L4/L4VIDEO.cpp b/MUNGA_L4/L4VIDEO.cpp index 03aee1a..c340e9d 100644 --- a/MUNGA_L4/L4VIDEO.cpp +++ b/MUNGA_L4/L4VIDEO.cpp @@ -3605,6 +3605,16 @@ DPLRenderer::~DPLRenderer() // the next race of the single-binary loop - drop them with the device d3d_OBJECT::FlushTextureCache(); + // + // The particle engine is one of those caches and was missed. Its + // vertex buffer is D3DPOOL_DEFAULT and its texture belongs to this + // device, so while they were held the release below never reached + // zero: every race left a whole live device behind it, and the next + // race's Initialize was the only thing that ever let one go. Drop + // them here and the device dies with the mission that made it. + // + ParticleEngine::Destroy(); + SAFE_RELEASE(mDevice); SAFE_RELEASE(gD3D); //STUBBED: DPL RB 1/14/07