PDA

View Full Version : Please help me find a bug (DX9 Direct3D)



Firlefanz
30-10-2005, 08:21 AM
Hello,

in my Game demo one of my Beta testers has a bug in a 3D sequence, the screens freezes, but in the background it continues.

I cannot reproduze this error myself, everything fine here.

I build in some error messages to find out where the errors come from.
They are coming from my 3D Particles. I included them from the SDK sample.

So here is the code how I render those particles (please not exceptionmsg is a function that writes the string into a textfile):



function CParticleSystem.Render(pd3dDevice: IDirect3DDevice9;nsize: single): HResult;
var
p_Particle: P3DParticle;
pVertices: PPointVertex;
dwNumParticlesToRender: DWord;
Flags: DWord;
vPos, vVel: TD3DXVector3;
fLengthSq: Single;
dwSteps: DWord;
clrDiffuse: TD3DXColor;
dwDiffuse: DWORD;
i: Integer;
begin
try
// Set the render states for using point sprites
pd3dDevice.SetRenderState(D3DRS_POINTSPRITEENABLE, iTRUE);
pd3dDevice.SetRenderState(D3DRS_POINTSCALEENABLE, iTRUE);
pd3dDevice.SetRenderState(D3DRS_POINTSIZE, FtoDW(nsize)); //GrA¬?AYe Partikel
pd3dDevice.SetRenderState(D3DRS_POINTSIZE_MIN, FtoDW(0.01));
pd3dDevice.SetRenderState(D3DRS_POINTSCALE_A, FtoDW(0.01));
pd3dDevice.SetRenderState(D3DRS_POINTSCALE_B, FtoDW(0.01));
pd3dDevice.SetRenderState(D3DRS_POINTSCALE_C, FtoDW(1.00));
// Set up the vertex buffer to be rendered
pd3dDevice.SetStreamSource(0, m_pVB, SizeOf(TPointVertex),SizeOf(TPointVertex)); //dx9 letzter Param size in byte
pd3dDevice.SetFVF(D3DFVF_POINTVERTEX);
if m_pParticles=nil then exit;
p_Particle := m_pParticles;
if p_particle=nil then exit;
dwNumParticlesToRender := 0;
// Lock the vertex buffer. We fill the vertex buffer in small
// chunks, using D3DLOCK_NOOVERWRITE. When we are done filling
// each chunk, we call DrawPrim, and lock the next chunk. When
// we run out of space in the vertex buffer, we start over at
// the beginning, using D3DLOCK_DISCARD.
Inc(m_dwBase, m_dwFlush);
if (m_dwBase >= m_dwDiscard) then m_dwBase:= 0;
if m_dwBase = 0 then Flags:= D3DLOCK_DISCARD else Flags:= D3DLOCK_NOOVERWRITE;
if m_pvb=nil then exit;
if failed(m_pVB.Lock(m_dwBase * SizeOf(TPointVertex), m_dwFlush * SizeOf(TPointVertex),
Pointer(pVertices), Flags)) then begin
commandoform.ExceptionMsg('ParticleLock');
exit;
end;
// Render each particle
while Assigned(p_Particle) do
begin
vPos:= p_Particle.m_vPos;
vVel:= p_Particle.m_vVel;
fLengthSq := D3DXVec3LengthSq(vVel);
if &#40;fLengthSq < 1.0&#41; then dwSteps &#58;= 2
else if &#40;fLengthSq < 4.00&#41; then dwSteps &#58;= 3
else if &#40;fLengthSq < 9.00&#41; then dwSteps &#58;= 4
else if &#40;fLengthSq < 12.25&#41; then dwSteps &#58;= 5
else if &#40;fLengthSq < 16.00&#41; then dwSteps &#58;= 6
else if &#40;fLengthSq < 20.25&#41; then dwSteps &#58;= 7
else dwSteps &#58;= 8;
D3DXVec3Scale&#40;vVel, vVel, -0.04 / dwSteps&#41;;
D3DXColorLerp&#40;clrDiffuse, p_Particle.m_clrFade, p_Particle.m_clrDiffuse, p_Particle.m_fFade&#41;;
dwDiffuse&#58;= D3DXColorToDWord&#40;clrDiffuse&#41;;
// Render each particle a bunch of times to get a blurring effect
for i&#58;= 0 to dwSteps - 1 do
begin
pVertices.v &#58;= vPos;
pVertices.color &#58;= dwDiffuse;
Inc&#40;pVertices&#41;;
Inc&#40;dwNumParticlesToRender&#41;;
if &#40;dwNumParticlesToRender = m_dwFlush&#41; then
begin
// Done filling this chunk of the vertex buffer. Lets unlock and
// draw this portion so we can begin filling the next chunk.
if failed&#40;m_pVB.Unlock&#41; then begin
commandoform.exceptionmsg&#40;'ParticleUnLock'&#41;;
exit;
end;
Result&#58;= pd3dDevice.DrawPrimitive&#40;D3DPT_POINTLIST, m_dwBase, dwNumParticlesToRender&#41;;
if FAILED&#40;Result&#41; then begin
commandoform.exceptionmsg&#40;'Rendererror DrawPrimitive'&#41;;
Exit;
end;
// Lock the next chunk of the vertex buffer. If we are at the
// end of the vertex buffer, DISCARD the vertex buffer and start
// at the beginning. Otherwise, specify NOOVERWRITE, so we can
// continue filling the VB while the previous chunk is drawing.
Inc&#40;m_dwBase, m_dwFlush&#41;;
if &#40;m_dwBase >= m_dwDiscard&#41; then m_dwBase&#58;= 0;
if m_dwBase = 0 then Flags&#58;= D3DLOCK_DISCARD else Flags&#58;= D3DLOCK_NOOVERWRITE;
if failed&#40;m_pVB.Lock&#40;m_dwBase * SizeOf&#40;TPointVertex&#41;, m_dwFlush * SizeOf&#40;TPointVertex&#41;,
Pointer&#40;pVertices&#41;, Flags&#41;&#41; then begin
commandoform.exceptionmsg&#40;'ParticleLock2'&#41;;
exit;
end;
dwNumParticlesToRender &#58;= 0;
end;
D3DXVec3Add&#40;vPos, vPos, vVel&#41;;
end;
if p_Particle.m_pNext=nil then exit;
p_Particle &#58;= p_Particle.m_pNext;
end;
// Unlock the vertex buffer
m_pVB.Unlock;
// Render any remaining particles
if &#40;dwNumParticlesToRender <> 0&#41; then
begin
Result&#58;= pd3dDevice.DrawPrimitive&#40;D3DPT_POINTLIST, m_dwBase, dwNumParticlesToRender&#41;;
if FAILED&#40;Result&#41; then Exit;
end;
// Reset render states
pd3dDevice.SetRenderState&#40;D3DRS_POINTSPRITEENABLE, iFALSE&#41;;
pd3dDevice.SetRenderState&#40;D3DRS_POINTSCALEENABLE, iFALSE&#41;;
Result &#58;= S_OK;
except
commandoform.exceptionmsg&#40;'Rendererror Particles'&#41;;
end;
end;


My betatester reports the error is Particlelock and then it freezes.

Here is the code I use to render the particles:



//fA¼r partikel setzen
setrenderstate&#40;d3drs_alphablendenable,dword&#40;true&#41;&#41; ;
setrenderstate&#40;d3drs_srcblend,d3dblend_srcalpha&#41;;
setrenderstate&#40;d3drs_destblend,d3dblend_destalpha&#41; ;
setmaterial&#40;m_pParticleMaterial&#41;;
try
if m_pParticleTexture<>nil then SetTexture&#40;0, m_pParticleTexture &#41;
else exceptionmsg&#40;'3D Particle Nil'&#41;;
except
exceptionmsg&#40;'3D Particle Error'&#41;;
end;
setTextureStageState&#40;0, D3DTSS_COLORARG1, D3DTA_Texture&#41;;
setTextureStageState&#40;0, D3DTSS_COLOROP, D3DTOP_selectarg1&#41;;
setTextureStageState&#40;1, D3DTSS_COLOROP, D3DTOP_disable&#41;;
m_pParticleSystem.Render&#40;XenScreen.Device,0.3&#41;;
setrenderstate&#40;d3drs_alphablendenable,dword&#40;false&#41; &#41;;
setrenderstate&#40;d3drs_specularenable,dword&#40;false&#41;&#41;;
//fA¼r skybox setzen
setTextureStageState&#40;0, D3DTSS_COLOROP, d3dtop_modulate&#41;;
setTextureStageState&#40;1, D3DTSS_COLOROP, d3dtop_modulate&#41;;
setRenderState&#40;D3DRS_NORMALIZENORMALS, 0&#41;;
setTextureStageState&#40;1, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU or 1&#41;;
setTextureStageState&#40;1, D3DTSS_COLOROP, D3DTOP_DISABLE&#41;;


So do you have any idea, how to fix that bug?

I have a htm file with the devicecaps of my testers graficx card here:

http://www.ericbehme.de/download/DeviceCaps.htm

Any idea how to fix that error or at least make the screen not freeze or what to do next?

Thanks a lot,
Firle

LP
30-10-2005, 03:09 PM
I don't see the problem right away, but is there any reason why are you using D3DLOCK_NOOVERWRITE and update vertex buffer slowly rather than using dynamic vertex buffer and fill it in a single shot?

Clootie
30-10-2005, 04:44 PM
Seems this:
pd3dDevice.SetStreamSource(0, m_pVB, SizeOf(TPointVertex),SizeOf(TPointVertex));
should be:
pd3dDevice.SetStreamSource(0, m_pVB, 0, SizeOf(TPointVertex));

So you rendering starts from 0 item, not 1st. Your code can generate AV (trying to read from other's process memory).

Have you looked at mine conversion of PointSprites from DirectX9 SDK (http://www.clootie.ru/delphi/download_dx90.html#D3Dex_PointSprites)? Link to near download all Direct3D 9.0 samples (summer 2003) for Delphi item (http://www.clootie.ru/delphi/download_dx90.html#Direct3D)

Firlefanz
30-10-2005, 04:44 PM
Hi Lifepower,

thanks a lot for replying. Could you perhaps give me a piece of code, like replace this with that sutff here....

I have that particles form an older sample, if you can tell me how to improve that this would be great, and perhaps it also fixes my hard-to-find bug?

Thanks,
Firle

Firlefanz
30-10-2005, 04:46 PM
Hi Clootie,

thanks a lot! I'll try that and take a look at your link, thank you :D
Seems we posted the same time. :wink:

Firle

Firlefanz
30-10-2005, 04:49 PM
Hi Clootie,

yes I guess it was an older version of that Pointsprite sample I used first, right! And I now see the error, you're absolutley right! :wink:

Maybe this is the bug, I'll change that and let my beta tester test it, thanks!

Firle

LP
30-10-2005, 05:07 PM
Hi Lifepower,

thanks a lot for replying. Could you perhaps give me a piece of code, like replace this with that sutff here....

I have that particles form an older sample, if you can tell me how to improve that this would be great, and perhaps it also fixes my hard-to-find bug?

Thanks,
Firle

Here's a *very quick* example:

type
TVertexCacheClass = class
private
Buffer: Pointer;
Index : Integer;

public
procedure AddVertex(const p: TPointVertex);
procedure FlushCache();

constructor Create();
destructor Destroy(); override;
end;


const
CacheSize = 200;

constructor TVertexCacheClass.Create();
begin
inherited;

Buffer:= AllocMem(CacheSize * SizeOf(TPointVertex));
end;

destructor TVertexCacheClass.Destroy();
begin
FreeMem(Buffer);
inherited;
end;

procedure TVertexCacheClass.AddVertex(const p: TPointVertex);
begin
if (Index >= CacheSize) then FlushCache();
PPointVertex(Integer(Buffer) + (Index * SizeOf(TPointVertex)))^:= p;
Inc(Index);
end;

procedure TVertexCacheClass.FlushCache();
begin
if (Index > 0) then
begin
pd3dDevice.DrawPrimitiveUP(D3DPT_POINTLIST, Index, Buffer, SizeOf(TPointVertex));
Index:= 0;
end;
end;

It doesn't explicitly use dynamic Vertex Buffers (Direct3D uses its internal dynamic vertex buffers for DrawPrimitiveUP), but it renders all your point sprites in one call (or few calls). Of course, you have to put all render states and everything to make it work. Creating and using your own vertex buffers might be *slightly* faster than using DrawPrimitiveUP, but it's rather under debate and depends on how many points you are actually drawing.

Clootie
30-10-2005, 05:19 PM
I wouldn't recommend using DPUP.
Because:
1) It's not recommended by NVIDIA and ATI (less tested, more prone to bugs, etc.).
2) There is too much work going on behind your code and before HW: if your data is small Direct3D runtime tries to embed it into command stream (data send to video-driver); if it's large D3D tries to draw your data with pre-created (by D3D runtime at startup) VB, but if your data size is not equal to size of D3D internal buffer -> more draw call's will reach video driver.

So, if you draw particle system of measurable size - DON'T use DPUP! :D

Firlefanz
30-10-2005, 06:13 PM
Hi!

@Lifepower: Thanks a lot for your sample! But if Clootie says this may cause other Bugs, I think I leave it the way it is now, I only have <100 Particles in count. And because...

...it works now!

@Clootie: Thanks a lot, this little bug caused my problems!

Thank you both very much for helping me out, good job!

Firle

LP
30-10-2005, 06:20 PM
Actually, I agree with Clootie. Although DrawPrimitiveUP might be useful for some GUI stuff, using dynamic VBs is a proven way :)

Clootie
30-10-2005, 08:10 PM
Firlefanz: Only "<100" particles? If they are small in screen space you could/should freely increase they number to at least 1000! 100 is not a REAL number any currently selling standalone / embedded videocard.

Lifepower: 8)

Firlefanz
31-10-2005, 08:22 AM
I just need some sparks coming out of my jumpgate, not much more.

Perhaps I'll later need some more for some other FX, right now I don't need much of them at a time. :wink:

Thanks,
Firle