0
\$\begingroup\$

The program cycle is

Update();
UpdatePipeline();

In Update() constant buffer for each object, that after transformations, has this object world matrix is copied to GPU upload heap. And in UpdatePipeline(), among other things, installed shaders are called. Because we do all matrix transformation using CPU, vertex shader just returns position, right? If yes - is it true that performance will increase?

Now I want to do all transformations using GPU, i.e. via vertex shader. It means that in Update() I just should call memcpy() with an empty constant buffer as a source?


Learning more, I found that I can put in constant buffer position vector float4 or matrix float4x4 of vertex, and as a root constant choose ProjectionView Matrix. The shader will mul one on other. Keep searching... Or constant buffer is one of the root constants? Confused

I don’t understand how to choose what to send as an argument to shader function, like input here:

VS_OUTPUT main(VS_input input)
{
...
}

As I understand, I can access constant buffer the next way:

cbuffer ConstantBuffer : register(b0)
{
float4x4 mat;
}

There can be ProjectionView matrix, okey got that. But how to send vertex pos to input then?

\$\endgroup\$
  • \$\begingroup\$ Doing all matrix transforms using the CPU means that you're using the slower processor for a critical stage of the pipeline, AND you have to update your vertex buffers every frame. No, that will NOT be faster unless you're doing something very weird elsewhere. As a general rule: use the API the way it's been designed to be used. \$\endgroup\$ – Maximus Minimus Mar 28 at 21:52
  • \$\begingroup\$ @MaximusMinimus But I don’t seems to update vertex buffer , I update only **constant buffer**(it actually stores vertices, but "vertex buffer", as far as I understand, is another thing) \$\endgroup\$ – Артур Клочко Mar 29 at 8:10
  • \$\begingroup\$ @MaximusMinimus and You didn’t answer should I keep constant buffer empty if I want to do all work in shader \$\endgroup\$ – Артур Клочко Mar 29 at 8:12
0
\$\begingroup\$

It very much depends on the use case. The best option you could do is to implement different methods and choose depending on which is fastest and easiest to maintain for your project. Some points to keep in mind:

  • Vertex shader is a very good place to transform vertices as it is designed exactly for that purpose.
  • It is very good to keep the vertex shader to a minimum, too, so if you can premultiply matrices on the CPU. For example, View and Projection matrices are usually premultiplied on CPU, so the shader only has to transform vertex with ViewProjection and World matrix. If you don't care about vertex world position, only the screen position, then you can also premultiply World matrix with ViewProjection matrix.
  • Transforming all vertices on CPU by viewprojection matrix is usually not recommended, but transforming them with world matrix or skinning animation is not uncommon.
  • You probably want to have matrices in ConstantBuffer, because Root Signature space is very limited. If you store a single matrix in a root signature, you won't be able to store anything else as it takes up all space of 64 DWORDs. Also, the input assembler (vertex buffer, index buffer) want space in the root signature too.
  • You can read data from the vertex buffer in multiple ways in a vertex shader. If you use IASetVertexBuffers(), you will receive the vertex properties as input parameters to the shader function. You can also bind a shader resource view of the vertex buffer and have a uint vertexID : SV_VertexID input parameter to the vertex shader and index into the vertex buffer (that could be declared as ByteAddressBuffer, StructuredBuffer, or Buffer in the shader).
  • You can also use instancing, which will allow to render one vertex buffer multiple times with different transformations. For that, you will need to use instance buffers, which are filled with transformation matrices. Again, which matrices you have in the instance buffer or constant buffer are up to you, but commonly world matrix should be in the instance buffer, viewprojection matrix in a contant buffer. The instance buffers are bound with IASetVertexBuffers() call, but their input layout must specify INPUT_CLASSIFICATION_PER_INSTANCE_DATA.
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Thanks for feedback. All I want now is just draw many objects(currently 10k cubes). No scaling, no rotation. Just draw fast, having 60 fps. I just edited (again) my question, can You update Your answer? \$\endgroup\$ – Артур Клочко Mar 29 at 10:35
  • \$\begingroup\$ For that, you most likely want to use instancing. Let me update with a point about it -- done \$\endgroup\$ – János Turánszki Mar 29 at 10:39
  • \$\begingroup\$ It seems, that the thing I can’t get is where I chose what to be sent as an argument to vertex shader function(VS_input input in my example). How do I understand conception: I set premultiplied Projection * View matrix as a constant buffer using memcpy each iteration. Then VS_input should contain vertex position. Multiply position on the matrix and return new position \$\endgroup\$ – Артур Клочко Mar 29 at 10:51
  • \$\begingroup\$ If understand Your edit right, I should create 2 "constant" buffers - one for view * projection and another for vertex position. What is the sense of VS_input input parameter then? \$\endgroup\$ – Артур Клочко Mar 29 at 10:54
  • \$\begingroup\$ Interesting, I tried to send as a constant buffer only projection * view, in shader I mul(input.pos, viewProjMat) . As a result I get one cube in the middle of screen(instead of hundreds, like earlier). Seems like input.pos is zero \$\endgroup\$ – Артур Клочко Mar 29 at 11:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.