Sign in to follow this  

[Instancing] Flickering when updating one instance.

This topic is 1605 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 there,

I currently work on a voxel engine similar to minecraft. So far i've been using instancing to render cubes with a very naive Update() method i would optimise when my FPS gets too low. Well ... it's too low now :-)

 

So far, the only job of the Update() method is to set a flag on the cube instance the mouse cursor is over, the shader picks that flag up, and render the cube in a highlighted way. A friend told me i should probably draw a slightly larger already highlighted cube on top of the instanced one to be more efficient, and i probably will, but that's beyond the point here, as i want to know what is technically wrong with my code.

 

This is my old code setting the flag for the highlighted cube :

void Chunk::Update(float dt)
{
	BuildShadowTransform();

	ID3D11Device* device = Graphics::GetInstance().GetDevice();
	ID3D11DeviceContext* deviceContext = Graphics::GetInstance().GetImmediateContext();
	D3D11_MAPPED_SUBRESOURCE mappedData; 
	deviceContext->Map(mInstanceBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedData);
	InstanceData* dataView = reinterpret_cast<InstanceData*>(mappedData.pData);
	UINT i = 0;
	for(UINT x = 0 ; x < Chunk::SIZE ; x++)
	{
		for(UINT y = 0 ; y < Chunk::SIZE ; y++)
		{
			for(UINT z = 0 ; z < Chunk::SIZE ; z++)
			{
				if(mBlocks[x][y][z].IsActive())
				{
					InstanceData data;
					data.IsSelected = XMFLOAT2(0.0f, 0.0f);
					if(mIsBlockHighlighted == true)
					{						
						if(x == mHighlightedBlockPosition.x && y == mHighlightedBlockPosition.y && z == mHighlightedBlockPosition.z)
						{
							data.IsSelected = XMFLOAT2(1.0f, 1.0f);
						}						
					}
					
					data.World = XMFLOAT3(x, y, z);
					dataView[i++] = data;
				}
			}
		}
	}

	deviceContext->Unmap(mInstanceBuffer, 0);
}

As you can see, i am basically updating the ENTIRE instancing stream for all active blocks, when i actually only want to update one of them.

So i changed the code to this :

	ID3D11DeviceContext* deviceContext = Graphics::GetInstance().GetImmediateContext();
	D3D11_MAPPED_SUBRESOURCE mappedData; 
	deviceContext->Map(mInstanceBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedData);
	InstanceData* dataView = reinterpret_cast<InstanceData*>(mappedData.pData);
	if(mIsBlockHighlighted)
	{	
		int index = (mHighlightedBlockPosition.x * (Chunk::SIZE * Chunk::SIZE)) + (mHighlightedBlockPosition.y * Chunk::SIZE) + mHighlightedBlockPosition.z;
		dataView[index].IsSelected = XMFLOAT2(1.0f, 1.0f);;
	}

	deviceContext->Unmap(mInstanceBuffer, 0);
	mIsUpdateRequired = false;

Here i'm only updating the instance i am interested in, but now my cubes flicker like crazy.

Hell, even if i remove the whole if block, it still flickers.

 

How can i update a single instance of the stream ?

 

Thanks !

Share this post


Link to post
Share on other sites

When you use D3D11_MAP_WRITE_DISCARD you're telling the graphic's driver that you're going to fill in the entire buffer and original contents can be destroyed/wiped/deleted. In most cases, the driver will allocate you an entirely new and fresh piece of memory whose contents are undefined, you should assume the memory you're given to write to is full of random junk. If, by chance, the driver gives you a piece of memory where your original data is already there, it's a fluke, highly manufacturer/driver specific and should not be relied on at all, this might explain why it sometimes works and sometimes doesn't.

 

For that reason, your second code block is wrong as you're only filling in one block's worth of data and leaving all other blocks with undefined memory contents. If you want to only update a small subsection of the buffer, use D3D11_MAP_WRITE or D3D11_MAP_READ_WRITE_NO_OVERWRITE if you're not concerned about synchronisation with the GPU.

Edited by ajmiles

Share this post


Link to post
Share on other sites

When you map with D3D11_MAP_WRITE_DISCARD you lose all previous contents of that resource. So if you use it, you need to fill up the entire buffer again.

If your common use case is to only change a small part of the buffer at a time, then you can try using a two-buffer approach. Create your primary instancing buffer as D3D11_USAGE_DEFAULT, and then create a secondary buffer with D3D11_USAGE_STAGING. Then when you want to change the buffer contents, map your staging buffer and then use CopySubresourceRegion to copy some or all of the staging buffer to your primary buffer.

Edited by MJP

Share this post


Link to post
Share on other sites

If your common use case is to only change a small part of the buffer at a time, then you can try using a two-buffer approach. Create your primary instancing buffer as D3D11_USAGE_IMMUTABLE, and then create a secondary buffer with D3D11_USAGE_STAGING. Then when you want to change the buffer contents, map your staging buffer and then use CopySubresourceRegion to copy some or all of the staging buffer to your primary buffer.


Wait. Copying to an immutable ? I've just tried that with D3D 11.0 and I get this:

[tt]ID3D11DeviceContext::CopySubresourceRegion: Cannot invoke CopySubresourceRegion when the destination Resource was created with the D3D11_USAGE_IMMUTABLE Usage.[/tt]

What am I missing ? Is this some D3D 11.1 or higher feature ?

Share this post


Link to post
Share on other sites

 

If your common use case is to only change a small part of the buffer at a time, then you can try using a two-buffer approach. Create your primary instancing buffer as D3D11_USAGE_IMMUTABLE, and then create a secondary buffer with D3D11_USAGE_STAGING. Then when you want to change the buffer contents, map your staging buffer and then use CopySubresourceRegion to copy some or all of the staging buffer to your primary buffer.


Wait. Copying to an immutable ? I've just tried that with D3D 11.0 and I get this:

[tt]ID3D11DeviceContext::CopySubresourceRegion: Cannot invoke CopySubresourceRegion when the destination Resource was created with the D3D11_USAGE_IMMUTABLE Usage.[/tt]

What am I missing ? Is this some D3D 11.1 or higher feature ?

 

You're not missing anything, I messed up. That should have been D3D11_USAGE_DEFAULT instead of D3D11_USAGE_IMMUTABLE.

Edited by MJP

Share this post


Link to post
Share on other sites

This topic is 1605 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.

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