HLSL: packing error? - c++

I am passing in a constant buffer with the following layout:
struct
{
float spread;
D2D1_POINT_2F dimension;
D2D1_POINT_2F dimension2;
} m_constants;
for debugging sake, dimension and dimension2 have the same values.
In the shader i have:
cbuffer constants
{
float spread;
float2 dimension;
float2 dimension2;
};
float4 main(
float4 pos : SV_POSITION,
float4 posScene : SCENE_POSITION,
float4 uv0 : TEXCOORD0
) : SV_Target
{
float width = dimension.x;
float height = dimension.y;
float2 uv2 = float2(posScene.x / width, posScene.y / height);
color.rgb = float3(uv2.xy, 0);
return color;
}
this, in theory, should output a gradient with green on the bottom left and red at the top right. And it does.
But if, in the shader i have the width and height to use dimension2 instead. i get a horizontal gradient from green on the left to yellow on the right.
Why is that? both dimensions have the same value when i passed the m_constants to the shader

Constant buffers data is aligned by 16 bytes by default, so this means:
cbuffer constants
{
float spread;
float2 dimension;
float2 dimension2;
};
will be
cbuffer constants
{
float spread; // 4 bytes
float2 dimension; // 4 + 8 = 12 bytes
float dummy; //12+8 = 20, which means we cross 16 for dimension 2, hence a dummy 4 bytes element is added
float2 dimension2;
};
here is a link that describes this.
So a better way to arrange your structure would be:
struct
{
D2D1_POINT_2F dimension;
D2D1_POINT_2F dimension2;
float spread;
} m_constants;
and modify the hlsl counterpart accordingly:
cbuffer constants
{
float2 dimension;
float2 dimension2;
float spread; // No more 16 bytes crossing problem
};
Another way, without modifying initial layout, in c++ side, either declare your structure like:
#pragma pack(push)
#pragma pack(16)
struct
{
float spread;
D2D1_POINT_2F dimension;
D2D1_POINT_2F dimension2;
} m_constants;
#pragma pack(pop)
That will force structure to be 16 bytes aligned.
You can also use /Zp16 compiler flag , but that will then apply to every structure in your program (which is not always desirable). In visual studio go to project properties -> c/c++ -> Code Generation, then you have option "Struct Member Alignment", where you can set it from.
You can also use packoffset on hlsl side, but then it means that the c++ layout needs to match the packed hlsl one (which means you keep same order in hlsl constant buffer, but still have to modify the c++ version).

Related

storage buffer and wrongfully aligned data

I'm giving some materials to my storage buffer so that my shader can have the materials of the objects I'm trying to draw, but the colors do no correspond, I think it's due to memory alignment but I'm new to opengl so I don't find the error
struct Material
{
Color diffuse;
Color specular;
Color emission;
float ns;
Material( ) : diffuse(0.8f, 0.8f, 0.8f), specular(Black()), emission(), ns(0) {}
};
struct Color
{
float r, g, b, a;
};
and my fragment shader
struct Material
{
vec4 diffuse;
vec4 specular;
vec4 emission;
float ns;
};
layout(binding=1) readonly buffer IndexBlock{
uint color_indices[];
};
layout(binding=2) readonly buffer MaterialBlock {
Material materials[];
};
in the main :
uint color_index = color_indices[gl_PrimitiveID];
vec3 frag_color = materials[color_index].diffuse.xyz;
I recommend to use std140 or std430 layout qualifier.
See OpenGL 4.6 API Core Profile Specification; 7.6.2.2 Standard Uniform Block Layout:
[...]
If the member is a structure, the base alignment of the structure is N, where
N is the largest base alignment value of any of its members, [...]
If the member is an array of S structures, the S elements of the array are laid out in order, according to rule (9).
You can add 3 floats to the c++ structure. But it It would be better to use an alignas specifier and align the structure to 16 bytes:
struct alignas(16) Material
{
Color diffuse;
Color specular;
Color emission;
float ns;
// [...]
}
Shader:
struct Material
{
vec4 diffuse;
vec4 specular;
vec4 emission;
float ns;
};
layout(binding=1, std430) readonly buffer IndexBlock{
uint color_indices[];
};
layout(binding=2, std430) readonly buffer MaterialBlock {
Material materials[];
};
The difference between std140 and std430 is, that for std140 the base alignment and stride of arrays of scalars and vectors and of structures is rounded up to a multiple of the base alignment of a vec4. This is not the case for std430.
You can test if Color matches vec4 easily.
std::cout<< sizeof(Color) <<std::endl;
std::cout<< sizeof(vec4) <<std::endl;
if the sizes are different then you can stop here.
if the sizes are equal, then try to init the same values for Color and vec4
Color c(1.0,2.0,3.0,4.0);
vec4 v(1.0,2.0,3.0,4.0);
unsigned char buf1[sizeof(Color)];
unsigned char buf2[sizeof(vec4)];
memcpy(buf1, &c, sizeof(Color));
memcpy(buf2, &v, sizeof(vec4));
bool equal = (memcmp(buf1, buf2, sizeof(Color)) == 0);
std::cout<< "Is equal? " << equal ? "yes" : "no" <<std::endl;
BTW: I think vec4 uses double, not float.

Alpha channel is acting and outputting green in directx11 and c++

I am new to Directx11 .I wanted to create point Light behaviour to my program.So I defined a struct like so
struct PointLights
{
XMFLOAT3 Pos; //Light Position in (X,Y,Z)
float Range; //the range after which objects will not be affected by this light
XMFLOAT4 Color; //Light Color
XMFLOAT3 att; // Attenuation Parameters
float Intensity;
};
And Added it to constant buffer like
struct ConstantBuffer
{
XMMATRIX World;
XMMATRIX WorldInv;
XMMATRIX View;
XMMATRIX Project;
XMFLOAT3 LighDirs[2];
XMFLOAT4 LightColor[2];
float SpecualrIntensity;
XMFLOAT3 CameraPos;
PointLights pointLight;
};
This function is used to create buffer
void InitConstantBuffer(void)
{
D3D11_BUFFER_DESC desc;
SecureZeroMemory(&desc,sizeof(desc));
desc.BindFlags= D3D11_BIND_CONSTANT_BUFFER;
desc.ByteWidth = sizeof(ConstantBuffer);
desc.CPUAccessFlags = 0;
desc.Usage = D3D11_USAGE_DEFAULT;
SecureZeroMemory(&g_ConstantBuffer,sizeof(ID3D11Buffer));
HRESULT HR= dev->CreateBuffer(&desc,0,&g_ConstantBuffer);
if(FAILED(HR))
MessageBox(_hwnd,L"Couldnt Create constant buffer",L"ERROR",0);
}
Now When i update the point lights color to black but set its alpha to 1 (0,0,0,1)
the out put is green and output is black if alpha is 0 (0,0,0,0)
PointLights pointLight;
SecureZeroMemory(&pointLight,sizeof(PointLights));
pointLight.att = XMFLOAT3(0,0,1);
pointLight.Intensity = 5;
pointLight.Pos = XMFLOAT3(Lppos[0],Lppos[1],Lppos[2]);
pointLight.Range = 200;
pointLight.Color = XMFLOAT4(0,0,0,1); // black
ConstantBuffer cb;
cb.World = XMMatrixTranspose(g_World);
cb.View = XMMatrixTranspose(g_View);
cb.Project = XMMatrixTranspose(g_Projection);
cb.WorldInv = InverseTranspose(g_World);
cb.LighDirs[1] = lights[1].Dir;
cb.LightColor[1] = lights[1].Color;
cb.LightColor[0] = lights[0].Color;cb.LighDirs[0] = lights[0].Dir;
cb.LightColor[0] = lights[0].Color;
cb.SpecualrIntensity = 10;
cb.CameraPos = XMFLOAT3(0,0,-10);
cb.pointLight = pointLight;
devcon->UpdateSubresource(g_ConstantBuffer,0,0,&cb,0,0);
![output is green][1]
Cant upload image as stackoverflow wont let me...I hate it
Here is hte pixel shader
struct PointLights{
float3 Position;
float Range;
float4 Color;
float3 Atten;
float Intensity;
};
cbuffer ConstantBuffer:register(b0)
{
matrix world;
matrix worldInv;
matrix view;
matrix project;
float3 LightDir[2];
float4 LightColor[2];
float SpecualrIntensity;
float3 CameraPos;
PointLights pointLight;
};
float4 Ps(Vout vo):SV_TARGET
{
float4 col = (float4)0;
col = pointLight.Color;
return col;}
Please do help me out.. I tried rearranging the members of struct but still in vain
Thanks ....
It's quite likely that the data alignment between the C++ constant buffer structures and HLSL cbuffer declarations do not quite match. Try using XMFLOAT4 instead of XMFLOAT3 to ensure they line up the same way in both.
You are using XMMATRIX in struct ConstantBuffer. This might work, it might not, depending on exactly how you are using it. It makes ConstantBuffer require 16-byte alignement in memory. If you are making this assumption, use XMVECTOR rather then XMFLOAT3 or XMFLOAT4. Otherwise, change XMMATRIX to XMMFLOAT4X4.
If you find the DirectXMath alignment thing confusing, consider using the SimpleMath wrapper from DirectX Tool Kit.

Constant buffer receives wrong value

I have got this HLSL struct and when I pass in a Material buffer from C++ to HLSL, some values in my struct is wrong. I know this because I have tried for example setting the emissive color to Vector4(0, 1, 0, 1) or GREEN and in my pixel shader I return only the emissive color and the result becomes BLUE!
And when I set the emissive to (1, 0, 0, 1) or RED, the pixel shader outputs GREEN color. So it seems like everything is shifted 8 bytes to the right. What might be the reason behind this?
EDIT: I noticed that my virtual destructor made my structure bigger than usual. I removed that and then it worked!
HLSL struct
struct Material
{
float4 emissive;
float4 ambient;
float4 diffuse;
float4 specular;
float specularPower;
bool useTexture;
float2 padding;
};
C++ struct
class Material
{
virtual ~Material(); // - I removed this!
helium::Vector4 m_emissive;
helium::Vector4 m_ambient;
helium::Vector4 m_diffuse;
helium::Vector4 m_specular;
float m_specularPower;
int m_useTexture;
float m_padding[2];
};

Pixel Shader Constant Buffer alignment

I have a pixel shader buffer
cbuffer InputBuffer {
float fTextureWidth;
float fTextureHeight;
float fTimeStep;
float padding; //padding to align to 16 bytes
};
That corresponds to a struct in my shader class:
struct InputBuffer {
float fTextureWidth;
float fTextureHeight;
float fTimeStep;
float padding;
};
Which looks all fine, since its 16-byte aligned. But when I render I get this warning:
The size of the Constant Buffer at slot 0 of the Pixel Shader unit is too small (16 bytes provided, 32 bytes, at least, expected). This is OK, as out-of-bounds reads are defined to return 0. It is also possible the developer knows the missing data will not be used anyway. This is only a problem if the developer actually intended to bind a sufficiently large Constant Buffer for what the shader expects. [ EXECUTION WARNING #351: DEVICE_DRAW_CONSTANT_BUFFER_TOO_SMALL]
Why would the buffer expect 32 bytes? I thought 16 was the minimum and mine clearly is?
EDIT 1: I've added the output of D3DReflect and my shader code.
After running D3DReflect on the pixel shader and retrieving a ID3D11ShaderReflectionConstantBuffer pointer to the constant buffer I ran the GetDesc method on the buffer and got what I expected:
Type: D3D_CT_BUFFER(0)
Variables: 4
Size: 16
Flags: 0
As requested, here is the pixel shader code:
/////////////
// GLOBALS //
/////////////
Texture2D shaderTextures[2]; // 0 is the velocity field texture and 1 is the field that is to be advected
SamplerState SampleType;
/////////////
// BUFFERS //
/////////////
cbuffer InputBuffer {
float fTextureWidth;
float fTextureHeight;
float fTimeStep;
float fDissipation;
};
//////////////
// TYPEDEFS //
//////////////
struct PixelInputType {
float4 position : SV_POSITION;
float2 tex : TEXCOORD0;
};
// Pixel Shader
float4 AdvectionPixelShader(PixelInputType input) : SV_TARGET {
float2 u = shaderTextures[0].Sample(SampleType, input.tex).xy; // velocity
float2 rdFactors = float2(1.0f/fTextureWidth,1.0f/fTextureHeight);
float2 prevPos = float2(input.tex.x - (fTimeStep*rdFactors.x*u.x),input.tex.y - (fTimeStep*rdFactors.y*u.y) );
float2 final = shaderTextures[1].Sample(SampleType,prevPos).xy;
return float4(final,0.0f,1.0f)*fDissipation;
}

C++ HLSL Buffer variable

So hi guys,
right now I'm just trying to pass a value to the hlsl shader. Now heres the constant buffer in the shader:
cbuffer ConstantBuffer
{
float4x4 final;
float4x4 rotation; // the rotation matrix
float4 lightvec; // the light's vector
float4 lightcol; // the light's color
float4 ambientcol; // the ambient light's color
float3 SPACE;
float alpha; // <-- Variable trying to recieve, the first 5 works!
}
And in c++:
struct CBUFFER
{
public:
D3DXMATRIX Final;
D3DXMATRIX Rotation;
D3DXVECTOR4 LightVector;
D3DXCOLOR LightColor;
D3DXCOLOR AmbientColor;
D3DXVECTOR3 SPACE;
float Alpha;
};
So I've tried to align the variables to be able to send them in packs of 16 bits. But for some reason, it isn't working, because the variable alpha in the shader isn't set.
So what did i do wrong? (PS. I set the bufferwidth in the initialization of the shader to: 192)
Thank You
Problem here is that bufferwidth should be 208 instead of 192.
In this case, the sum to calculate cbuffer size is:
64 (float4x4 final) + 64 (float4x4 rotation) + 16 (float4 lightvec)+ 16 (float4 lightcol) + 16 (float4 ambientcol) + 16 (float3 SPACE) + 16 (float alpha) = 208 bytes
Variables of a cbuffer are grouped in packs of 16 bytes, this means that the minimum size of a variable is 16 bytes. Thats why 'float3 SPACE' and 'float alpha' represent 16 bytes each.
"ConstantBuffer" occupies 13 packs of 16 bytes (13 * 16 = 208).
Variables can be packed. The keyword "packoffset" allows you to pack 2, 3 or 4 variables in one 16 bytes pack. In this case, packing 'float3 SPACE' and 'float alpha' reduce the cbuffer size to 192:
cbuffer ConstantBuffer
{
float4x4 final : packoffset(c0);
float4x4 rotation : packoffset(c4);
float4 lightvec : packoffset(c8);
float4 lightcol : packoffset(c9);
float4 ambientcol : packoffset(c10);
float3 SPACE : packoffset(c11); // 'SPACE' is mapped to the first 12 bytes of the 12th pack.
float alpha : packoffset(c11.w); // 'alpha' is mapped to the last 4 bytes of the 12th pack.
}
now the sum to calculate cbuffer size is:
64 (float4x4 final) + 64 (float4x4 rotation) + 16 (float4 lightvec)+ 16 (float4 lightcol) + 16 (float4 ambientcol) + 12 (float3 SPACE) + 4 (float alpha) = 192 bytes
and "ConstantBuffer" occupies 12 packs of 16 bytes (12 * 16 = 192).
Finally the struct "CBUFFER" should be modified:
struct CBUFFER
{
public:
D3DXMATRIX Final;
D3DXMATRIX Rotation;
D3DXVECTOR4 LightVector;
D3DXCOLOR LightColor;
D3DXCOLOR AmbientColor;
D3DXVECTOR4 SPACE_and_alpha; // SPACE_and_alpha.xyz = SPACE, SPACE_and_alpha.w = alpha
};
more packing info: https://msdn.microsoft.com/en-us/library/windows/desktop/bb509632(v=vs.85).aspx
check de Dave awnser, is possible that the compiler created a offset in variables of struct to make all more fast to use in CPU fetch. If pragma not works check if you are using the alpha value direct in pixel shader stage and set the correctly constant buffer (constant buffer set in vertex shader is not shared in Pixel shader if you aren't using effects).