Sign in to follow this  
leonard2012

Can the size of dynamic vertex buffer be changed freely at runtime?

Recommended Posts

Hi, all

I am new to Direct3D programming and now writing an graphics editing application that use D3D for rendering graphical shapes (lines, polygons etc.). In the application, I want to draw in the client window a world map that consists of multiple (400 or so) lines (strips and loops). Someone at this post ([url="http://www.gamedev.net/topic/622877-poor-rendering-performance-when-drawing-simple-2d-shapes/"]http://www.gamedev.n...mple-2d-shapes/[/url]) suggest me to use dynamic vertex buffer.
I post the code of the drawing routine at the end of this post. The function draw() will be called to draw each line of the map. Since each line has different number of vertices, the size of the vertex buffer has to be changed. I find that unless I sort the lines of the map in decreasing order based on their number of vertices, my application will crash the display driver. So I wonder if the size of dynamic vertex buffer can be changed freely at runtime?

[CODE]
#define USE_DYNAMIC_BUFFER 1
void mgral_directx::draw(int obj_type, mpoint *points, int count)
{
static ID3D10Buffer* iD3dVB=NULL;
int numVertices;
std::vector<Vertex> vertices;
numVertices = count;
// Assign values to vertex buffer
#ifndef USE_DYNAMIC_BUFFER
vertices.resize(numVertices);
for ( int i = 0; i < count; i++ ) {
vertices[i].pos = D3DXVECTOR3(points[i].x, points[i].y, points[i].z);
vertices[i].color = D3DXCOLOR(penColor_);
}
#endif

// Create vertex buffer
if ( iD3dVB==NULL ) {
D3D10_BUFFER_DESC vbd;
#ifndef USE_DYNAMIC_BUFFER
vbd.Usage = D3D10_USAGE_IMMUTABLE;
vbd.ByteWidth = sizeof(Vertex) * numVertices;
vbd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
vbd.CPUAccessFlags = 0;
vbd.MiscFlags = 0;
D3D10_SUBRESOURCE_DATA vinitData;
vinitData.pSysMem = &vertices[0];
HR(iD3dDevice_->CreateBuffer(&vbd, &vinitData, &iD3dVB));
#else
vbd.Usage = D3D10_USAGE_DYNAMIC;
vbd.ByteWidth = sizeof(Vertex) * numVertices;
vbd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
vbd.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
vbd.MiscFlags = 0;
HR(iD3dDevice_->CreateBuffer(&vbd, 0, &iD3dVB));
#endif
// Create effect
buildFX();
// Create input layout
buildVertexLayouts();
}
// Update vertex buffer
#ifdef USE_DYNAMIC_BUFFER
Vertex* v = 0;
HR(iD3dVB->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**)&v ));
for ( int i = 0; i < count; i++ ) {
v[i].pos = D3DXVECTOR3(points[i].x, points[i].y, points[i].z);
v[i].color = D3DXCOLOR(penColor_);
}
iD3dVB->Unmap();
#endif
// Restore default states, input layout and primitive topology
// because iD3dxFont_->DrawText changes them. Note that we can
// restore the default states by passing null.
iD3dDevice_->OMSetDepthStencilState(0, 0);
float blendFactors[] = {0.0f, 0.0f, 0.0f, 0.0f};
iD3dDevice_->OMSetBlendState(0, blendFactors, 0xffffffff);
// Bind the input layout to the device
iD3dDevice_->IASetInputLayout(mVertexLayout);
// Specify the primitive topology
iD3dDevice_->IASetPrimitiveTopology(d3dPrimitiveTopologies[obj_type]);
// Bind vertex buffer to the input slot of the device
UINT stride = sizeof(Vertex);
UINT offset = 0;
iD3dDevice_->IASetVertexBuffers(0, 1, &iD3dVB, &stride, &offset);
// set constants
D3DXMATRIX mWVP = modelMatrix_*viewMatrix_*projectMatrix_;
mfxWVPVar->SetMatrix((float*)&mWVP);
// Draw the geometry objects
D3D10_TECHNIQUE_DESC techDesc;
mTech->GetDesc( &techDesc );
for(UINT p = 0; p < techDesc.Passes; ++p) {
mTech->GetPassByIndex( p )->Apply(0);
iD3dDevice_->Draw(numVertices, 0);
}
// Remember to add the line below after all drawing code.
iDxgiSwapChain_->Present(0, 0);
#ifndef USE_DYNAMIC_BUFFER
ReleaseCOM(iD3dVB);
#endif
}

[/CODE]

Share this post


Link to post
Share on other sites
Hi,

You can’t change the size of a resource, once it was created. Common practice is to allocate a buffer that is “big enough”. If it turns out during runtime that your buffer can't contain all the line geometry, then just split up the rendering of the lines into multiple draw calls.

By the way, do you release the resource at the end of every draw call?
Better release it just once, when you shut down the application.

Cheers!

Share this post


Link to post
Share on other sites
Generally with a dynamic vertex buffer you'd use the discard/no-overwrite pattern, which is recommended by the DirectX SDK and is more likely to be optimized by drivers.

So, the way to do this is to allocate a vertex buffer that is several multiples of the maximum size you're going to need at startup. You'll need to experiment some to tune this for your program, but that bit's trivial enough. Store out the maximum number of vertexes this can hold somewhere; let's call it "MaxVertexes" (and apologies in advance for the anglicised plural of "vertex" throughout this post; old habits die hard and this is a very old one).

Keep a global (or whatever) counter which I'll call "FirstVertex" for the purposes of this explanation; it starts at 0.

You then work out how many vertexes you're going to need for each draw call (I'll call it "NumVertexes"), and do one of two things.

If (FirstVertex + NumVertexes >= MaxVertexes) you Map the buffer with D3D10_MAP_WRITE_DISCARD and set FirstVertex to 0.
Otherwise map with D3D10_MAP_WRITE_NO_OVERWRITE and do nothing with FirstVertex (we'll come to that in a bit...)

Having done that you set a pointer to the first vertex you're going to write to, by casting the ppData param to your vertex type (or casting your vertex type to void ** when mapping), then adding the value of FirstVertex to the result.

Now you've got a block of memory you can write your vertex data into, so do so and Unmap when done.

Then you issue your draw call, using the appropriate parameters (for Draw it would be "device->Draw (NumVertexes, FirstVertex)"), and finally add NumVertexes to the value of FirstVertex.

Repeat as necessary until your entire scene is complete.

Code-wise it looks something like this (error-checking/etc omitted for clarity):
[code]vertextype *verts = NULL;

if (FirstVertex + NumVertexes >= MaxVertexes)
{
buffer->Map (D3D10_MAP_WRITE_DISCARD, 0, (void **) &verts);
FirstVertex = 0;
}
else buffer->Map (D3D10_MAP_WRITE_NO_OVERWRITE, 0, (void **) &verts);

verts += FirstVertex;

// ....write in data....

buffer->Unmap ();
device->Draw (NumVertexes, FirstVertex);

FirstVertex += NumVertexes;[/code]

Share this post


Link to post
Share on other sites
[quote name='Tsus' timestamp='1333561145' post='4928250']
Hi,

You can’t change the size of a resource, once it was created. Common practice is to allocate a buffer that is “big enough”. If it turns out during runtime that your buffer can't contain all the line geometry, then just split up the rendering of the lines into multiple draw calls.

By the way, do you release the resource at the end of every draw call?
Better release it just once, when you shut down the application.

Cheers!
[/quote]
Thanks, I do not release the resource at the end of every draw call.

Share this post


Link to post
Share on other sites
Hi, mhagain
I tried your approach but my application crashes the display driver whenever this line of code
iDxgiSwapChain_->Present(0, 0);
is executed when the function draw() is called at the second time.
The number of vertices are 312 and 20871 for the first and second call of the function draw().
I believe in your code the line to update verts should be put before the function call Map():
[CODE]
v += firstVertex;
if (firstVertex + numVertices >= MaxVertexes) {
iD3dVB->Map(D3D10_MAP_WRITE_DISCARD, 0, (void **)&v);
firstVertex = 0;
}
else
iD3dVB->Map(D3D10_MAP_WRITE_NO_OVERWRITE, 0, (void **)&v);
[/CODE]

I also add the code
[CODE]
static Vertex* v = 0;
static int firstVertex = 0, MaxVertexes=65535;
int numVertices;
if ( v==NULL )
v = (Vertex*)malloc(MaxVertexes*sizeof(Vertex));
[/CODE]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this