Setting Constants Using ID3D11ShaderReflection

Started by
4 comments, last by KoldGames 10 years ago

Hello all! I got to the point where I am tired of hardcoding shaders and decided that it was now time to look into using ID3D11ShaderReflection to get data from the shader and set my shader's constant buffer using that data. I have a simple system that loads up shaders and gets it's data.

H:


enum NXDeviceShaderType
		{
			NXDST_VERTEX,
			NXDST_PIXEL,
			NXDTS_COMPUTE,
			NXDST_GEOMETRY,
			NXDST_DOMAIN,
			NXDST_HULL,
		};

		struct NXShaderVariable
		{
			D3D11_SHADER_VARIABLE_DESC Description;
			D3D11_SHADER_TYPE_DESC Type;
		};

		struct NXShaderConstantBuffer
		{
			ID3D11Buffer* Data = nullptr;
			D3D11_SHADER_BUFFER_DESC Description;
			vector<NXShaderVariable> Variables;

			bool Create();

			~NXShaderConstantBuffer()
			{
				if (Data)
				{
					Data = nullptr;
				}

				Variables.clear();
			}
		};

		class NXDeviceShader
		{
			public:
				~NXDeviceShader()
				{
					if (Reflection)
					{
						Reflection->Release();
						Reflection = nullptr;
					}

					ConstantBuffers.clear();
				};

				D3D_FEATURE_LEVEL MinimumFeatureLevel;

				ID3D11ShaderReflection* Reflection = nullptr;

				vector<NXShaderConstantBuffer> ConstantBuffers;

				bool Enabled = true;

				virtual const NXDeviceShaderType Type() = 0;
				virtual void Apply() = 0;
				virtual void Dispose() = 0;

				NXShaderVariable* GetVariableByName(string name);

				/*bool Set(string constantName, DirectX::XMMATRIX& value)
				{
					return UpdateConstant(0, constantName, &value);
				}

				void Set(string constantName, int value);
				void Set(string constantName, float value);
				void Set(string constantName, double value);

				void Set(string constantName, XMVECTOR& value);
				void Set(string constantName, XMFLOAT2 value);
				void Set(string constantName, XMFLOAT3 value);
				void Set(string constantName, XMFLOAT4 value);

				void Set(string constantName, XMMATRIX& value);*/

			private:
		};

		class NXVertexShader : public NXDeviceShader
		{	
			public:
				const NXDeviceShaderType Type()
				{
					return NXDST_VERTEX;
				}

				void Apply();
				void Dispose();

				ID3D11VertexShader* Shader = nullptr;
				ID3D11InputLayout* Layout = nullptr;
		};

		class NXPixelShader : public NXDeviceShader
		{
			public:
				const NXDeviceShaderType Type()
				{
					return NXDST_PIXEL;
				}

				void Apply();
				void Dispose();

				ID3D11PixelShader* Shader = nullptr;
		};

		class NXComputeShader : public NXDeviceShader
		{
			public:
				const NXDeviceShaderType Type()
				{
					return NXDTS_COMPUTE;
				}

				void Apply();
				void Dispose();

				ID3D11ComputeShader* Shader = nullptr;
		};

		class NXGeometryShader : public NXDeviceShader
		{
			public:
				const NXDeviceShaderType Type()
				{
					return NXDST_GEOMETRY;
				}

				void Apply();
				void Dispose();

				ID3D11GeometryShader* Shader = nullptr;
		};

		class NXDomainShader : public NXDeviceShader
		{
			public:
				const NXDeviceShaderType Type()
				{
					return NXDST_DOMAIN;
				}

				void Apply();
				void Dispose();

				ID3D11DomainShader* Shader = nullptr;
		};

		class NXHullShader : public NXDeviceShader
		{
			public:
				const NXDeviceShaderType Type()
				{
					return NXDST_HULL;
				}

				void Apply();
				void Dispose();

				ID3D11HullShader* Shader = nullptr;
		};

		bool NXGetShaderInformation(NXDeviceShader* shader);
		bool NXCreateVertexShader(string& file, NXVertexShader* shader);
		bool NXCreatePixelShader(string& file, NXPixelShader* shader);
		bool NXCreateComputeShader(string& file, NXComputeShader* shader);
		bool NXCreateGeometryShader(string& file, NXGeometryShader* shader);
		bool NXCreateDomainShader(string& file, NXDomainShader* shader);
		bool NXCreateHullShader(string& file, NXHullShader* shader);

		class NXShader
		{
			public:
				NXShader(){}
				NXShader(string vsFileName, string psFileName, string csFileName, string gsFileName, string dsFileName, string hsFileName);
				NXShader(string vsFileName, string psFileName, string csFileName, string gsFileName, string dsFileName);
				NXShader(string vsFileName, string psFileName, string csFileName, string gsFileName);
				NXShader(string vsFileName, string psFileName, string csFileName);
				NXShader(string vsFileName, string psFileName);

				void Apply();
				void Dispose();

				int ID = 0;

				NXVertexShader* VertexShader = nullptr;
				NXPixelShader* PixelShader = nullptr;
				NXComputeShader* ComputeShader = nullptr;
				NXGeometryShader* GeometryShader = nullptr;
				NXDomainShader* DomainShader = nullptr;
				NXHullShader* HullShader = nullptr;
		};

CPP:


bool NXShaderConstantBuffer::Create()
		{
			D3D11_BUFFER_DESC desc;
			desc.ByteWidth = Description.Size;
			desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
			desc.MiscFlags = 0;
			desc.StructureByteStride = 0;
			desc.Usage = D3D11_USAGE_DYNAMIC;
			desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;

			if (FAILED(Core::NXGPU::CreateBuffer(&desc, 0, &Data)))
				return false;

			return true;
		}

		//----------------------------------------------------//

		bool NXGetShaderInformation(NXDeviceShader* shader)
		{
			if (shader->Reflection == nullptr)
				return false;

			D3D11_SHADER_DESC shaderDesc;
			shader->Reflection->GetDesc(&shaderDesc);

			shader->Reflection->GetMinFeatureLevel(&shader->MinimumFeatureLevel);

			/*for (UINT i = 0; i < shaderDesc.InputParameters; ++i)
			{
				D3D11_SIGNATURE_PARAMETER_DESC input_desc;
				shader->Reflection->GetInputParameterDesc(i, &input_desc);
				shader->InputParameters.push_back(input_desc);
			}

			for (UINT i = 0; i < shaderDesc.OutputParameters; ++i)
			{
				D3D11_SIGNATURE_PARAMETER_DESC output_desc;
				shader->Reflection->GetInputParameterDesc(i, &output_desc);
				shader->OutputParameters.push_back(output_desc);
			}*/
			
			for (UINT i = 0; i < shaderDesc.ConstantBuffers; ++i)
			{
				NXShaderConstantBuffer constantBuffer;
				ID3D11ShaderReflectionConstantBuffer* buffer = shader->Reflection->GetConstantBufferByIndex(i);
				
				if (FAILED(buffer->GetDesc(&constantBuffer.Description)))
					return false;
				
				for (UINT v = 0; v < constantBuffer.Description.Variables; ++v)
				{
					NXShaderVariable shaderVariable;

					ID3D11ShaderReflectionVariable* variable = buffer->GetVariableByIndex(v);
					if (FAILED(variable->GetDesc(&shaderVariable.Description)))
						return false;

					ID3D11ShaderReflectionType* type = variable->GetType();
					if (FAILED(type->GetDesc(&shaderVariable.Type)))
						return false;
					
					constantBuffer.Variables.push_back(shaderVariable);
				}

				if (!constantBuffer.Create())
					return false;
				
				shader->ConstantBuffers.push_back(constantBuffer);
			}

			return true;
		}

		bool NXCreateVertexShader(string& file, NXVertexShader* shader)
		{
			HRESULT result;

			IO::NXBinaryReader vsReader(file);
			vector<uint8_t> vsCode;

			if (vsReader.Exists())
			{
				vsReader.ReadFileToBuffer(vsCode);

				result = Core::NXGPU::GetDevice()->CreateVertexShader(vsCode.data(), vsReader.GetSize(), nullptr, &shader->Shader);
				if (FAILED(result))
				{
					vsCode.clear();
					vsReader.Close();
					shader->Dispose();
					MessageBox(nullptr, L"Failed To Create Vertex Shader.", L"Error", MB_OK);
					return false;
				}

				result = D3DReflect(vsCode.data(), vsReader.GetSize(), IID_ID3D11ShaderReflection, (void**) &shader->Reflection);
				if (FAILED(result))
				{
					vsCode.clear();
					vsReader.Close();
					shader->Dispose();
					MessageBox(nullptr, L"Failed To Create Vertex Shader Reflection.", L"Error", MB_OK);
					return false;
				}
			}
			else
			{
				vsReader.Close();
				shader->Dispose();
				MessageBox(nullptr, L"Could not find vertex shader.", L"Error", MB_OK);
				return false;
			}

			result = Core::NXGPU::GetDevice()->CreateInputLayout(Graphics::NXVertex::InputElements, Graphics::NXVertex::InputElementCount, vsCode.data(),
				vsReader.GetSize(), &shader->Layout);

			vsCode.clear();
			vsReader.Close();
			if (FAILED(result))
			{
				shader->Dispose();
				MessageBox(nullptr, L"Failed To Create Input Layout.", L"Error", MB_OK);
				return false;
			}

			result = NULL;

			return NXGetShaderInformation(shader);
		}

		bool NXCreatePixelShader(string& file, NXPixelShader* shader)
		{
			HRESULT result;
			IO::NXBinaryReader psReader(file);
			vector<uint8_t> psCode;
			if (psReader.Exists())
			{
				psReader.ReadFileToBuffer(psCode);
				result = Core::NXGPU::GetDevice()->CreatePixelShader(psCode.data(), psReader.GetSize(), nullptr, &shader->Shader);
				
				if (FAILED(result))
				{
					shader->Dispose();
					MessageBox(nullptr, L"Failed To Create Pixel Shader.", L"Error", MB_OK);
					return false;
				}

				result = D3DReflect(psCode.data(), psReader.GetSize(), IID_ID3D11ShaderReflection, (void**) &shader->Reflection);

				psCode.clear();
				psReader.Close();
				if (FAILED(result))
				{
					shader->Dispose();
					MessageBox(nullptr, L"Failed To Create Pixel Shader Reflection.", L"Error", MB_OK);
					return false;
				}
			}
			else
			{
				psReader.Close();
				shader->Dispose();
				MessageBox(nullptr, L"Could not find pixel shader.", L"Error", MB_OK);
				return false;
			}

			result = NULL;

			return NXGetShaderInformation(shader);
		}

		bool NXCreateComputeShader(string& file, NXComputeShader* shader)
		{
			HRESULT result;
			IO::NXBinaryReader csReader(file);
			vector<uint8_t> csCode;

			if (csReader.Exists())
			{
				csReader.ReadFileToBuffer(csCode);
				result = Core::NXGPU::GetDevice()->CreateComputeShader(csCode.data(), csReader.GetSize(), nullptr, &shader->Shader);

				if (FAILED(result))
				{
					shader->Dispose();
					MessageBox(nullptr, L"Failed To Create Compute Shader.", L"Error", MB_OK);
					return false;
				}

				result = D3DReflect(csCode.data(), csReader.GetSize(), IID_ID3D11ShaderReflection, (void**) &shader->Reflection);

				csCode.clear();
				csReader.Close();
				if (FAILED(result))
				{
					shader->Dispose();
					MessageBox(nullptr, L"Failed To Create Compute Shader Reflection.", L"Error", MB_OK);
					return false;
				}
			}
			else
			{
				csReader.Close();
				shader->Dispose();
				MessageBox(nullptr, L"Could not find compute shader.", L"Error", MB_OK);
				return false;
			}

			result = NULL;

			return NXGetShaderInformation(shader);
		}

		bool NXCreateGeometryShader(string& file, NXGeometryShader* shader)
		{
			HRESULT result;
			IO::NXBinaryReader gsReader(file);
			vector<uint8_t> gsCode;

			if (gsReader.Exists())
			{
				gsReader.ReadFileToBuffer(gsCode);
				result = Core::NXGPU::GetDevice()->CreateGeometryShader(gsCode.data(), gsReader.GetSize(), nullptr, &shader->Shader);

				if (FAILED(result))
				{
					shader->Dispose();
					MessageBox(nullptr, L"Failed To Create Geometry Shader.", L"Error", MB_OK);
					return false;
				}

				result = D3DReflect(gsCode.data(), gsReader.GetSize(), IID_ID3D11ShaderReflection, (void**) &shader->Reflection);

				gsCode.clear();
				gsReader.Close();
				if (FAILED(result))
				{
					shader->Dispose();
					MessageBox(nullptr, L"Failed To Create Geometry Shader Reflection.", L"Error", MB_OK);
					return false;
				}
			}
			else
			{
				gsReader.Close();
				shader->Dispose();
				MessageBox(nullptr, L"Could not find geometry shader.", L"Error", MB_OK);
				return false;
			}

			result = NULL;

			return NXGetShaderInformation(shader);
		}

		bool NXCreateDomainShader(string& file, NXDomainShader* shader)
		{
			HRESULT result;
			IO::NXBinaryReader dsReader(file);
			vector<uint8_t> dsCode;

			if (dsReader.Exists())
			{
				dsReader.ReadFileToBuffer(dsCode);
				result = Core::NXGPU::GetDevice()->CreateDomainShader(dsCode.data(), dsReader.GetSize(), nullptr, &shader->Shader);

				if (FAILED(result))
				{
					shader->Dispose();
					MessageBox(nullptr, L"Failed To Create Domain Shader.", L"Error", MB_OK);
					return false;
				}

				result = D3DReflect(dsCode.data(), dsReader.GetSize(), IID_ID3D11ShaderReflection, (void**) &shader->Reflection);

				dsCode.clear();
				dsReader.Close();
				if (FAILED(result))
				{
					shader->Dispose();
					MessageBox(nullptr, L"Failed To Create Domain Shader Reflection.", L"Error", MB_OK);
					return false;
				}
			}
			else
			{
				dsReader.Close();
				shader->Dispose();
				MessageBox(nullptr, L"Could not find domain shader.", L"Error", MB_OK);
				return false;
			}

			result = NULL;

			return NXGetShaderInformation(shader);
		}

		bool NXCreateHullShader(string& file, NXHullShader* shader)
		{
			HRESULT result;
			IO::NXBinaryReader hsReader(file);
			vector<uint8_t> hsCode;

			if (hsReader.Exists())
			{
				hsReader.ReadFileToBuffer(hsCode);
				result = Core::NXGPU::GetDevice()->CreateHullShader(hsCode.data(), hsReader.GetSize(), nullptr, &shader->Shader);

				if (FAILED(result))
				{
					shader->Dispose();
					MessageBox(nullptr, L"Failed To Create Hull Shader.", L"Error", MB_OK);
					return false;
				}

				result = D3DReflect(hsCode.data(), hsReader.GetSize(), IID_ID3D11ShaderReflection, (void**) &shader->Reflection);

				hsCode.clear();
				hsReader.Close();
				if (FAILED(result))
				{
					shader->Dispose();
					MessageBox(nullptr, L"Failed To Create Hull Shader Reflection.", L"Error", MB_OK);
					return false;
				}
			}
			else
			{
				hsReader.Close();
				shader->Dispose();
				MessageBox(nullptr, L"Could not find hull shader.", L"Error", MB_OK);
				return false;
			}

			return NXGetShaderInformation(shader);
		}

		//----------------------------------------------------//

		NXShaderVariable* NXDeviceShader::GetVariableByName(string name)
		{
			for (unsigned int i = 0; i < ConstantBuffers.size(); ++i)
			{
				for (unsigned int v = 0; v < ConstantBuffers[i].Variables.size(); ++v)
				{
					if (ConstantBuffers[i].Variables[v].Description.Name == name)
					{
						return &ConstantBuffers[i].Variables[v];
					}
				}
			}

			return nullptr;
		}

		void NXVertexShader::Apply()
		{
			if (Shader && Layout && Enabled)
			{
				Core::NXGPU::GetDeviceContext()->IASetInputLayout(Layout);
				Core::NXGPU::SetVertexShader(Shader, nullptr, 0);
			}
		}

		void NXVertexShader::Dispose()
		{
			if (Layout)
			{
				Layout->Release();
				Layout = nullptr;
			}

			if (Shader)
			{
				Shader->Release();
				Shader = nullptr;
			}
		}

		void NXDomainShader::Apply()
		{
			if (Shader && Enabled)
			{
				Core::NXGPU::SetDomainShader(Shader, nullptr, 0);
			}
		}

		void NXDomainShader::Dispose()
		{
			if (Shader)
			{
				Shader->Release();
				Shader = nullptr;
			}
		}

		void NXHullShader::Apply()
		{
			if (Shader && Enabled)
			{
				Core::NXGPU::SetHullShader(Shader, nullptr, 0);
			}
		}

		void NXHullShader::Dispose()
		{
			if (Shader)
			{
				Shader->Release();
				Shader = nullptr;
			}
		}

		void NXGeometryShader::Apply()
		{
			if (Shader && Enabled)
			{
				Core::NXGPU::SetGeometryShader(Shader, nullptr, 0);
			}
		}

		void NXGeometryShader::Dispose()
		{
			if (Shader)
			{
				Shader->Release();
				Shader = nullptr;
			}
		}

		void NXPixelShader::Apply()
		{
			if (Shader && Enabled)
			{
				Core::NXGPU::SetPixelShader(Shader, nullptr, 0);
			}
		}

		void NXPixelShader::Dispose()
		{
			if (Shader)
			{
				Shader->Release();
				Shader = nullptr;
			}
		}

		void NXComputeShader::Apply()
		{
			if (Shader && Enabled)
			{
				Core::NXGPU::SetComputeShader(Shader, nullptr, 0);
			}
		}

		void NXComputeShader::Dispose()
		{
			if (Shader)
			{
				Shader->Release();
				Shader = nullptr;
			}
		}

		//----------------------------------------------------//

		NXShader::NXShader(string vsFileName, string psFileName, string csFileName, string gsFileName, string dsFileName, string hsFileName)
		{
			if (vsFileName != "")
			{
				VertexShader = new NXVertexShader();
				if (!NXCreateVertexShader(vsFileName, VertexShader))
				{
					Dispose();
					return;
				}
			}

			if (psFileName != "")
			{
				PixelShader = new NXPixelShader();
				if (!NXCreatePixelShader(psFileName, PixelShader))
				{
					Dispose();
					return;
				}
			}

			if (csFileName != "")
			{
				ComputeShader = new NXComputeShader();
				if (!NXCreateComputeShader(csFileName, ComputeShader))
				{
					Dispose();
					return;
				}
			}

			if (gsFileName != "")
			{
				GeometryShader = new NXGeometryShader();
				if (!NXCreateGeometryShader(gsFileName, GeometryShader))
				{
					Dispose();
					return;
				}
			}

			if (dsFileName != "")
			{
				DomainShader = new NXDomainShader();
				if (!NXCreateDomainShader(dsFileName, DomainShader))
				{
					Dispose();
					return;
				}
			}

			if (hsFileName != "")
			{
				HullShader = new NXHullShader();
				if (!NXCreateHullShader(hsFileName, HullShader))
				{
					Dispose();
					return;
				}
			}
		}

		NXShader::NXShader(string vsFileName, string psFileName, string csFileName, string gsFileName, string dsFileName) : NXShader(vsFileName, psFileName, csFileName, gsFileName, dsFileName, ""){}
		NXShader::NXShader(string vsFileName, string psFileName, string csFileName, string gsFileName) : NXShader(vsFileName, psFileName, csFileName, gsFileName, "", ""){}
		NXShader::NXShader(string vsFileName, string psFileName, string csFileName) : NXShader(vsFileName, psFileName, csFileName, "", "", ""){}
		NXShader::NXShader(string vsFileName, string psFileName) : NXShader(vsFileName, psFileName, "", "", "", ""){}

		void NXShader::Apply()
		{
			if (VertexShader)
				VertexShader->Apply();

			if (PixelShader)
				PixelShader->Apply();

			if (ComputeShader)
				ComputeShader->Apply();
			
			if (GeometryShader)
				GeometryShader->Apply();
			
			if (DomainShader)
				DomainShader->Apply();
			
			if (HullShader)
				HullShader->Apply();
		}

		void NXShader::Dispose()
		{
			if (VertexShader)
			{
				VertexShader->Dispose();
				delete VertexShader;
				VertexShader = nullptr;
			}

			if (PixelShader)
			{
				PixelShader->Dispose();
				delete PixelShader;
				PixelShader = nullptr;
			}

			if (ComputeShader)
			{
				ComputeShader->Dispose();
				delete ComputeShader;
				ComputeShader = nullptr;
			}

			if (GeometryShader)
			{
				GeometryShader->Dispose();
				delete GeometryShader;
				GeometryShader = nullptr;
			}

			if (DomainShader)
			{
				DomainShader->Dispose();
				delete DomainShader;
				DomainShader = nullptr;
			}

			if (HullShader)
			{
				HullShader->Dispose();
				delete HullShader;
				HullShader = nullptr;
			}
		}

Now that I have the data, I would like to know how the correct way to upload the data to the constant buffer. I've searched on Google and couldn't find too much information on this. Any help? Thanks! :)

Advertisement

Hello all! I got it working but I am now having a problem. In my test shader, my constant buffer is setup like this:


cbuffer Data : register(cb0)
{
    matrix worldMatrix; //Offset: 0
    matrix viewMatrix; //Offset: 64
    matrix projectionMatrix; //Offset: 128
}

And I currently set my constants like this:


bool NXDeviceShader::Set(int cb, string constantName, void* value)
		{
			NXShaderVariable* variable = GetVariableByName(constantName);

			if (variable == nullptr)
				return false;

			D3D11_MAPPED_SUBRESOURCE resource;
			resource.pData = nullptr; resource.DepthPitch = 0; resource.RowPitch = 0;

			if (FAILED(Core::NXGPU::Map(ConstantBuffers[cb].Data, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource)))
				return false;

			memcpy((unsigned char*) resource.pData + variable->Description.StartOffset, value, variable->Description.Size);

			Core::NXGPU::Unmap(ConstantBuffers[cb].Data, 0);

			variable = nullptr;

			SetConstantBuffer(cb, ConstantBuffers[cb].Data);

			return true;
		}

This works fine, but I get input lag based on which order I set the parameters in. If I move my mouse to the right to change my looking direction, after 2 seconds, it moves over. It's weird. Setting the parameters in this order gives me a lot of input lag.


XMMATRIX world = XMMatrixIdentity();
XMMATRIX view = camera.GetViewAsMatrix();
XMMATRIX proj = camera.GetProjectionAsMatrix();

world = XMMatrixTranspose(world);
view = XMMatrixTranspose(view);
proj = XMMatrixTranspose(proj);

	shader.VertexShader->Set(0, "worldMatrix", &world);
	shader.VertexShader->Set(0, "viewMatrix", &view);
	shader.VertexShader->Set(0, "projectionMatrix", &proj);

shader.Apply();
model.Render();

While setting the parameters in this order gives a lot less input lag:


shader.VertexShader->Set(0, "worldMatrix", &world);
shader.VertexShader->Set(0, "projectionMatrix", &proj);
shader.VertexShader->Set(0, "viewMatrix", &view);

It's pretty weird. Any help?

I'm amazed that input lag is the only observable side effect of this.

If you look inside your set method, you'll see that you're discarding each time you Map. The sequence is therefore:

  • You map with discard which throws away the previous contents of the buffer.
  • You set one matrix.
  • You map with discard which throws away the previous contents of the buffer (NB: this is the matrix you've just set).
  • You set the next matrix.
  • Etc.

Of course that's not going to give you the result you want.

To be honest, I think you're trying to make this too general. Whether you like it or not, your program has to have some degree of knowledge of what goes into your cbuffers; the API is designed that way and if you try to fight against the design of the API you invariably end up only causing yourself trouble.

If what you really want is something like the old Effects framework (and to me it looks like that's what you're shooting for) then you may not be aware that full source code for the D3D11 Effects framework shipped with the DirectX SDK - you'll find it in C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Samples\C++\Effects11, and you can compile it and use it instead.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

I'm amazed that input lag is the only observable side effect of this.

If you look inside your set method, you'll see that you're discarding each time you Map. The sequence is therefore:

  • You map with discard which throws away the previous contents of the buffer.
  • You set one matrix.
  • You map with discard which throws away the previous contents of the buffer (NB: this is the matrix you've just set).
  • You set the next matrix.
  • Etc.

Of course that's not going to give you the result you want.

To be honest, I think you're trying to make this too general. Whether you like it or not, your program has to have some degree of knowledge of what goes into your cbuffers; the API is designed that way and if you try to fight against the design of the API you invariably end up only causing yourself trouble.

If what you really want is something like the old Effects framework (and to me it looks like that's what you're shooting for) then you may not be aware that full source code for the D3D11 Effects framework shipped with the DirectX SDK - you'll find it in C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Samples\C++\Effects11, and you can compile it and use it instead.

Hey! Thanks for the help! I can't believe I missed that. I'm just so used to using D3D11_MAP_WRITE_DISCARD that it's a habit when mapping. Anyways, I've fixed it now. Thanks for the info! :) Seems to be working great now. I now get no input lag or performance problems. And I will remember what you said about the program needing to have some degree of knowledge of what goes into my cbuffers. Also, thanks for the suggestion! I will definitely check out Effects11 on codeplex and look at Hieroglyph 3. Thanks for your help, I really appreciate it. smile.png

Anyways, I've fixed it now by using D3D11_MAP_WRITE_NO_OVERWRITE. smile.png

I'm not sure I'd rely on that - the documentation for D3D11_MAP specifies that no-overwrite can't be used with a cbuffer, the exception being if you're on 11.1. In other words, if you need to target 11 or a lower feature level, this isn't guaranteed to work.

A solution that would work is to create your cbuffers with default usage and update them with UpdateSubresource, building a D3D11_BOX that offsets into the correct location within the buffer. Now, that's documented as being slower than mapping, but I've tested it and I believe that for small updates it's not going to be measurably so.

Otherwise you'll need to break your current setup and map the buffer once, write all 3 matrices, then unmap. To be honest that's what I'd probably do anyway, as on reflection you seem to be building a lot of boilerplate around what's really supposed to be a very simple operation.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

Anyways, I've fixed it now by using D3D11_MAP_WRITE_NO_OVERWRITE. smile.png

I'm not sure I'd rely on that - the documentation for D3D11_MAP specifies that no-overwrite can't be used with a cbuffer, the exception being if you're on 11.1. In other words, if you need to target 11 or a lower feature level, this isn't guaranteed to work.

A solution that would work is to create your cbuffers with default usage and update them with UpdateSubresource, building a D3D11_BOX that offsets into the correct location within the buffer. Now, that's documented as being slower than mapping, but I've tested it and I believe that for small updates it's not going to be measurably so.

Otherwise you'll need to break your current setup and map the buffer once, write all 3 matrices, then unmap. To be honest that's what I'd probably do anyway, as on reflection you seem to be building a lot of boilerplate around what's really supposed to be a very simple operation.

Hey! Yeah I saw that. What I decided to do instead was create an integer called StatusID in NXShaderVariable, then I created one Set method with a void* as the value parameter:


void NXDeviceShader::Set(string constantName, void* value)
		{
			NXShaderVariable* updateVariable = GetVariableByName(constantName);

			if (updateVariable == nullptr)
				return;

			updateVariable->StatusID = 1;

			if (updateVariable->Type.Class == D3D_SVC_MATRIX_COLUMNS || updateVariable->Type.Class == D3D_SVC_MATRIX_ROWS)
			{
				if ((XMMATRIX*) updateVariable->Value != (XMMATRIX*) value)
					updateVariable->Value = value;
			}
			else if (updateVariable->Type.Class == D3D_SVC_VECTOR)
			{
				if ((XMVECTOR*) updateVariable->Value != (XMVECTOR*) value)
					updateVariable->Value = value;
			}
			else if (updateVariable->Type.Class == D3D_SVC_SCALAR)
			{
				if (updateVariable->Type.Type == D3D_SVT_FLOAT)
				{
					if ((float*) updateVariable->Value != (float*) value)
						updateVariable->Value = value;
				}
				else if (updateVariable->Type.Type == D3D_SVT_INT)
				{
					if ((int*) updateVariable->Value != (int*) value)
						updateVariable->Value = value;
				}
			}
		}

And created a bool in NXShaderConstantBuffer called NeedsUpdate() which gets the StatusID of each variable and compares it to zero. If StatusID is not equal to zero, then it returns true. When Shader.Apply() is called, I call UpdateConstantBuffers which checks each buffer's NeedsUpdate() and if it returns true, then I update the constants and set each variable's StatusID back to zero. This method works great and I use D3D11_MAP_WRITE_DISCARD because I'm uploading the whole buffer at the same time rather than uploading individual variables.


bool NXDeviceShader::UpdateConstantBuffers()
		{
			for (unsigned int cb = 0; cb < ConstantBuffers.size(); ++cb)
			{
				if (ConstantBuffers[cb].NeedsUpdate())
				{
					if (ConstantBuffers[cb].Data)
					{
						D3D11_MAPPED_SUBRESOURCE resource;
						resource.pData = nullptr; resource.DepthPitch = resource.RowPitch = 0;

						if (FAILED(Core::NXGPU::Map(ConstantBuffers[cb].Data, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource)))
							return false;

						for (unsigned int i = 0; i < ConstantBuffers[cb].Variables.size(); ++i)
						{
							memcpy((unsigned char*) resource.pData + ConstantBuffers[cb].Variables[i].Description.StartOffset, ConstantBuffers[cb].Variables[i].Value, ConstantBuffers[cb].Variables[i].Description.Size);
							ConstantBuffers[cb].Variables[i].StatusID = 0;
						}

						Core::NXGPU::Unmap(ConstantBuffers[cb].Data, 0);

						SetConstantBuffer(cb, ConstantBuffers[cb].Data);
					}
				}
			}

			return true;
		}

biggrin.png Thanks for the help! I really appreciate it! smile.png

This topic is closed to new replies.

Advertisement