Flax Facts #5 – DirectX 12

Flax Facts #5 – DirectX 12

Next-Gen and DirectX 12

Did you hear about DirectX 12? It’s a next graphics API from Microsoft after DirectX 11 which is not a successor but kind of new low-level API. This gives a way more power and control over the GPU but does it?

Writing own GPU driver

First of all we’ve successfully implemented both DirectX 11 and DirectX 12 as our graphics backends. Both are running pretty well but we couldn’t write such a good driver code for DX12 and it’s slower than DX11. Why driver code? Because DirectX 12 is very low-level API and many obvious operations from DX11 have to be done manually. Which is not always resolved in a proper way. Probably in a future we will investigate it even more and improve the current implementation because I think DX12 can run faster than DX11.

Here is sample image comparison on DirectX 11 vs DirectX 12. Looks the same, huh?

directx

Resource Barriers

For those who also try to implement DirectX 12 I’ve got sample code to help you out with resource barriers and resources state tracking. For example if you have a texture used for downscaling (eg. blur/bloom effect or luminance map) you have to sample higher texture mip map and render it to the lower mip map (kind of copy). During this operation one subresource is in reading state while the other one is in writing state. Why it’s problematic? Because GPU is all about being asynchronous and multi-threaded. So in order to prevent data mismatches and race conditions we have to specify resource barriers. Those help GPU to synchronize work but are a little pain on DirectX 12 because we have to do it manually (DirectX 11 does it on it’s own). Basically here is a sample code which provides resource state tracking with per subresource changes so it helps with barriers errors and strange problems. It just works.


class ResourceStateDX12
{
	// Custom resource state used to indicate invalid state (usefull for debuging resource tracking issues).
	#define D3D12_RESOURCE_STATE_CORRUPT (D3D12_RESOURCE_STATES)-1
	
private:

	// The whole resource state (used only if _allSubresourcesSame is 1).
	D3D12_RESOURCE_STATES _resourceState : 31;

	// Set to 1 if _resourceState is valid. In this case, all subresources have the same state
	// Set to 0 if _subresourceState is valid. In this case, each subresources may have a different state
	uint32 _allSubresourcesSame : 1;
	
	// The per subresource state (used only if _allSubresourcesSame is 0).
	Array<D3D12_RESOURCE_STATES> _subresourceState;

public:
	
	ResourceStateDX12()
		: _resourceState(D3D12_RESOURCE_STATE_CORRUPT)
		, _allSubresourcesSame(true)
	{
	}

public:

	static bool IsTransitionNeeded(D3D12_RESOURCE_STATES currentState, D3D12_RESOURCE_STATES targeState)
	{
		return currentState != targeState
			&& ((currentState | targeState) != currentState || targeState == D3D12_RESOURCE_STATE_COMMON);
	}

public:

	void Initialize(uint32 subresourceCount, D3D12_RESOURCE_STATES initialState)
	{
		ASSERT(_subresourceState.IsEmpty() && subresourceCount > 0);
		
		_allSubresourcesSame = true;
		_resourceState = initialState;
		_subresourceState.Resize(subresourceCount, false);
	}

	bool IsInitializated() const
	{
		return _resourceState != D3D12_RESOURCE_STATE_CORRUPT || _subresourceState.HasElements();
	}

	void Release()
	{
		_resourceState = D3D12_RESOURCE_STATE_CORRUPT;
		_subresourceState.Resize(0);
	}

	bool AreAllSubresourcesSame() const
	{
		return _allSubresourcesSame;
	}

	bool CheckResourceState(D3D12_RESOURCE_STATES state) const
	{
		if (_allSubresourcesSame)
		{
			return state == _resourceState;
		}

		// All subresources must be individually checked
		const uint32 numSubresourceStates = _subresourceState.Count();
		for (uint32 i = 0; i < numSubresourceStates; i++)
		{
			if (_subresourceState[i] != state)
			{
				return false;
			}
		}

		return true;
	}

	D3D12_RESOURCE_STATES GetSubresourceState(uint32 subresourceIndex) const
	{
		if (_allSubresourcesSame)
		{
			return _resourceState;
		}

		ASSERT(subresourceIndex < static_cast(_subresourceState.Count()));
		return _subresourceState[subresourceIndex];
	}

	void SetResourceState(D3D12_RESOURCE_STATES state)
	{
		_allSubresourcesSame = 1;
		_resourceState = state;
	}

	void SetSubresourceState(uint32 subresourceIndex, D3D12_RESOURCE_STATES state)
	{
		// If setting all subresources, or the resource only has
		// a single subresource, set the per-resource state
		if (subresourceIndex == D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES
			|| _subresourceState.Count() <= 1)
		{
			SetResourceState(state);
		}
		else
		{
			ASSERT(subresourceIndex < static_cast(_subresourceState.Count()));

			// If state was previously tracked on a per-resource level,
			// then transition to per-subresource tracking
			if (_allSubresourcesSame)
			{
				const uint32 numSubresourceStates = _subresourceState.Count();
				for (uint32 i = 0; i < numSubresourceStates; i++)
				{
					_subresourceState[i] = _resourceState;
				}

				_allSubresourcesSame = 0;
			}

			_subresourceState[subresourceIndex] = state;
		}
	}
};

Hope it helps. Bye!


Wojciech Figat

Project Author

1 COMMENT
  • Atreix
    Reply

    Super

Leave a Reply

Your email address will not be published. Required fields are marked *