I’m rendering 2D sprites with DX9 using a vertex buffer (DrawPrimitive).
some animations got different texture files with different sizes.
Now I get the following problem: on the very single frame where I’m switching between two animations that have different texture files (for example when start walking or when finish walking and begin standing), it’s rendering with the coordinates for the new texture, but still presenting the old texture. This looks as if I did’nt set the texture for the new animation. only problem is – I did.
How I understood what’s the problem: I took a screenshot right on that frame, and noticed that it renders one texture with coordinates fitting to the other texture.
In my rendering function I first get the new texture and send it to DX, than calculating the coords, and in the end I render my vertices with the coords and the new texture set. I checked and debugged it million times and all the values are correct, and yet the bug happens.
any ideas why this might happen?
thanks!
Edit: added some code:
// Render a quad using the vertex buffer
void CGraphicsManager::RenderQuadViaVertexBuffer(const SVertex* pVertices) const
{
// Increase renders count
this->m_RenderCount++;
// vb_vertices now points to our vertices inside the Vertex buffer, so
// to fill in our VB, we copy to vb_vertices.
memcpy(this->m_pVertexBufferBuffPtr + this->m_OffsetInVertexBuffer, pVertices, sizeof(SVertex) * (VERTICES_IN_QUAD));
// Render the rectanlge using the vertices we got.
this->m_pD3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, this->m_OffsetInVertexBuffer, PRIMITIVES_IN_QUAD);
// Increment the offset in the vertex buffer
this->m_OffsetInVertexBuffer += VERTICES_IN_QUAD;
}
// Render a quad
void CGraphicsManager::Render(const STexture& p_Texture, SLocation p_RenderLocation, SSize p_RenderSize, const SQuad& p_TextureQuad, SfColor p_RenderColor, ERenderEffects p_RenderEffect) const
{
// Set render effect
this->SetRenderEffect(p_RenderEffect);
// Set texture
this->SetTexture(p_Texture);
// Set the vertex needed for the rendering
VerticesForQuadRender[0].Position.x = p_RenderLocation.x;
VerticesForQuadRender[0].Position.y = p_RenderLocation.y;
VerticesForQuadRender[0].Position.z = 0.0f;
VerticesForQuadRender[0].color = p_RenderColor;
VerticesForQuadRender[0].tv = p_TextureQuad.left;
VerticesForQuadRender[0].tu = p_TextureQuad.top;
VerticesForQuadRender[1].Position.x = p_RenderLocation.x + p_RenderSize.x;
VerticesForQuadRender[1].Position.y = p_RenderLocation.y;
VerticesForQuadRender[1].Position.z = 0.0f;
VerticesForQuadRender[1].color = p_RenderColor;
VerticesForQuadRender[1].tv = p_TextureQuad.right;
VerticesForQuadRender[1].tu = p_TextureQuad.top;
VerticesForQuadRender[2].Position.x = p_RenderLocation.x;
VerticesForQuadRender[2].Position.y = p_RenderLocation.y + p_RenderSize.y;
VerticesForQuadRender[2].Position.z = 0.0f;
VerticesForQuadRender[2].color = p_RenderColor;
VerticesForQuadRender[2].tv = p_TextureQuad.left;
VerticesForQuadRender[2].tu = p_TextureQuad.bottom;
VerticesForQuadRender[3].Position.x = p_RenderLocation.x + p_RenderSize.x;
VerticesForQuadRender[3].Position.y = p_RenderLocation.y + p_RenderSize.y;
VerticesForQuadRender[3].Position.z = 0.0f;
VerticesForQuadRender[3].color = p_RenderColor;
VerticesForQuadRender[3].tv = p_TextureQuad.right;
VerticesForQuadRender[3].tu = p_TextureQuad.bottom;
this->RenderQuadViaVertexBuffer(VerticesForQuadRender);
}
// Starts a rendering frame
bool CGraphicsManager::StartFrame()
{
// Clear texture
this->ClearTexture();
// Zero renders count
this->m_RenderCount = 0;
// Clear screen
if (!this->ClearScreen())
{
this->ResetDevice();
return false;
}
// Begin new rendering scene
if (FAILED(this->m_pD3dDevice->BeginScene()))
{
this->ResetDevice();
return false;
}
// Set render from our vertex buffer
this->BeginRenderFromVertexBuffer();
return true;
}
// Finish rendering
bool CGraphicsManager::EndFrame()
{
// Unlock vertex buffer
this->m_pVertexBuffer->Unlock();
// Notify the device that we're finished rendering for this frame
if (FAILED(this->m_pD3dDevice->EndScene()))
{
this->ResetDevice();
return false;
}
// Present scene
if(FAILED(this->m_pD3dDevice->Present(NULL, //Source rectangle to display, NULL for all of it
NULL, //Destination rectangle, NULL to fill whole display
NULL, //Target window, if NULL uses device window set in CreateDevice
NULL ))) //Dirty Region, set it to NULL
{
this->ResetDevice();
return false;
}
// Finish rendering
return true;
}
// This function must be called before rendering textured surfaces
void CGraphicsManager::BeginRenderFromVertexBuffer() const
{
// Lock the vertex buffer (unlock on release) and get the pointer to the begining of the buffer
HRESULT hr = this->m_pVertexBuffer->Lock
(0, // Offset, we want to start at the beginning
0, //SizeToLock, 0 means lock the whole thing
(void**)&this->m_pVertexBufferBuffPtr, //If successful, this will point to the data in the vertex buffer
0);
ASSERT(SUCCEEDED(hr), "Failed to lock vertex buffer! (err: " << hr << ")");
// Set offset in vertex buffer back to 0
this->m_OffsetInVertexBuffer = 0;
}
Well I can see your problem you are copying data into an locked vertex buffer and then rendering that data. You can’t do this. The data in the locked vertex buffer is not actually valid yet. It only becomes valid once you unlock it. This is because the data you are filling in is in main memory and may actually be shadowing memory on the graphics card. When you unlock it copies the data across to the graphics card.
As such if you lock/unlock for each draw call everything works fine because you are forcing the copy back.
As you have found, though, this is far from optimal.
What you should be doing is calculating all your quad vertex positions in one pass. Then you need to go through and render all the quads (Ideally without swapping texture/shader/etc every call).
One way to do this would be to fill your vertex buffer as you are doing and then store the draw commands in a vertex buffer. This would require you storing things like texture, effect used etc. You can then run through the list and render everything from the vertex buffer you have already filled.
If you then sort things by texture and shader constants (to minimise state changes) you will find your performance is many many times better. Although you will need to mark your vertex buffer as “dynamic” and its advisable to use the D3DLOCK_DISCARD flag when you lock the vertex buffer as this will discard the data currently in the vertex buffer (Basically it will return you a totally new block of memory to fill and won’t copy anything from the card) instead of blocking and waiting for the GPU to finish using it.
I hope thats some help!