• Advertisement
Sign in to follow this  

Getting in a global variable in HLSL

This topic is 2155 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

Hello, I am working on a simple 2D project and have just about all the pieces I need except animation for 2d. I have looked around I have a good idea of how to do it, that is, what I am doing now is making a quad, texturing it and then running through different parts on a sprite sheet style texture. I understand the math on how to get the right co ordinates of a single frame on the sprite sheet but I'm embarrassed to say I'm stuck on something that seems like it should be simple: I can't seem to get a variable into the HLSL vertex shader code so I can manipulate the math it over time. I've looked around the net and found info about global variables being able to be loaded in through certain buffers but I can't figure out how to get a couple of float values in that I can do arithmetic with.

I can create a variable WITHIN the vertex shader, but this will of course just be reinitialized every time and doesn't serve me much use.

I've found examples of loading in matrices into constant buffers but I can't find how to do simple variables, I've read it can be done but I can't find examples hehe.

Again I know this seems like a simple problem and I really hope it is but if anyone could lead me on the right path or give me a code idea on how I can set up a simple variable like a float that I can manipulate in my C++ code that transfers into the HLSL vertex shader it would help me a lot.

Thank you so much!

Share this post


Link to post
Share on other sites
Advertisement
If you are using D3D9, you set individual registers using device->Set*ShaderConstant*() methods (where first star is Vertex or Pixel, and second star is I, F or B depending on register datatype). The compiled shader code contains the symbolic register offsets of shader constants so you can find the registers by name in your host program (see ID3DXConstantTable for a convenient helper for this). D3D9 has only single constant register bank.
Even though you set constant registers from the host program, you still allocate the corresponding constants in the HLSL code.

D3D10 and up, you have multiple banks of constants, called "constant buffers". You can fill these up with pretty much any data you want, if you follow the alignment rules of HLSL in your data. To fill a constant buffer, you either provide the initial data contents when you create it, or alternatively map->copy data->unmap the buffer like any other GPU resource. D3D10 and up also support "shader reflection" so you can still lookup offsets to the buffer data given a symbolic constant name.

The D3DX Effects framework simplifies setting shader constants (and other device state); among other things, it performs the constant name->register offset mapping automatically for you.

Share this post


Link to post
Share on other sites
Thanks for you're response and yea, I'm working with D3D11. I was pretty sure it was those constant buffers but I think I'm just not sure how they come together. I've been digging through the examples I've seen that usually just have one of these buffers for the matrix information. I tried to recreate one for just a float, in fact I believe I tried that map->copy data->unmap that you suggested but it wasn't working for me... I think that's why I feel this is got to be more simple than I'm thinking. Can I place a float into the buffer I've already made? Like enter a float into this somewhere:

cbuffer cbChangesPerFrame : register( b0 )
{
matrix mvp_;
//float floatThing;
};


Or do I need to make a new one? Again sorry if this question is something simple I'm just super overlooking, this is just new territory for me.

Share this post


Link to post
Share on other sites
You can place any fields in there, as long as you don't blow the total maximum limit of the buffer (way past a few variables).

In this case, the matrix would occupy the first 4 registers of the buffer, and the data for the float would occupy the first 4 bytes of the next register. Note, though, that the concept of "registers" does not strictly apply as the buffers are handled as resources in D3D11.

In C++ side, you could fill the buffer with a following structure (note that alignment differences would have to be considered further if you added more individual floats):

struct cbcontents
{
float[16] mvp_;
float floatThing;
};

It is not strictly necessary to use pre-structured data, though - you could use an array of bytes and find the offsets manually.

To find the offset in which a shader constant is within a given constant buffer at runtime, you can use D3DReflect function (against the compiled shader bytecode) to get a shader reflection interface. From the reflection interface, you can resolve the offsets of variables by name. And armed with the offset, you can manipulate the data within the buffer (via map/unmap) to effectively change the individual constant.

To understand how the offsets are assigned, run fxc.exe from the command prompt and compile your shader with it. The tool will list all offsets of all constants.

Share this post


Link to post
Share on other sites
Wow I'm really digging into this now, I've been reading up on all the related material you've given me but I think I'm still off on one thing. Currently I've got this experiment to set up that constant buffer:

D3D11_BUFFER_DESC constDesc;
ZeroMemory(&constDesc, sizeof(constDesc));
constDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
constDesc.ByteWidth = sizeof(XMMATRIX) + sizeof(float);
constDesc.Usage = D3D11_USAGE_DEFAULT;
d3dResult = d3dDevice_->CreateBuffer(&constDesc, 0, &mvp_); //mvp_ is a buffer object
if(FAILED(d3dResult))
{
MessageBox(0, "Failed to create buffer (constDesc)", "CompileError", MB_OK);
return false;
}


So my question is, is that +(float) doing what I think it is and adding space that a float would take?

Then later in the rendering function after I transform my matrix these functions are called:

d3dContext_->UpdateSubresource(mvp_, 0, 0, &mvpInfo, 0, 0); //mvpInfo is a matrix
d3dContext_->VSSetConstantBuffers(0, 1, &mvp_);


So I looked up the documentation and I understand the first argument is the buffer object and the 4th argument is a pointer to the data you want sent in. What I'm confused about is the second argument, the "index." Is this the offset you were mentioning previously? So would something like:

d3dContext_->UpdateSubresource(mvp_, 16, 0, &floatValue, 0, 0);

Access the position the float value should go? Or actually would it be 16*4 there since the matrix is made up of float values? Or do I need a little more info than this?

Thank you so much for your help so far, I'm learning a lot more than my original question set out to find hehe and I love it.

Also I did try that fxc.exe to compile my shader but It would only give me info on the mvp_ matrix as:

// Parameters:
//
// float4x4 mvp_;
//
//
// Registers:
//
// Name Reg Size
// ------------ ----- ----
// mvp_ c0 4
//

vs_2_0
dcl_position v0
dcl_texcoord v1
dp4 oPos.x, v0, c0
dp4 oPos.y, v0, c1
dp4 oPos.z, v0, c2
dp4 oPos.w, v0, c3
mov oT0.xy, v1

// approximately 5 instruction slots used

I got errors when attempting to have the float in there.

Share this post


Link to post
Share on other sites
You need to update all of the buffer contents at the same time; that's why it is practical to use a c++ struct to match the contents of the constant buffer (so you can handle the buffer contents data as a single variable on c++ side).

As a side note, it is confusing that your constant buffer has the same name as the matrix contained within. They are logically very different things.

What errors do you get when you add the float to the constant buffer? The compiler information is usually very specific.

Is it your actual intent to compile the shader with VS 2.0 profile?

Share this post


Link to post
Share on other sites
Took your advice on the struct (and the naming convention for the buffer/matrix, what a goof there thank you:)), created a struct defined as:

struct cBufferObject
{
XMMATRIX mvp_;
float xThing;
float yThing;
};


Then created the buffer with:

D3D11_BUFFER_DESC constDesc;
ZeroMemory(&constDesc, sizeof(constDesc));
constDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
constDesc.ByteWidth = (sizeof(cBufferObject));
constDesc.Usage = D3D11_USAGE_DEFAULT;
d3dResult = d3dDevice_->CreateBuffer(&constDesc, 0, &mvpBufferInfo_);
if(FAILED(d3dResult))
{
MessageBox(0, "Failed to create buffer (constDesc)", "CompileError", MB_OK);
return false;
}


And it compiles and gets through just fine now. I'm still putting in the matrix directly with:

d3dContext_->UpdateSubresource(mvpBufferInfo_, 0, 0, &mvp, 0, 0); //mvp is a matrix and mvpBufferInfo is the constant buffer
//d3dContext_->UpdateSubresource(mvpBufferInfo_, 16, 0, &xthing, 0, 0); //xthing is the float
d3dContext_->VSSetConstantBuffers(0, 1, &mvpBufferInfo_);


I'm not throwing in a pointer to a created object of the struct type "cBufferObject" itself as I wasn't sure how that would be interpreted and the program its working(correctly) the same. Does this mean I have space now for that float in this buffer, and indexing to that spot will allow me to store data for a float there? and if so how do I access it in the vertex shader this manner(I'm assuming C-style)? Or should I be giving UpdateSubresource() a single call using a pointer to that struct and then just modifying its contents. I think my confusion is in that subtle count of bytes and how to access what. I'm actually taking computer architecture this semester so I'm instantly seeing this creep up on me already hehe, strangely this looks like this is going to give me a bunch of insight there as well.:) The project here is just something I'm working on to get my graphics coding skills a boost(or a head start at least), wanted to do some 2D stuff first.

Share this post


Link to post
Share on other sites
Got it! Yea all I had to do was send in a pointer to the structure I defined in the previous post instead of trying to index to things individually. Sure enough the floats were right in order as expected after the matrix within the data for the struct so this code in the shader:

cbuffer cbChangesPerFrame : register( b0 )
{
matrix mvp_;
float xthing;
float ything;
};


It took in the matrix just as before, and the 2 floats following it were right in line after the matrix data and I was able to manipulate them by changing the floats in the structure object I made in the C++ code, awesome! Then I just used the float values to get to the right frame on the sprite sheet. With a bit more coding and a timer I can easily make an animation style shader with this! Well this has been a fun little journey for me, learned a lot more than "how do I do this" for sure! Thank you so much Nik02 your guidance was incredibly helpful and greatly appreciated!

Share this post


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

  • Advertisement