[d3d9/10] Moving away from Effects

Started by
10 comments, last by MJP 13 years, 6 months ago
After doing some research, it appears that effects are too slow and should be avoided.

With that said, I haven't found any tutorials on how to use shaders without effects, except for one. Any links to tutorials that uses the shader pipeline and does not use effects (preferably D3D10) is greatly appreciated.

Anyway, I have quite a few questions...

When rendering with D3D9 :
1) I don't need any technique code like
technique ShaderModel2_Technique{    pass P0    {        vertexShader = compile ...        pixelShader  = compile ...    }}

since I'll tell it how to compile the shaders with D3DXCompileShaderFromFile, right?

2) How do I do multiple passes with the shaders?
What I mean is with effects you would do something like this :
for( unsigned m = 0; m < NumPasses; m++ ){    Effect->BeginPass( m );

how do I do it without effects?

When rendering with D3D10 :
Do I need to have technique code inside my shader? Because when rendering with the effects, I had to do something like this when creating the input layout :
D3D10_PASS_DESC PassDesc;Tech->GetPassByIndex( 0 )->GetDesc( &PassDesc );m_pd3dDevice->CreateInputLayout( pLayout, dwNumInputElements, PassDesc.pIAInputSignature, ... );


Compiling and creating the shaders seem simple enough, I just need to call "D3DX10CompileFromFile" and "ID3D10Device::CreateVertexShader"/"ID3D10Device::CreatePixelShader", but how do I do everything else like :

1) interlacing with (point to) the variables inside a shader? Is there an equivalent to ID3DXCONSTANTTABLE?

2) setting the variables inside the shader

and most importantly

3) render using the shaders?
Advertisement
Quote:Original post by 16bit_port
After doing some research, it appears that effects are too slow and should be avoided.


They're not slow at all, and should not be avoided unless you have a good reason.

Quote:
2) How do I do multiple passes with the shaders?
What I mean is with effects you would do something like this :
*** Source Snippet Removed ***
how do I do it without effects?


pDevice->PSSetShader(..., firstShader);
Render();
PSSetshader(..., secondPassShader);
RenderAgain();

Quote:
When rendering with D3D10 :
Do I need to have technique code inside my shader?


Techniques are effects, without effects you don't have techniques. You need to pass the byte-code you get from D3DCompile for the vertex-shader when creating the input layout.

Quote:
1) interlacing with (point to) the variables inside a shader? Is there an equivalent to ID3DXCONSTANTTABLE?


Create a constant buffer and Map it to fill it with your data.

Quote:3) render using the shaders?

VSSetShader/GSSetShader/PSSetShader.
Quote:Original post by Erik Rufelt
They're not slow at all, and should not be avoided unless you have a good reason.


A couple of people said that effects were slow.
and
I remember reading that effects were not supported in D3D11 but were later due to educational purposes or something like that.

[Edited by - 16bit_port on October 14, 2010 4:48:16 PM]
1. With both D3D9 and D3D10 you no longer have techniques or passes without effects. Instead you individually compile the shaders that would be part of each pass, by passing the shader profile and the entry point function to compiler (which is essentially what you declare in your pass using an effect).

2. If you want some sort of pass functionality, you do it yourself. You must compile the shader or shaders required, and then for each pass set up the shader + constants and then draw your geometry. All BeginPass does is set the required shaders onto the device, and set all constants/resources that need to be bound for the shaders.

3. When creating an input layout, you just need a pointer to the shader bytecode. You'll get the bytecode back from the D3DCompile/D3DX10CompileFromFile functions.

4. D3D10 and D3D11 have an extensive reflection API, which can be used to gather constant data as well as all sorts of data about your shaders. Check out D3DX10ReflectShader and the ID3D10ShaderReflection interface. OF course with D3D10/D3D11 things are more complicated because you have the concept of concept buffers rather than just individual constants. For best performance you really want to deal with constants in terms of their entire constant buffer, rather than always setting one constant at a time. But if you want to still go with the latter approach for simplicity, it's possible to get all of the information you need using reflection. If you want you can look at any of my samples on my blog for a very simple approach to dealing with constant buffers, or you can look at Hieroglyph3 for a more complex approach that still lets you deal with individual constants/parameters.

5. With D3D9 you set constants using SetVertexShaderConstantF/SetPixelShaderConstantF, which sets a value onto one or more constant registers. You set textures using SetTexture, and sampler states using SetSamplerState.

With D3D10 and D3D11 you have to create the necessary constant buffer, Map it to set data into it, and then bind it to a slot for the shader stage that needs it (for instance you call PSSetConstantBuffers to bind constant buffers to the pixel shader stage). Textures are bound using *SetShaderResources, and sampler states are set using *SetSamplers.

6. To render with a shader you simply bind the shader for the appropriate shader stage, set the constants and resources for each stage, and then draw.

I'm looking at the declaration of IDirect3DDevice9::SetVertexShaderConstantF and I'm not entirely sure what I should be doing with the register parameter.
In D3D9 all constants are mapped to a constant register. They can be mapped explicitly in your shader code (by using the register binding syntax), or automatically by the compiler. If you have a constant table available you can use it to query the constant register for any particular constant.

Each constant register is a float4, and you have to set all 4 float's of a register at once. So if you have a constant that's only 1 float, you must set all 4 floats of that register (although it doesn't matter what you set the other components to, since they won't be used). Some constants can take up multiple registers, for instance a 4x4 matrix will occupy 4 registers. SetVertexShaderConstantF lets you set multiple registers at once, so you can set a single matrix in one call.
Why explicitly map a constant to a register? Why not let the compiler do it for you?

Also, if I let the compiler do it for me, what do I specify in that argument then?

Quote:
Some constants can take up multiple registers, for instance a 4x4 matrix will occupy 4 registers.

Any reason why anyone would do that instead of using IDirect3DDevice9::SetTexture?
Quote:Original post by 16bit_port
Why explicitly map a constant to a register? Why not let the compiler do it for you?


So that you can know the register for a constant without having to reflect it, and/or so you can make sure the same constant gets mapped to the same register across multiple shaders so that you don't have to set it multiple times. So for instance if you have a view matrix that never changes during a frame, you could set it once at the beginning of the frame and that's it.

Quote:Original post by 16bit_port
Also, if I let the compiler do it for me, what do I specify in that argument then?


Like I said, you can use the constant table to retrieve the register index.


Quote:Original post by 16bit_port
Any reason why anyone would do that instead of using IDirect3DDevice9::SetTexture?


Huh? What does setting a matrix as a constant have to do with setting a texture?

Quote:
Huh? What does setting a matrix as a constant have to do with setting a texture?


Whoops! Copied the wrong text. I meant IDirect3DDevice9::SetTransform - but then again I just remembered that that's for the fixed pipeline. Was quickly browsing through the device interfaces and hastily assumed that it for the shaders without really looking at it. So... nevermind about that.
Quote:Original post by 16bit_port
Quote:
Huh? What does setting a matrix as a constant have to do with setting a texture?


Whoops! Copied the wrong text. I meant IDirect3DDevice9::SetTransform - but then again I just remembered that that's for the fixed pipeline. Was quickly browsing through the device interfaces and hastily assumed that it for the shaders without really looking at it. So... nevermind about that.


Oh right, that makes a lot more sense. :P

And yeah it's for fixed-function processing only. For shaders you have to set world/view/projection transforms as shader constants.

This topic is closed to new replies.

Advertisement