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

Recommended Posts

Hello! I am working on a class for my game engine called NXShader which will allow me to load my shaders easily like this:

NXShader(HWND*, WCHAR*, const string&, const string&);


The next thing I would like to add is the ability to dynamically set shader parameters if that makes any sense. Kind of similar to how shader parameters were handled in XNA:

Effect.Parameters["parameter"].SetValue(value);

I want to be able to do something like this with my NXShader class:

NXShader* shader = new NXShader(nullptr, L"Texture.fx", "VS", "PS");


But I am not actually sure about to do this. The class is currently set up like this:

struct MatrixBufferType
{
XMMATRIX World;
XMMATRIX View;
XMMATRIX Projection;
};

{
public:
NXShader(HWND*, WCHAR*, const string&, const string&);
HRESULT Initialize(HWND*);
void Dispose();

WCHAR* TargetProfile = L"_4_0";

WCHAR* Name;
ID3D11InputLayout* Layout;
ID3D11Buffer* MatrixBuffer;
};


Source:

#include "stdafx.h"
#include <d3dcompiler.h>
#include "NXTypes.h"

#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "d3dcompiler.lib")

namespace NexEngine
{
{
Layout = 0;
MatrixBuffer = 0;

HRESULT result;

result = Initialize(wnd);
if (FAILED(result))
return;
}

{
HRESULT result;
D3D11_BUFFER_DESC matrixBufferDesc;
ID3DBlob* pVSBlob = nullptr;
if (FAILED(result))
{
MessageBox(nullptr,
L"The FX file cannot be compiled.  Please run this executable from the directory that contains the FX file.", L"Error", MB_OK);
exit(0);
return result;
}

if (FAILED(result))
{
pVSBlob->Release();
MessageBox(nullptr, L"Failed To Create Vertex Shader.", L"Error", MB_OK);
return result;
}

ID3DBlob* pPSBlob = nullptr;
if (FAILED(result))
{
MessageBox(nullptr,
L"The FX file cannot be compiled.  Please run this executable from the directory that contains the FX file.", L"Error", MB_OK);
exit(0);
return result;
}

pPSBlob->Release();
if (FAILED(result))
{
MessageBox(nullptr, L"Failed To Create Pixel Shader.", L"Error", MB_OK);
return result;
}
pPSBlob = 0;

result = NXGPU::GetDevice()->CreateInputLayout(NXVertex::InputElements, NXVertex::InputElementCount, pVSBlob->GetBufferPointer(),
pVSBlob->GetBufferSize(), &Layout);

pVSBlob->Release();
if (FAILED(result))
{
MessageBox(nullptr, L"Failed To Create Input Layout.", L"Error", MB_OK);
exit(0);
return result;
}
pVSBlob = 0;

matrixBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
matrixBufferDesc.ByteWidth = sizeof(MatrixBufferType);
matrixBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
matrixBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
matrixBufferDesc.MiscFlags = 0;
matrixBufferDesc.StructureByteStride = 0;

result = NXGPU::GetDevice()->CreateBuffer(&matrixBufferDesc, NULL, &MatrixBuffer);
if (FAILED(result))
{
MessageBox(nullptr, L"Faield to Create Matrix Buffer", L"Error", MB_OK);
exit(0);
return result;
}

return S_OK;
}

{
HRESULT result;
D3D11_MAPPED_SUBRESOURCE mappedResource;
MatrixBufferType* dataPtr;
unsigned int bufferNumber;

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

result = NXGPU::GetDeviceContext()->Map(MatrixBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
if (FAILED(result))
{
return false;
}

dataPtr = (MatrixBufferType*) mappedResource.pData;

dataPtr->World = world;
dataPtr->View = view;
dataPtr->Projection = proj;

NXGPU::GetDeviceContext()->Unmap(MatrixBuffer, 0);

bufferNumber = 0;

NXGPU::GetDeviceContext()->VSSetConstantBuffers(bufferNumber, 1, &MatrixBuffer);

return true;
}

{
HRESULT result;
D3D11_MAPPED_SUBRESOURCE mappedResource;
MatrixBufferType* dataPtr;
unsigned int bufferNumber;

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

result = NXGPU::GetDeviceContext()->Map(MatrixBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
if (FAILED(result))
{
return false;
}

dataPtr = (MatrixBufferType*) mappedResource.pData;

dataPtr->World = world;
dataPtr->View = view;
dataPtr->Projection = proj;

NXGPU::GetDeviceContext()->Unmap(MatrixBuffer, 0);

bufferNumber = 0;

NXGPU::GetDeviceContext()->VSSetConstantBuffers(bufferNumber, 1, &MatrixBuffer);

return true;
}

{
if (MatrixBuffer)
{
MatrixBuffer->Release();
MatrixBuffer = 0;
}

if (Layout)
{
Layout->Release();
Layout = 0;
}

{
}

{
}

return;
}

{
HRESULT hr = S_OK;

#if defined( DEBUG ) || defined( _DEBUG )
// Set the D3DCOMPILE_DEBUG flag to embed debug information in the shaders.
// Setting this flag improves the shader debugging experience, but still allows
// the shaders to be optimized and to run exactly the way they will run in
// the release configuration of this program.
#endif

ID3DBlob* pErrorBlob;
hr = D3DCompileFromFile(szFileName, nullptr, nullptr, szEntryPoint, szShaderModel,
if (FAILED(hr))
{
if (pErrorBlob != nullptr)
OutputDebugStringA((char*) pErrorBlob->GetBufferPointer());
if (pErrorBlob) pErrorBlob->Release();
return hr;
}
if (pErrorBlob) pErrorBlob->Release();

return S_OK;
}
} 

I would like to remove the SetShaderParameter bool methods above, and replace them with something like this:

void SetParameter(const string&, float);
void SetParameter(const string&, int);
void SetParameter(const string&, XMMatrix*)
void SetParameter...etc.

This would really help me out. The class can currently compile and load Pixel/Vertex shaders correctly:

But I seem to limited to setting parameters by creating a bunch of constant buffers for each cbuffer in my shaders. Is there a way to do what I am trying to do in DX11? Thanks!

Edited by KoldGames

Share on other sites
Sure, there's a way. You could create a mapping from string to a structure member.

There's really no reason to go that route though. Event the DX9 effects framework essentially uses cbuffers on the back end, with the strings just being mappings.

You really shouldn't be using strings for this though, its not a particularly efficient idea.

Share on other sites

The way shader constants have changed from D3D9 to 11, and the fact that D3D, even in version 9, never really had per-shader constants (they were always global state, the old Effects framework just never exposed that well), means that you're probably better off separating your constants from your shaders, otherwise you've got an artificial coupling that's only going to hinder you as you go forward.

As an example, what would you propose to do if you had two different vertex shaders but they both needed to use the same matrices?  Your current design doesn't handle this requirement, instead forcing you to upload the matrices twice.  However, D3D itself doesn't (and never did) require you to do that.

If what you really want is just a D3D11 version of the old Effects framework (which it looks like is what you're designing towards), there is one provided with the DirectX SDK (I don't know about newer Windows SDK versions) in source-code-only form; you'll find it in C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Samples\C++\Effects11 and you can compile it and link to it.

Personally I've just got a much more lightweight shader class that just contains creation and destruction code for the types of shaders (and input layouts) I'm interested in, using a hash of the bytecode and a global shader cache to avoid creating the same object more than once, some simple state change management, and otherwise I handle cbuffer updates completely separately and via the raw API.

Edited by mhagain

Share on other sites

Hey, thanks for the replies!

Sure, there's a way. You could create a mapping from string to a structure member.

There's really no reason to go that route though. Event the DX9 effects framework essentially uses cbuffers on the back end, with the strings just being mappings.

You really shouldn't be using strings for this though, its not a particularly efficient idea.

What would you recommend to replace the strings? Is WCHAR or char good?

The way shader constants have changed from D3D9 to 11, and the fact that D3D, even in version 9, never really had per-shader constants (they were always global state, the old Effects framework just never exposed that well), means that you're probably better off separating your constants from your shaders, otherwise you've got an artificial coupling that's only going to hinder you as you go forward.

As an example, what would you propose to do if you had two different vertex shaders but they both needed to use the same matrices?  Your current design doesn't handle this requirement, instead forcing you to upload the matrices twice.  However, D3D itself doesn't (and never did) require you to do that.

If what you really want is just a D3D11 version of the old Effects framework (which it looks like is what you're designing towards), there is one provided with the DirectX SDK (I don't know about newer Windows SDK versions) in source-code-only form; you'll find it in C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Samples\C++\Effects11 and you can compile it and link to it.

Personally I've just got a much more lightweight shader class that just contains creation and destruction code for the types of shaders (and input layouts) I'm interested in, using a hash of the bytecode and a global shader cache to avoid creating the same object more than once, some simple state change management, and otherwise I handle cbuffer updates completely separately and via the raw API.

Hey! Thanks for the info! It is really helpful.

Edited by KoldGames

Share on other sites

The way I do it in my engine is that I add meta data for my shaders, describing the size of the constant, it's register, it's name, whatever information I may need. Then in my game I have a SetUniform call that takes in a name or an enum that corresponds to known constants, a void*, and the size of the uniform. Then I map the name/id to a global shader cache, and apply it from there. It takes  a bit of setup to work, but that's all handled by tools now. And I can create custom uniforms without having to cram it into some sort of pre-defined register index.

• 18
• 20
• 11
• 21
• 16