Jump to content
  • Advertisement
Sign in to follow this  
Alundra

DX11 Template shader, Bad practice ?

This topic is 1416 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 all,

It's possible to do that to factor the shader code, code from intel sample :

// Templated shader factory utilities
namespace ShaderFactoryUtil
{
    template <typename T> LPCSTR GetShaderProfileString();
    template <typename T> T* CreateShader(ID3D11Device* d3dDevice, const void* shaderBytecode, size_t bytecodeLength);

    // Vertex shader
    template <> inline LPCSTR GetShaderProfileString<ID3D11VertexShader>() { return "vs_5_0"; }
    template <> inline 
    ID3D11VertexShader* CreateShader<ID3D11VertexShader>(ID3D11Device* d3dDevice, const void* shaderBytecode, size_t bytecodeLength)
    {
        ID3D11VertexShader *shader = 0;
        HRESULT hr = d3dDevice->CreateVertexShader(shaderBytecode, bytecodeLength, 0, &shader);
        if (FAILED(hr)) {
            // This shouldn't produce errors given proper bytecode, so a simple assert is fine
            assert(false);
        }
        return shader;
    }

    // Pixel shader
    template <> inline LPCSTR GetShaderProfileString<ID3D11PixelShader>() { return "ps_5_0"; }
    template <> inline 
    ID3D11PixelShader* CreateShader<ID3D11PixelShader>(ID3D11Device* d3dDevice, const void* shaderBytecode, size_t bytecodeLength)
    {
        ID3D11PixelShader *shader = 0;
        HRESULT hr = d3dDevice->CreatePixelShader(shaderBytecode, bytecodeLength, 0, &shader);
        if (FAILED(hr)) {
            // This shouldn't produce errors given proper bytecode, so a simple assert is fine
            assert(false);
        }
        return shader;
    }

    // Compute shader
    template <> inline LPCSTR GetShaderProfileString<ID3D11ComputeShader>() { return "cs_5_0"; }
    template <> inline 
    ID3D11ComputeShader* CreateShader<ID3D11ComputeShader>(ID3D11Device* d3dDevice, const void* shaderBytecode, size_t bytecodeLength)
    {
        ID3D11ComputeShader *shader = 0;
        HRESULT hr = d3dDevice->CreateComputeShader(shaderBytecode, bytecodeLength, 0, &shader);
        if (FAILED(hr)) {
            // This shouldn't produce errors given proper bytecode, so a simple assert is fine
            assert(false);
        }
        return shader;
    }
}

// Templated (on shader type) shader wrapper to wrap basic functionality
// TODO: Support optional lazy compile
template <typename T>
class Shader
{
public:
    Shader(ID3D11Device* d3dDevice, LPCTSTR srcFile, LPCSTR functionName, CONST D3D10_SHADER_MACRO *defines = 0)
    {
        // TODO: Support profile selection from the application? Probably not necessary as we don't
        // support down-level hardware at the moment anyways.
        LPCSTR profile = ShaderFactoryUtil::GetShaderProfileString<T>();
        UINT shaderFlags = D3D10_SHADER_ENABLE_STRICTNESS | D3D10_SHADER_PACK_MATRIX_ROW_MAJOR;
        ID3D10Blob *bytecode = 0;
        ID3D10Blob *errors = 0;
        HRESULT hr = D3DX11CompileFromFile(srcFile, defines, 0, functionName, profile, shaderFlags, 0, 0, &bytecode, &errors, 0);
        if (errors) {
            OutputDebugStringA(static_cast<const char *>(errors->GetBufferPointer()));
        }
        if (FAILED(hr)) {
            // TODO: Define exception type and improve this error string, but the ODS will do for now
            throw std::runtime_error("Error compiling shader");
        }
        mShader = ShaderFactoryUtil::CreateShader<T>(d3dDevice, bytecode->GetBufferPointer(), bytecode->GetBufferSize());
        bytecode->Release();
    }
    
    ~Shader() { mShader->Release(); }
    T* GetShader() { return mShader; }

private:
    // Not implemented
    Shader(const Shader&);
    Shader& operator=(const Shader&);

    T* mShader;
};

typedef Shader<ID3D11VertexShader> VertexShader;
typedef Shader<ID3D11PixelShader> PixelShader;
typedef Shader<ID3D11ComputeShader> ComputeShader;

Do you see that as a bad practice ?

 

Share this post


Link to post
Share on other sites
Advertisement

It certainly works fine biggrin.png
 
But I would probably prefer to follow the KISS rule and stick to something simpler like a switch statement instead of fancy templates here. 

namespace ShaderProfile { enum Type {
	Vertex, Pixel, Compute
}}
const char* s_shaderProfileStrings[] = { "vs_5_0", "ps_5_0", "cs_5_0" };
void* CreateShader(ID3D11Device* d3dDevice, const void* shaderBytecode, size_t bytecodeLength);
{
	HRESULT hr = 0;
	void* shader = 0;
	switch(type)
	{
	case ShaderProfile::Vertex:
	        hr = d3dDevice->CreatePixelShader(shaderBytecode, bytecodeLength, 0, (ID3D11PixelShader**)&shader); break;
	case ShaderProfile::Pixel:
	        hr = d3dDevice->CreateComputeShader(shaderBytecode, bytecodeLength, 0, (ID3D11VertexShader**)&shader); break;
	case ShaderProfile::Compute:
	        hr = d3dDevice->CreateVertexShader(shaderBytecode, bytecodeLength, 0, (ID3D11ComputeShader**)&shader); break;
	default: assert(false); return 0;
	}
	if (FAILED(hr))
		assert(false);
	return shader;
}


class Shader : NonCopyable
{
public:
    Shader(ShaderProfile::Type type, ID3D11Device* d3dDevice, LPCTSTR srcFile, LPCSTR functionName, CONST D3D10_SHADER_MACRO *defines = 0)
    {
        mType = type;
        //todo - this should be loading bytecode from disk instead of compiling at runtime
        const char* profile = s_shaderProfileStrings[type];
        UINT shaderFlags = D3D10_SHADER_ENABLE_STRICTNESS | D3D10_SHADER_PACK_MATRIX_ROW_MAJOR;
        ID3D10Blob *bytecode = 0;
        ID3D10Blob *errors = 0;
        HRESULT hr = D3DX11CompileFromFile(srcFile, defines, 0, functionName, profile, shaderFlags, 0, 0, &bytecode, &errors, 0);
        if (errors) {
            OutputDebugStringA(static_cast<const char *>(errors->GetBufferPointer()));
        }
        if (FAILED(hr)) assert(false && "Error compiling shader");
        mShader = ShaderFactoryUtil::CreateShader(type, d3dDevice, bytecode->GetBufferPointer(), bytecode->GetBufferSize());
        bytecode->Release();
    }
    
    ~Shader()
    {
	switch(mType)
	{
	case ShaderProfile::Vertex:
	        ((ID3D11VertexShader*)mShader)->Release();
	case ShaderProfile::Pixel:
	        ((ID3D11PixelShader*)mShader)->Release();
	case ShaderProfile::Compute:
	        ((ID3D11ComputeShader*)mShader)->Release();
	}
        
    }
    void* GetVertexShader()  const { assert(mType==ShaderProfile::Vertex) ; return (ID3D11VertexShader* )mShader; }
    void* GetPixelShader()   const { assert(mType==ShaderProfile::Pixel  ); return (ID3D11PixelShader*  )mShader; }
    void* GetComputeShader() const { assert(mType==ShaderProfile::Compute); return (ID3D11ComputeShader*)mShader; }

private:
    ShaderProfile::Type mType;
    void* mShader;
};

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!