D3D10 InputLayout

Started by
1 comment, last by Dragon_Strike 15 years, 10 months ago
im havin some big issues in implementing a good abstraction for d3d10 inputlayouts... it seems rly hard since u have to keep track of which pass and what shader is rendered etc... and i havent yet found any good way to implement it... iv tried creating an "input layout" class which i "bind" too vertex buffers... and have a cache of inputlayouts for different shaders... ive also tried to have a cache of inputlayouts in the shader class... but nothing seems to work well... how do u guys solve it? EDIT: this is one of my tries... its unifinished... i stopped since its just messy and bad

#include "D3D10InputLayout.hpp"
#include "D3D10Shader.hpp"
#include "D3D10Exception.hpp"

#include <boost/cast.hpp>

#include <iostream>

namespace drone
{
	namespace d3d10
	{

struct InputLayout::Implementation
{
	Implementation(RenderDevicePtr pDevice) : pDevice_(pDevice), locked(false){}

	void Add(LPCSTR semantic, 
		 int semanticindex, 
		 UINT format, 
		 UINT slot, 
		 UINT offset, 
		 UINT slotclass, 
		 UINT steprate)
	{

		assert(!locked && "Cannot change inputlayout once used.");

		D3D10_INPUT_ELEMENT_DESC layout[] =
		{
			{semantic, semanticindex, static_cast<DXGI_FORMAT>(format), slot, offset, static_cast<D3D10_INPUT_CLASSIFICATION>(slotclass), steprate },  
		};
		inputElementDesc_.push_back(layout[0]);
	}

	void Bind(graphics::IShaderPtr shader)
	{
		locked = true;
		ID3D10InputLayoutPtr layout;
		ShaderPtr d3d10shader = boost::shared_polymorphic_cast<Shader>(shader);

		if (associations_.empty() || activeLayout_->first != shader->Hash())
		{				    
			AssocMap::const_iterator i = associations_.find(shader->Hash());
			if (i != associations_.end())		
			{
			  activeLayout_ = i;	
			}
			else
			{
				D3D10_PASS_DESC PassDesc;
				d3d10shader->GetTech()->GetPassByIndex(d3d10shader->ActivePass())->GetDesc(&PassDesc);

				HRESULT hr = pDevice_->Device()->CreateInputLayout(&inputElementDesc_[0], 
																  inputElementDesc_.size(), 
																  PassDesc.pIAInputSignature, 
																  PassDesc.IAInputSignatureSize, 
																  &layout);

				if(FAILED(hr))
					throw Exception(L"Failed to Create Input layout", hr);

				activeLayout_ = associations_.insert(AssocMap::value_type(d3d10shader->Hash(), layout)).first;
			}			
		}
		pDevice_->Device()->IASetInputLayout(activeLayout_->second.get());
	}

	bool locked;
	
	RenderDevicePtr pDevice_;

	typedef std::map<int, ID3D10InputLayoutPtr> AssocMap;
	AssocMap associations_;

	AssocMap::const_iterator activeLayout_;

	std::vector<D3D10_INPUT_ELEMENT_DESC> inputElementDesc_;
};

InputLayout::InputLayout(const std::string& id, RenderDevicePtr pDevice) : IInputLayout(id), pImpl_(new Implementation(pDevice))
{
	std::wcout <<">> Created InputLayout: \"" << id.c_str() << "\". (D3D10)\n";
}

void InputLayout::Add(LPCSTR semantic, 
		 int semanticindex, 
		 UINT format, 
		 UINT slot, 
		 UINT offset, 
		 UINT slotclass, 
		 UINT steprate)
{
	pImpl_->Add(semantic, semanticindex, format, slot, offset, slotclass, steprate);
}

void InputLayout::Bind(graphics::IShaderPtr shader)
{
	pImpl_->Bind(shader);
}

int InputLayout::NumElements()
{
	return pImpl_->inputElementDesc_.size();
}

	}
}


Advertisement
It might help to understand why D3D10 introduced input layouts and let that guide the way you design your abstractions. The reason D3D10 introduces a tight coupling between input layouts and shader input signatures is to reduce the cost of draw calls (reducing the cost of draw calls was a major design goal for D3D10). In D3D9 you can use any vertex declaration with any vertex shader provided the input semantics for the vertex shader are a strict subset of the semantics available in the vertex declaration. This is convenient and makes your life easier as a programmer but comes at an unavoidable performance cost in the runtime and driver which have to do a fairly significant amount of work at draw call time to bind the two together. Drivers can do all kinds of caching behind the scenes to try and hide or reduce some of the cost but because of the API design there's a limit to how much you can do on the application side to make things more efficient.

If you try to design your abstraction to mimic the D3D9 API then you'll have to do most of the work that the drivers do in D3D9 yourself, which basically comes down to maintaining your own 'vertex declaration' like class and a cache of on-demand generated input layouts created/looked up at draw time for each combination of vertex declaration and shader input signature your app uses. Of course if you do things this way you lose the potential performance benefits of D3D10's input layout model.

If you want to get the full performance benefits of using D3D10 then a better approach is to try and arrange your app so that you can create the Input Layouts you need up front based on the particular needs of your app. Most apps don't really need the full generality of being able to bind any compatible vertex declaration and vertex shader together at runtime. In reality most models you draw will use one of a fairly small set of vertex declarations and only be drawn with one of a fairly small set of compatible vertex shaders. It should be possible to figure this out in advance as part of your model export pipeline for general assets, or hard coded for dynamic data generated by code.

Game Programming Blog: www.mattnewport.com/blog

thx.. that helped alot

This topic is closed to new replies.

Advertisement