Solved

# DirectX Question about vertices, buffers

Posted on 2012-04-11
618 Views
Experts,

I`m a DirectX newbie. I`m using DirectX 9 on C# .net VS2008 3.5.

Let`s say I have a Vertex List, with indices 0-99. I use a Vertex Buffer to store these. Now my application requires that after an event the new vertices are added to the same list. Lets say 100-199. So on.

What is the most efficient way to to display the vertices in stages?

For example Step 1. Display 0-99
Step 2. Calculate vertices 100-199 and then display 0-199
Step 3. Calculate vertices 200-299 and then display 0-299
So on.

Do I calculate all the vertices before hand and then display them in stages, or can I calculate the vertices in stages and then display them.

Also, the user should be able to use transformations (rotation, shifting) at any stage.

I`m looking for some guidance with this.
0
Question by:San24
[X]
###### Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

• Help others & share knowledge
• Earn cash & points
• 8
• 7

LVL 12

Expert Comment

ID: 37835611
There isn't one answer to this, it depends what you are doing.

It is better to draw as much as possible in one call.  However, 99 vertices or 199 vertices is plenty to draw in one go.  If I understand the question properly, it's not efficient to draw 0-99, the 0-199 then 0-299, because you're drawing the same vertices many times.  It better to draw each vertex once if you can.  Unless the list of vertices is growing for some reason?

When you say the user should be able to transform, in what sense do you mean?  Moving a camera around?  Moving a model?  Moving a subset of the vertices?

Any set of vertices you render uses the current render state (world transform, texture, shader etc.).  Any need to change to the render state, means you have to render a separate group of vertices.  This is why it is more efficient to group vertices according to render state (texture or shader).  So if vertices 0-99 use texture A and 100-199 use texture B (or transform B), you have to render them as separate batches.

That said, assuming you're using a dynamic vertex buffer you can calculate vertices 100-199 while rendering vertices 0-99 and this can make things faster in many cases.  The hardware can process vertices 0-99 in the same time it takes the CPU to calculate 100-199, so you exploit parallelism.

However, if you only drawing 300 vertices per frame it won't make much difference.  This is only a speed up if the vertices change each frame and there are more of them than you can fit in a vertex buffer.  If they are static then its much faster to just fill a vertex buffer with vertices and render that.

I realise this might be a bit confusing.  If something is not clear I'd be happy to explain more detail if you let me know what applies to this case.
0

Author Comment

ID: 37837522

I agree, it`s not efficient to draw 0-99, then 0-199, then 0-299. etc. The reason I was wondering is this - the user can apply transform at any point. Transform here is moving the model. My thinking was (still a newbie with Graphics). Lets say the user wants to rotate the model at 0-199 stage, doesn`t that mean I need to have the vertices 0-199 drawn?

I should have been clear in my initial post - the vertices 0-99 are the same as 100-199 or 200-299 .. each set is offset by an angle. Think of it as a pattern. So, I`ll be saving time on not calculating them again at each step except for the time to calculate new vertices with the offset.

Let me try explaining with a simple group of numbers (each number is a vertex)

Lets say I have a group of numbers : 5 - 10 - 15 Stage 1

Next I apply an offset of 2 to the group : 7 - 12 - 17 Stage 2

Now at this point I have [5 - 10 - 15] - [7 - 12 - 17] which I should draw, and allow the user to move the model.

Next I get  [5 - 10 - 15] - [7 - 12 - 17]  - [9 - 14 - 19] .etc ..

So, my question is do i add to the vertex buffer at every stage and redraw? What would be the most efficient way to do this. The number of vertices could be quite staggering, so efficiency is important too.

I hope that helps. Any help on this is immensely appreciated.
0

Author Comment

ID: 37838693
Okay, did a bit more of research and I think I`m headed in the correct direction. I still need help though.

So, What I`m looking to do is draw batches of geometry using Dynamic Vertex Buffers.

Here is the flow (not working)

/// <summary>
/// Calculate and Initialize Vertex Data
/// </summary>
private void InitVertex()
{
Cnt = FX0.Count;
VertexData = new List<CustomVertex.PositionNormalTextured>();

for (i = 0; i < Cnt - 1; i++)
{
//Calculate and fill into Veretex Data
}

Type VBType = typeof(CustomVertex.PositionNormalTextured);
VertexFormats VBFormats = CustomVertex.PositionNormalTextured.Format;
Usage VBUsage = Usage.WriteOnly | Usage.Dynamic;                               //Dynamic Vertex Buffer
Pool Pl = Pool.Default;

VBuffer = new VertexBuffer(VBType,
VertexData.Length,
GDevice,
VBUsage,
VBFormats,
Pl);

VBuffer.SetData(VertexData.ToArray(), 0, LockFlags.None);
}

Now, I have an Add Event which should append the Vertex Data

//Here I`m just duplicating the entries. The appended entries will be offset in the final code
private void Add_Click(object sender, EventArgs e)
{

//How do I Lock, Copy and Unlock. I`m lost here.

Render();
}

/// <summary>
/// Render
/// </summary>
private void Render()
{
GDevice.Clear(ClearFlags.Target, System.Drawing.Color.FromArgb(0, 0, 0).ToArgb(), 1.0f, 0);

GDevice.BeginScene();

InitDevice();
GDevice.VertexFormat = CustomVertex.PositionNormalTextured.Format;
GDevice.SetStreamSource(0, VFiberBuffer, 0);
GDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (VertexData.Count / 3));

GDevice.EndScene();
GDevice.Present();
}

Am I headed in the correct direction? Any sample code will be hugely helpful.
0

LVL 12

Expert Comment

ID: 37838787
I think I understand what you're trying to do but not 100% sure.  I will have a go at answering what I think you're doing, which is appears to be instancing.  Drawing the same thing at different positions and angles.

Assuming vertices 0-99 describe a model, and vertices 100-199 would describe the same model, but rotated 5 degrees.  The fastest way would normally be to draw vertices 0-99, set the world transform to rotate 5 degrees and draw vertices 0-99 again.  You don't need vertices 100-199 at all.

Assuming the vertices of the model don't change, the vertex buffer containing the vertices can stay in GPU ram all the time.  You can render it as many times as you want without sending data from CPU to GPU (a fairly slow process).  Even if the vertex buffer is not stored in GPU ram, you don't have to recalculate vertices 100-199 so that saves CPU time.

If the model is small, say 16 vertices, the time taken to repeatedly change the transform might exceed the time it takes to calculate new vertices and send the extra data.  In that case it might be faster to calculate vertices into a big buffer and render it all in one go.  But note, that is quite a few mights, you can only really find out by testing it.  As I say, there isn't single answer.

A good way to think about it is anything that changes is slow, vertex, matrix, texture or shader.  Speed means changing the smallest amount of things possible.  Changing the matrix is less change than making a new set of vertices (unless the set of vertices is very small).
0

LVL 12

Expert Comment

ID: 37838854
Oops, you added those while I was typing that last answer.  The best answer depends on what you are going to draw?  Dynamic vertex buffer are useful for models that change shape each frame.  Things like explosions, or fluid similations.  If the model is something like a spaceship, it doesn't change shape, it just moves around.

Dynamic vertex buffers are also useful when you are trying to save GPU memory (like on a phone).  Instead of storing all the models in static buffers you copy them into the dynamic buffer and render it for each set of data.

I'd suggest a method if I knew exactly what kind of thing you were rendering, I guess theres a reason you've chosen dynamic buffers?
0

LVL 12

Expert Comment

ID: 37838894
Now I see what's happening in that code.  That code is filling the vertex buffer with new vertices while the GPU is rendering the previous vertices.  It's an effective way to render lots of geometry from CPU ram when you have more than will fit in GPU ram.  It's not as fast as static vertex buffers, but its a useful technique, I use it when rendering objects with few vertices.

Still, the same answer applies, if all that changes is the transform, you can render the same vertices again and it will save time.  Particularly in the case of a dynamic buffer.
0

LVL 12

Expert Comment

ID: 37838958
Using the method the process would normally go like this:

Fill buffer with vertices for model A (vertices 0-50)
For each instance of model A set the transform and render vertices 0-50
Fill buffer with vertices of model B (50-120)
For each instance of model B set the transform and render vertices 50-120
Continue this way until all vertices in the buffer are used.
Start again from vertex 0.

If the models in question are small there's no gain from doing the instancing part.  This would be the case with something like a particle system, where each item is a quad.
0

Author Comment

ID: 37839107
Bingo! You described exactly what I`m trying to achieve -

Assuming vertices 0-99 describe a model, and vertices 100-199 would describe the same model, but rotated 5 degrees.  The fastest way would normally be to draw vertices 0-99, set the world transform to rotate 5 degrees and draw vertices 0-99 again.  You don't need vertices 100-199 at all.

What I initially did was : Step 1: Display 0-99
Step 2: Set Clear Device to false
Step 3: Rotate by angle
Step 4: Display 0-99
Repeat.

What this is doing is pretty much drawing vertices on top of each other.

This gives me the intended results. But if the user wants to transform the model, lets say he wants to see the side view, all the data is lost except the last set of vertices.
So, my thinking was to have all the vertices drawn.
0

LVL 12

Expert Comment

ID: 37842523
I'm not sure what step 2 is for? Are you redrawing a whole scene but without clearing the display?  Based on the code you shown I would do something like this
GDevice.Clear(ClearFlags.Target, System.Drawing.Color.FromArgb(0, 0, 0).ToArgb(), 1.0f, 0);

GDevice.BeginScene();

GDevice.VertexFormat = CustomVertex.PositionNormalTextured.Format;
GDevice.SetStreamSource(0, VFiberBuffer, 0);
GDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (VertexData.Count / 3));

GDevice.SetTransform(D3DTS_WORLD, &mtxObject);
GDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, (VertexData.Count / 3));

GDevice.EndScene();
GDevice.Present();
mtxObject is a D3DMATRIX that transforms the model in some way; rotate, translate or scale.  You can repeat the set matrix/draw model process as often as you like.  The matrix can be obtained by any means, for example you could put that code in a loop and recalculate a rotation matrix each time around the loop.

What normally happens in a game or an editor is that there is an object which represent the location of a model in the world, each object has it own matrix.  So if the model is a box of bullets, there can be ten 'bullets' objects, each managing a matrix but all using the same vertices to render.  Typically that means there is also a 'model' object, which manages the set of vertices.  Is this making sense to you?
0

LVL 12

Expert Comment

ID: 37842557
To be really fast, a game will actually build a list of renderable objects.  It adds each 'bullets' object that can be seen to a list managed by the model.  When it comes to rendering, the model sets the vertices then goes through it's list of visible objects, setting the matrix and rendering.  That's fairly involved for someone who is just starting though.
0

Author Comment

ID: 37844450
Satsumo..I`ve been working on a approach where I update the vertex buffer. I`ll post the code shortly -

So, the idea is to append the vertex buffer which contains the vertices which have been rotated. The below code doesn`t seem to work though, the vertex buffer is not being appended.

Shouldn`t LockFlags.NoOverwrite append new vertices to the vertices already contained in the buffer? What am I missing?

VertexBuffer.SetData(NewVertexData.ToArray(), 0, LockFlags.NoOverwrite);
0

LVL 12

Expert Comment

ID: 37845029
NoOverwrite is quite literal.  It means you won't change anything in the vertex buffer while its locked.  I've never used it, can't see the point to be honest.  For what you're doing I would use LockFlags.Discard, which tells the driver that your going to change the vertices in the locked area.  That way it can choose to give you a different buffer if the existing buffer is still being used for rendering.  Yes, the naming of the flags is a bit confusing.

However, that might not be the problem.  Does the code use an index of zero all the time?  Shouldn't the index change according to which vertices you intend to use?
0

Author Comment

ID: 37853212
I was off most of the day today, I`ll update tomorrow. Not much progress since I last posted. Do I need IndexBuffers? Will it make it simpler? My code initially wasn`t using it, but I can incorporate it.
0

Accepted Solution

San24 earned 0 total points
ID: 37857889
Here is the code. Hopefully this will give a better picture of what I`m trying to do. I have requested for attention through the comments in the code. I`m still very new to Graphics and DirectX, patience is much appreciated.

Let me know if this is a good approach and I`m in the right direction

/// <summary>
/// Initialize
/// Set the VetrexBuffer and the IndexBuffer
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Initialize_Click(object sender, EventArgs e)
{
Type VBType = typeof(CustomVertex.PositionNormalTextured);
VertexFormats VBFormats = CustomVertex.PositionNormalTextured.Format;
Usage VBUsage = Usage.WriteOnly | Usage.SoftwareProcessing;
Pool Pl = Pool.Managed;
//Usage VBUsage = Usage.WriteOnly | Usage.Dynamic;
//Pool Pl = Pool.Default;

CVLength = FX0.Count * 3 * 2 * 2;                  //FX\$, FY\$ and FZ\$ contain the X, Y, Z Values. CVLength will be the number of vertices for a single pass.

VXtrBuffer = new VertexBuffer(VBType,
CVLength * 50,                     //Have enough for 50 passes, same with index buffer
GDevice,
VBUsage,
VBFormats,
Pl);

IXtrBuffer = new IndexBuffer(GDevice, sizeof(ushort) * CVLength * 50, VBUsage, Pl, true);

InitXtr();             //Xtr
}

Here is heart of the code, I need to generate the same pattern offset by an angle for every pass.

/// <summary>
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Add_Click(object sender, EventArgs e)
{
}

/// <summary>
/// This works for Pass 1, subsequent passes don`t seem to work.
/// What I`m trying to do, is draw subsequent passes offset by an angle.
/// </summary>
{
int Cnt = 0;
int i = 0;
int Ind = 0;
Vector3 V0, V1, V2, V3;
Vector3 N1, N2;
float VertX0 = 0, VertY0 = 0, VertZ0 = 0;
float VertX1 = 0, VertY1 = 0, VertZ1 = 0;
float VertX2 = 0, VertY2 = 0, VertZ2 = 0;
float VertX3 = 0, VertY3 = 0, VertZ3 = 0;

//FX\$,FY\$, FZ\$ are the X, Y, Z coordinates
Cnt = FX0.Count;

//An array to store the vertices for a single Pass
XtrTexture = new CustomVertex.PositionNormalTextured[CVLength];

//For every call of the function, the angle is incremented by 5.
//This means the same pattern is offset by Ang everytime the function is called i,e, every Pass
Ang = Ang + 5;

//This is the main code which generates the pattern to be dispalyed.
//This works good. This is not the most efficient way to do this, but for the sake of simplicity, I`ll go with this for now.
for (i = 0; i < Cnt - 1; i++)
{
//Triangle 1 and 2 [Top]
VertX0 = (float)(FX0[i] * Math.Cos(Ang) - FY0[i] * Math.Sin(Ang));
VertY0 = (float)(FX0[i] * Math.Sin(Ang) + FY0[i] * Math.Cos(Ang));
VertZ0 = (float)(FZ0[i]);
V0 = new Vector3(VertX0, VertY0, VertZ0);

VertX1 = (float)(FX0[i + 1] * Math.Cos(Ang) - FY0[i + 1] * Math.Sin(Ang));
VertY1 = (float)(FX0[i + 1] * Math.Sin(Ang) + FY0[i + 1] * Math.Cos(Ang));
VertZ1 = (float)(FZ0[i + 1]);
V1 = new Vector3(VertX1, VertY1, VertZ1);

VertX2 = (float)(FX1[i + 1] * Math.Cos(Ang) - FY1[i + 1] * Math.Sin(Ang));
VertY2 = (float)(FX1[i + 1] * Math.Sin(Ang) + FY1[i + 1] * Math.Cos(Ang));
VertZ2 = (float)(FZ1[i + 1]);
V2 = new Vector3(VertX2, VertY2, VertZ2);

VertX3 = (float)(FX1[i] * Math.Cos(Ang) - FY1[i] * Math.Sin(Ang));
VertY3 = (float)(FX1[i] * Math.Sin(Ang) + FY1[i] * Math.Cos(Ang));
VertZ3 = (float)(FZ1[i]);
V3 = new Vector3(VertX3, VertY3, VertZ3);

N1 = CalcNormal(V0, V1, V2);
N2 = CalcNormal(V2, V3, V0);

XtrTexture[Ind + 0] = new CustomVertex.PositionNormalTextured(V0, N1, 0f, .5f);
XtrTexture[Ind + 1] = new CustomVertex.PositionNormalTextured(V1, N1, 1.0f, 0.5f);
XtrTexture[Ind + 2] = new CustomVertex.PositionNormalTextured(V2, N1, 1.0f, 1.0f);

XtrTexture[Ind + 3] = new CustomVertex.PositionNormalTextured(V2, N2, 1.0f, 1.0f);
XtrTexture[Ind + 4] = new CustomVertex.PositionNormalTextured(V3, N2, 0.0f, 1.0f);
XtrTexture[Ind + 5] = new CustomVertex.PositionNormalTextured(V0, N2, 0f, 0.5f);

//Triangle 3 and 4 [Bottom]
VertX0 = (float)(FX2[i] * Math.Cos(Ang) - FY2[i] * Math.Sin(Ang));
VertY0 = (float)(FX2[i] * Math.Sin(Ang) + FY2[i] * Math.Cos(Ang));
VertZ0 = (float)(FZ2[i]);
V0 = new Vector3(VertX0, VertY0, VertZ0);

VertX1 = (float)(FX2[i + 1] * Math.Cos(Ang) - FY2[i + 1] * Math.Sin(Ang));
VertY1 = (float)(FX2[i + 1] * Math.Sin(Ang) + FY2[i + 1] * Math.Cos(Ang));
VertZ1 = (float)(FZ2[i + 1]);
V1 = new Vector3(VertX1, VertY1, VertZ1);

VertX2 = (float)(FX0[i + 1] * Math.Cos(Ang) - FY0[i + 1] * Math.Sin(Ang));
VertY2 = (float)(FX0[i + 1] * Math.Sin(Ang) + FY0[i + 1] * Math.Cos(Ang));
VertZ2 = (float)(FZ0[i + 1]);
V2 = new Vector3(VertX2, VertY2, VertZ2);

VertX3 = (float)(FX0[i] * Math.Cos(Ang) - FY0[i] * Math.Sin(Ang));
VertY3 = (float)(FX0[i] * Math.Sin(Ang) + FY0[i] * Math.Cos(Ang));
VertZ3 = (float)(FZ0[i]);
V3 = new Vector3(VertX3, VertY3, VertZ3);

N1 = CalcNormal(V0, V1, V2);
N2 = CalcNormal(V2, V3, V0);

XtrTexture[Ind + 6] = new CustomVertex.PositionNormalTextured(V0, N1, 0.0f, 0.0f);
XtrTexture[Ind + 7] = new CustomVertex.PositionNormalTextured(V1, N1, 1.0f, 0f);
XtrTexture[Ind + 8] = new CustomVertex.PositionNormalTextured(V2, N1, 1.0f, 0.5f);

XtrTexture[Ind + 9] = new CustomVertex.PositionNormalTextured(V2, N2, 1.0f, 0.5f);
XtrTexture[Ind + 10] = new CustomVertex.PositionNormalTextured(V3, N2, 0.0f, 0.5f);
XtrTexture[Ind + 11] = new CustomVertex.PositionNormalTextured(V0, N2, 0.0f, 0.0f);

Ind += 12;
}

//Question
//I want to update the index buffer, I`m not sure if this is the correct way of doing this.
//1st Pass I`m locking and updating from 0 to CVLength
//2nd Pass I`m locking from the CVLength to 2 * CVLength [since the number of indices are doubled, only update the new vertices?]
//3rd Pass.....nth Pass
using (GStream = IXtrBuffer.Lock((Pass * CVLength), ((Pass * CVLength) + CVLength) * sizeof(ushort), LockFlags.None))
{
for (int j = (Pass * CVLength); j < (Pass * CVLength) + CVLength; j += 12)
{
ushort[] Indices = {
(ushort)(j + 0),
(ushort)(j + 1 ),
(ushort)(j + 2),
(ushort)(j + 3),
(ushort)(j + 4),
(ushort)(j + 5),
(ushort)(j + 6),
(ushort)(j + 7),
(ushort)(j + 8),
(ushort)(j + 9),
(ushort)(j + 10),
(ushort)(j + 11)
};
GStream.Write(Indices);
}

IXtrBuffer.Unlock();
}

//Question
//Just like the index buffer, updating the new Vertices here
//Is VXtrBuffer appended with new vertices for each pass (what I`m trying to do) or VXtrbuffer gets reset and get new set of vertices?
GStream = VXtrBuffer.Lock((Pass * CVLength), ((Pass * CVLength) + CVLength) * 32, LockFlags.None);
GStream.Write(XtrTexture);
VXtrBuffer.Unlock();

GDevice.Clear(ClearFlags.Target, System.Drawing.Color.FromArgb(0, 0, 0).ToArgb(), 1.0f, 0);

GDevice.BeginScene();

InitDeviceXtr();
GDevice.VertexFormat = CustomVertex.PositionNormalTextured.Format;

//Question
//The function Setstream, comments in the parameter
GDevice.SetStreamSource(0,
VXtrBuffer,                                   //If the Vertex Buffer is appended, I should offset it for each pass
(Pass * CVLength) * 32);                        //Offset for each pass
GDevice.Indices = IXtrBuffer;

//Question
//The function DrawIndexPrimitives, comments in the paramter
GDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList,             //Primitive Type
Pass * CVLength,                        //Base Vertex - Offset applied?
0,                                      //MinIntVertex - Not sure? Should this point to the index of the next batch of vertices
CVLength,                               //Number of Vertices
(Pass * CVLength),                      //Start Index
((Pass * CVLength) + CVLength) / 3);    //Primitive Count

//Increment the Pass Number
Pass++;

GDevice.EndScene();
GDevice.Present();
}
0

Author Closing Comment

ID: 38140765
Problem resolved.
0

## Featured Post

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

### Suggested Solutions

The article shows the basic steps of integrating an HTML theme template into an ASP.NET MVC project
As cyber crime continues to grow in both numbers and sophistication, a troubling trend ofÂ optimizationÂ has emerged over the last year.
Nobody understands Phishing better than an anti-spam company. Thatâ€™s why we are providing Phishing Awareness Training to our customers. According to a report by Verizon, only 3% of targeted users report malicious emails to management. With companâ€¦
Finding and deleting duplicate (picture) files can be a time consuming task. My wife and I, our three kids and their families all share one dilemma: Managing our pictures. Between desktops, laptops, phones, tablets, and cameras; over the last decadeâ€¦
###### Suggested Courses
Course of the Month5 days, left to enroll