• Advertisement
Sign in to follow this  

One Or More VertexBuffers in DirectX

This topic is 1220 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi Everyone,

I have a question regarding vertex buffers and their transformation.I want to render objects of different shapes(vertices) and have them move in separate directions.For example if i have a triangle that's rotating in one direction and a square which is moving or rotating in another direction should the vertices of these two objects be placed in the same Vertex buffer or should they be placed in separate buffers.

Both solutions don't make sense to me: 

1) I've tried placing them in one buffer but then they rotate/move in the same direction. 

2) Having a VertexBuffer for every object that's moving differently will requier me having a huge number of buffers.

So my question is how should i go about placing vertices of objects,in one or more buffers, and how should i move(transform) them?

 

 

Share this post


Link to post
Share on other sites
Advertisement

- option 1 is the right way - pack objects in few vertex buffers

 

You are doing something wrong, can you show your draw code?

 

it is perfectly okay to draw just a portion of a vertex buffer, with separate transforms, in order to make the objects move in the unique directions.

 

Cheers!

Share this post


Link to post
Share on other sites

I guess option 1 then. So what i got from this is that we put all vertices(square,triangle) in a single vertex buffer, we update every vertex so that the object that we want to draw(square) sits in its designated position and then just draw only the vertices of that object using 

context->Draw  and ignore all other vertices. We do the same for the triangle except we update it differently.

Is this how it's done and if so doesn't updating every vertex in the buffer just for drawing one object impact performance?

I know this is not the most technical explanation so here is some code. i managed to update the square and the triangle separatelly :

 

The D3DGraphics.cpp

void D3DGraphics::InitGraphics()
{ VERTEX OurVertices[] =
    {
        {-0.5f, -0.5f, 0.5f, DirectX::PackedVector::XMCOLOR(1.0f, 0.0f, 0.0f, 1.0f)},
        {-0.5f,  0.5f, 0.5f, DirectX::PackedVector::XMCOLOR(0.0f, 1.0f, 0.0f, 1.0f)},
        { 0.5f,  0.5f, 0.5f, DirectX::PackedVector::XMCOLOR(0.0f, 0.0f, 1.0f, 1.0f)},
        { 0.8f, -0.5f, 0.5f, DirectX::PackedVector::XMCOLOR(0.0f, 0.0f, 1.0f, 1.0f)},
        {-0.4f, -0.3f, 0.4f, DirectX::PackedVector::XMCOLOR(1.0f, 0.0f, 0.0f, 1.0f)},
	{-0.4f,  0.3f, 0.4f, DirectX::PackedVector::XMCOLOR(0.0f, 1.0f, 0.0f, 1.0f)},
	{ 0.3f,  0.3f, 0.4f, DirectX::PackedVector::XMCOLOR(0.0f, 0.0f, 1.0f, 1.0f)}
    };
DWORD indices[] = 
	{0,2,3,
         0,1,2,
	 4,5,6};
//create and fill the index buffer
D3D11_BUFFER_DESC indexBufferDesc;
//...

//create and fill the vertex buffer
D3D11_BUFFER_DESC bd;
//...

//Create the buffer to send to the cbuffer in effect file
D3D11_BUFFER_DESC cbbd;	
//...
//Camera information
//Set the View matrix
//Set the Projection matrix

void D3DGraphics::DrawVertices()
{
        // select which vertex buffer to display
        UINT stride = sizeof(VERTEX);
        UINT offset = 0;

	m_pImmediateContext->IASetIndexBuffer( squareIndexBuffer, DXGI_FORMAT_R32_UINT, 0);
        m_pImmediateContext->IASetVertexBuffers(0, 1, &pVBuffer, &stride, &offset);
	// select which primtive type we are using
        m_pImmediateContext->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        //Refresh the Depth/Stencil view
	m_pImmediateContext->ClearDepthStencilView(depthStencilView, D3D11_CLEAR_DEPTH|D3D11_CLEAR_STENCIL, 1.0f, 0);
        
        //we update and draw the square
	WVP = squareWorld * camView * camProjection;
	cbPerObj.WVP = XMMatrixTranspose(WVP);	
	m_pImmediateContext->UpdateSubresource( cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0 );
	m_pImmediateContext->VSSetConstantBuffers( 0, 1, &cbPerObjectBuffer );
        m_pImmediateContext->DrawIndexed(6, 0, 0 );
        
        //we update and draw the triangle
        WVP = triangleWorld * camView * camProjection;
	cbPerObj.WVP = XMMatrixTranspose(WVP);	
	m_pImmediateContext->UpdateSubresource( cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0 );
	m_pImmediateContext->VSSetConstantBuffers( 0, 1, &cbPerObjectBuffer );
	m_pImmediateContext->DrawIndexed( 3, 4, 4 );
}

void D3DGraphics::UpdateScene()
{
	//Keep the cubes rotating
	rot += .0001f;
	if(rot > 6.2831f)
		rot = 0.0f;

	//Reset squareWorld
	squareWorld = DirectX::XMMatrixIdentity();
        //Define square's world space matrix
	DirectX::XMVECTOR rotaxis = DirectX::XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);
	Rotation = DirectX::XMMatrixRotationAxis( rotaxis, rot);
	Translation = DirectX::XMMatrixTranslation( 0.0f, 0.0f, 0.0f );

	//Set square's world space using the transformations
	squareWorld = Translation * Rotation;

	//Reset triangleWorld
	triangleWorld = DirectX::XMMatrixIdentity();

	//Define triangle's world space matrix
	Rotation = DirectX::XMMatrixRotationAxis( rotaxis, -rot);
	Scale = DirectX::XMMatrixScaling( 1.3f, 1.3f, 1.3f );

	//Set triangle's world space matrix
	triangleWorld = Rotation * Scale;
}


The shaders.fx code:

cbuffer cbPerObject
{
	float4x4 WVP;
};
struct VOut
{
    float4 position : SV_POSITION;
    float4 color : COLOR;
};

VOut VShader(float4 position : POSITION, float4 color : COLOR)
{
    VOut output;

    output.position = mul(position, WVP);
    output.color = color;

    return output;
}


float4 PShader(float4 position : SV_POSITION, float4 color : COLOR) : SV_TARGET
{
    return color;
}

Sorry if there's some unnecessary stuff here it's my first time posting on gamedev smile.png  and Thanks

Share this post


Link to post
Share on other sites

The general indexed approach you've coded can be made to work, or, alternatively, as mhagain suggests, you don't need to use an index buffer if you put the vertices in trianglestrip order and set primitive topology to D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP.

 

Note: Take a look at the docs for context->DrawIndexed. Your indices already point to the correct position in the vertex buffer. As a result, your code for drawing the triangle:

m_pImmediateContext->DrawIndexed( 3, 4, 4 );

should be:

m_pImmediateContext->DrawIndexed( 3, 6, 0 );
// 3 vertices, start at index buffer position 6, add 0 to each index to get the vertex buffer position.

Also, perhaps just semantics, but your comments: "we update every vertex so that the object that we want to draw(square) sits in its designated position" and "doesn't updating every vertex in the buffer just for drawing one object impact performance?" might be better phrased: "using the same vertices, the objects are transformed to their correct positions in the shader by setting the appropriate world matrix for each object."

 

With regard to updating the constant buffer with each object's world matrix prior to rendering - that's pretty much how it's done. smile.png EDIT: The alternative of actually updating the values in the vertex buffer itself is inefficient (having to do with caching, moving memory from GPU to CPU and back to GPU, etc.) As a result, being able to set a matrix to transform vertices in the GPU enhances performance!

 

EDIT: Just FYI, your line of code Translation = DirectX::XMMatrixTranslation( 0.0f, 0.0f, 0.0f );  doesn't serve any purpose, unless you anticipate changing the object position.

Edited by Buckeye

Share this post


Link to post
Share on other sites

Yeah that worked, now there separate and drawn properly. I just have one more thing i want to ask.

How can you add objects to a vertex buffer at runrime? 

For example if i had an empty vertexbuffer initialized at the start of my program could i:

-have the user make an object using vertices that they've defined by clicking on a position on the screen and then somehow add those vertices to the vertexbuffer to be drawn.I guess this was done by making the buffer dynamic in DX9 but there's nothing on how it's done in DX11.

OR

-have the vertexbuffer be filled with vertices and then transform each individual vertex(not all, just the number of vertices in the object) using XMMatrixTranslation to the position of vertices provided by the user so that they make the object. 

 

Both seem like they could be made to work but i'm not sure unsure.png

Edited by exscreen

Share this post


Link to post
Share on other sites

You might want to start by reading this Q&A.

 

If the vertex buffer is to be updated infrequently, but the addition(s) will be permanent, create the vertex buffer as described in that link. You can take a look at the docs for CreateBuffer (ID3D11Device), D3D11_USAGE, and Map (ID3D11DeviceContext). Pay attention to the various flags used to create and map the buffer. If you KNOW there are going to be more additions to the vertex buffer, you can initially create the buffer larger than initially needed, and keep track of (all) object indices elsewhere. It will be faster to add new info to an existing buffer, than create a new buffer each time you want to add something. Then use DrawIndexed to draw only what's needed.

 

EDIT: That being said, to answer your question regarding adding vertices at runtime (if you must):

 

To add new vertices to your scene:

 

1. If the current vertex buffer doesn't have room for the new vertices, get a copy of the old data from that buffer. Use context->Map(...) to get a pointer to the old data (similar to d3dx9 Lock); Copy the data to a CPU memory in a data array large enough to hold old + new data. Unmap.

2. Release the vertex buffer.

3. Add your new vertex data to the CPU data array.

4. Create a new vertex buffer (at least) large enough to hold all the data. Create it with appropriate flags if you anticipate adding more data later. Initialize the new vertex buffer from the data array (old+new verex data).

5. Update your index buffer similarly. Be sure to keep track of which objects are at which index positions, etc.

 

If your current vertex buffer is large enough to add new data to the end of the buffer, Map the buffer, copy new data to the mapped subresource, and Unmap the buffer. That process is similar to D3DX9 Lock, copy, Unlock.

 

EDIT: As you seem familiar with D3DX9, note that creating a D3D11 vertex buffer with initialization data is similar to the D3DX9 process of:

 

a. create the buffer.

b. Lock() the buffer.

c. copy data to the buffer.

d. Unlock() the buffer.

Edited by Buckeye

Share this post


Link to post
Share on other sites

I suggest modifying the above advice.  If your buffer isn't big enough for the data you need to draw in a frame, then you should have created a bigger buffer to begin with.  On PC there is, in the general case, absolutely no win to be had from micromanaging tiny amounts of memory.  If for example you find that one time out of 10 you need a 512k buffer, but the other 9 times 64k is enough, then just create the 512k buffer.  "Wasting" the extra memory each frame is far preferable to having lots of little hitches and stalls incurred by destroying and creating resources at runtime.

Share this post


Link to post
Share on other sites


I suggest modifying the above advice. If your buffer isn't big enough for the data you need to draw in a frame, then you should have created a bigger buffer to begin with.


If you KNOW there are going to be more additions to the vertex buffer, you can initially create the buffer larger than initially needed, and keep track of (all) object indices elsewhere. It will be faster to add new info to an existing buffer, than create a new buffer each time you want to add something.

 

I'm willing to learn - but I'm not sure I understand the difference between what you're suggesting and my suggestion. Can you clarify?

Share this post


Link to post
Share on other sites

 


I suggest modifying the above advice. If your buffer isn't big enough for the data you need to draw in a frame, then you should have created a bigger buffer to begin with.

 

 


If you KNOW there are going to be more additions to the vertex buffer, you can initially create the buffer larger than initially needed, and keep track of (all) object indices elsewhere. It will be faster to add new info to an existing buffer, than create a new buffer each time you want to add something.

 

I'm willing to learn - but I'm not sure I understand the difference between what you're suggesting and my suggestion. Can you clarify?

 

 

What I'm talking about is the 5 points under your "to add new vertices to your scene" heading; maybe you need to clarify that you don't mean "do this at runtime"?

Share this post


Link to post
Share on other sites


What I'm talking about is the 5 points under your "to add new vertices to your scene" heading; maybe you need to clarify that you don't mean "do this at runtime"?

 

Ah. My intent was to first state that buffers should be sized in anticipation of what needs may be. Then the "add new vertices to your scene" was a direct answer to:

 


How can you add objects to a vertex buffer at runrime?

 

So, I actually did mean "do this at runtime." Will add clarification. Tnx.

Share this post


Link to post
Share on other sites

So this is what i came up with:
1) I created the vertexbuffer and indexbuffer with arrays of [200] vertices and indices
2)I made a SetPolygon function that catches mouse clicks and sets up(InitPolygon) and updates(UpdatePolygon) the polygon(rectangle for now) based on the mouse coordinates in the main game loop.
3)InitPolygon - Initializes the rectangle and stores vertices in empty spots in a buffer called Vertexes and indices in Indexes:

4)UpdatePolygon - updates the rectangle based on the new mouse coords:
 

I don't need to post code for these because they will take alot of space.If you think it's necessary i will . They work and return the Vertexes and Indexes buffers.

Now i get a Vertexes[200] and Indexes[200] array with the rectangle vertices being the first 4 and indices being the first 6.These arrays(buffers) are structs that aside form DWORD and VERTEX variables have a bool Initialized; variable that keeps track of which vertices are Initialized and which are not.Then in the MapBuffers function i transport the VERTEX,DWORD variables of the arrays to 2 new arrays of VERTEX and DWORD and then map the index and vertx buffers insert these arrays into the pData and unmap. 

Now here is the D3DGraphics.cpp file:

void D3DGraphics::initGraphics()
{
	VERTEX OurVertices1[200];
	DWORD OurIndices1[200];


//create the index buffer
	D3D11_BUFFER_DESC indexBufferDesc;
	ZeroMemory( &indexBufferDesc, sizeof(indexBufferDesc) );

	indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
	indexBufferDesc.ByteWidth = sizeof(DWORD)*200 ;
	indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
	indexBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	indexBufferDesc.MiscFlags = 0;

	D3D11_SUBRESOURCE_DATA iinitData;

	iinitData.pSysMem = OurIndices1;
	m_pDevice->CreateBuffer(&indexBufferDesc, &iinitData, &squareIndexBuffer);
        m_pImmediateContext->IASetIndexBuffer( squareIndexBuffer, DXGI_FORMAT_R32_UINT, 0);
	
// create the vertex buffer
    D3D11_BUFFER_DESC bd;
    ZeroMemory(&bd, sizeof(bd));

    bd.Usage = D3D11_USAGE_DYNAMIC;             // write access access by CPU and GPU
    bd.ByteWidth = sizeof(VERTEX) * 200;          // size is the VERTEX struct * 3
    bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;    // use as a vertex buffer
    bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; // allow CPU to write in buffer
	
    D3D11_SUBRESOURCE_DATA vertexBufferData; 

    ZeroMemory( &vertexBufferData, sizeof(vertexBufferData) );
    vertexBufferData.pSysMem = OurVertices1;
    m_pDevice->CreateBuffer(&bd, &vertexBufferData, &pVBuffer); // create the buffer

//Create the Viewport
	D3D11_VIEWPORT m_Viewport;
	ZeroMemory(&m_Viewport, sizeof(D3D11_VIEWPORT));
	m_Viewport.Width = static_cast<float>(SCREEN_WIDTH);
	m_Viewport.Height = static_cast<float>(SCREEN_HEIGHT);
	m_Viewport.TopLeftX = 0;
        m_Viewport.TopLeftY = 0;
	m_Viewport.MaxDepth = 1.0f;
	m_Viewport.MinDepth = 0.0f;
	//BIND VIEWPORT
	m_pImmediateContext->RSSetViewports(1,&m_Viewport);

//Camera information
	camPosition = DirectX::XMVectorSet( 0.0f, 3.0f, -0.8f, 0.0f );
	camTarget = DirectX::XMVectorSet( 0.0f, 0.0f, 0.0f, 0.0f );
	camUp = DirectX::XMVectorSet( 0.0f, 1.0f, 0.0f, 0.0f );
        //Set the View matrix
	camView = DirectX::XMMatrixLookAtLH( camPosition, camTarget, camUp );
        //Set the Projection matrix
	camProjection = DirectX::XMMatrixPerspectiveFovLH( 0.4f*3.14f, (float)SCREEN_WIDTH/SCREEN_HEIGHT, 1.0f, 1000.0f);
}

void D3DGraphics::DrawVertices()
{
		bool win = MapBuffers();
	     // select which vertex buffer to display
        UINT stride = sizeof(VERTEX);
        UINT offset = 0;
		m_pImmediateContext->VSSetShader(pVS, 0, 0);
	        m_pImmediateContext->PSSetShader(pPS, 0, 0); 
		m_pImmediateContext->RSSetState(WireFrame);
		m_pImmediateContext->IASetIndexBuffer( squareIndexBuffer, DXGI_FORMAT_R32_UINT, 0);
		m_pImmediateContext->IASetVertexBuffers(0, 1, &pVBuffer, &stride, &offset);
	    
m_pImmediateContext->IASetInputLayout(vertLayout);
// select which primtive type we are using
m_pImmediateContext->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
//Refresh the Depth/Stencil view
m_pImmediateContext->ClearDepthStencilView(depthStencilView, D3D11_CLEAR_DEPTH|D3D11_CLEAR_STENCIL, 1.0f, 0);
// draw the vertex buffer to the back buffer
m_pImmediateContext->DrawIndexed(6, 0, 0 );
}

bool D3DGraphics::MapBuffers() 
{
	int numofinitvert = CountInitializedVert();
	int numofinitind = CountInitializedInd();
	DWORD OurIndices [200];
	VERTEX OurVertices [200];
	FromVertexestoVERTEX(numofinitvert,OurVertices);  //convert from Vertexes to VERTEX
	FromIndexestoDWORD(numofinitind,OurIndices);     //convert from Indexes to DWORD
	   
	D3D11_MAPPED_SUBRESOURCE resource1;
    HRESULT hResult1 = m_pImmediateContext->Map(pVBuffer, 0,
     D3D11_MAP_WRITE, 0, &resource1);

  // This will be S_OK
  if(hResult1 != S_OK)
     return false;

  resource1.pData = OurVertices;

  m_pImmediateContext->Unmap(pVBuffer, 0);
	

    D3D11_MAPPED_SUBRESOURCE resource2;
    HRESULT hResult2 = m_pImmediateContext->Map(squareIndexBuffer, 0,
     D3D11_MAP_WRITE, 0, &resource2);

  // This will be S_OK
  if(hResult2 != S_OK)
     return false;

  resource2.pData = OurIndices;

  m_pImmediateContext->Unmap(pVBuffer, 0);
	return true;
}

void D3DGraphics::FromVertexestoVERTEX(int numberofvert,VERTEX OurVertices[])
{
	for(int x= 0; x <= numberofvert;x++)
	{
		OurVertices[x] = Vertexes[x].vertex;
	};
}

void D3DGraphics::FromIndexestoDWORD(int numberofind,DWORD OurIndices[])
{
	for(int x= 0; x <= numberofind;x++)
	{
		OurIndices[x] = Indexes[x].index;
	};
}

int D3DGraphics::CountInitializedInd()
{
	int numberofind =  0;
	for(int x= 0; x < 120;x++)
	{
		if(Indexes[x].initialized ==true)
		{
			numberofind +=1;
		}
	};
	return numberofind;
}

int D3DGraphics::CountInitializedVert()
{
	int numberofvert = 0;
	for(int x= 0; x < 120;x++)
	{
		if(Vertexes[x].initialized ==true)
		{
			numberofvert +=1;
		}
	};
	return numberofvert;
}

The function fails and i have no idea why. The flags should be alright. I access the pData pointer in the resource and set the vertices. How should i go about writing into a buffer? What am i doing wrong?

Any help would be good, Thanks smile.png

Share this post


Link to post
Share on other sites

The function fails and i have no idea why.

 

You should be creating the device with D3D11_CREATE_DEVICE_DEBUG, and the debug output window (assuming you're using VS) should provide explicit error statements reflecting the following observations (possibly others).

 

A quick scan of the code - you have (at least) several problems.

 

1. You create squareIndexBuffer with usage D3D11_USAGE_DEFAULT. That should, like the vertex buffer, be xxx_DYNAMIC if you want CPU write access.

2. You Map(squareIndexBuffer...) but you Unmap(pVBuffer)

3. You need to Map both the vertex and index buffers with D3D11_MAP_WRITE_DISCARD, not D3D11_MAP_WRITE. With that flag, you must fill the entire buffer.

 

EDIT: You will do yourself a big favor by doing step by step debugging when something fails.

 

1. Write your code to check every function for errors. You create buffers, etc., etc. without checking for success or failure. Shame, shame.

2. Compile with D3D11_CREATE_DEVICE_DEBUG (for the debug compile) and watch the output window in VS.

3. When you check for errors, rather than just returning false on failure from a function that does several things, provide yourself with some explicit information, perhaps with:

 

if(FAILED(hr))
{
   MessageBox(NULL, "Vertex buffer creation failed.","Error", MB_OK); 
   return false;
}


4. You can set breakpoints at critical steps (such as buffer creation, mapping, etc.) and examine values passed to and returned from the function. If VS, you can just mouseover variables and see if the values are correct.

 

By following the above steps, you can save time and avoid having to post: "Here's 100 lines of code. It doesn't work. What's wrong?" wink.png

Edited by Buckeye

Share this post


Link to post
Share on other sites

It worked! biggrin.png  I fixed the Flags and the Unmap function and used  memcpy to copy the vertices and indices into the buffers and it's working.

Sorry for the 100 lines of code, really didn't need error handling up until this point so i'll start using it from now on. Thanks again smile.png

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement