What is the most efficient sprite rendering method in DirectX 10?

I'm currently experimenting with various ways to display 2D sprites in DirectX 10. I started by using the ID3DX10Sprite interface to batch paint my sprites at a time. In the end, however, I wanted to control a little more how my sprites were rendered, so I decided to look at rendering sprites based on four (i.e., each sprite is represented by a square with an applied texture).

I started simply: I created a single vertex buffer, consisting of 4 vertices, that was applied once before the sprites were drawn. Then I focused on my sprites, setting the appropriate properties for transfer to the shader and making a draw call for each sprite, for example: d3dDevice->Draw( 4, 0); . Although this worked, a call to call for each sprite listened to me, so I was looking for a more efficient method.

After searching, I found out about object instancing and decided to give it a try. Everything went well until I tried to implement the most important part of sprites - textures. In short, although I had an array of textures (declared at the top of my shader, for example, Texture2D textures[10]; ) that could be successfully selected in my pixel shader using literals / constants as indexes, I could not understand how to control which textures were applied to which instances through the texture index.

The idea would be to pass the texture index to one instance, which could then be used to select the corresponding texture in the array in the pixel shader. However, after searching more, I could not find an example of how to do this (and found many things suggesting that this could not be done without going to DirectX 11).

Is it possible to say that the only way to successfully manage sprites through an object instance in DirectX 10 is to display them in batches based on texture? So, for example, if my scene consists of 100 sprites with 20 different textures (each texture referenced by 5 sprites), then 20 separate drawing calls will be required to display the scene, and I will only send 5 sprites at a time.

In the end, I'm more at a loss. I have searched many times and seem to have come up with conflicting information. For example, in this article in paragraph 6 it says:

Using DirectX 10, you can apply different textures in an array to different instances of the same object, thereby making them different.

In addition, on page 3 of this document, it mentions an option:

Reading custom textures for each instance from an array of textures

However, I cannot find a concrete example of how a shader can be configured to access an array of textures using the instance index of each instance.

In conclusion, the main question: what is the most efficient sprite rendering method using DirectX 10?

If the answer is given, is it possible to control which texture is applied to each specific instance in the shader, thereby allowing sending much larger batches of sprites together with their corresponding texture index with only one draw call? Or should I be content only with instinctive sprites with the same texture at a time?

If the answer goes back to using the provided DX10 Sprite interface, then is there any way for me to control more how it is displayed?

As a side note, I also considered using the Geometry Shader to create the actual square, so I would only have to go through a few points instead of managing the vertex and instance buffer. Again, if there is no way to control which textures are applied to the generated ATVs, then I will return only to packet sprites with textures.

+4
source share
2 answers

There are several ways (as usual) to do what you describe.

Please note that use

 Texture2D textures[10]; 

will not allow you to use the variable index to search in Pixel Shader (since technically this ad will highlight the slot on the texture).

So you need to create a Texture2DArray. This is a bit like the texture of a volume, but the z component is a full number and there is no selection on it.

You will need to create this texture array. An easy way to start is when you make a one-time quad-core call to draw each texture into a slice of an array (you can create a RenderTargetView for a specific fragment). The shader will be a simple walkthrough here.

To create an array of textures (the code is in SlimDX, but the parameters are similar):

  var texBufferDesc = new Texture2DDescription { ArraySize = TextureCount, BindFlags = BindFlags.RenderTarget | BindFlags.ShaderResource, CpuAccessFlags = CpuAccessFlags.None, Format = format, Height = h, Width = w, OptionFlags = ResourceOptionFlags.None, SampleDescription = new SampleDescription(1,0), Usage = ResourceUsage.Default, }; 

Then the presentation of the shader resources is as follows:

  ShaderResourceViewDescription srvd = new ShaderResourceViewDescription() { ArraySize = TextureCount, FirstArraySlice = 0, Dimension = ShaderResourceViewDimension.Texture2DArray, Format = format, MipLevels = 1, MostDetailedMip = 0 }; 

Finally, to get the rendering target for a specific fragment:

  RenderTargetViewDescription rtd = new RenderTargetViewDescription() { ArraySize = 1, FirstArraySlice = SliceIndex, Dimension = RenderTargetViewDimension.Texture2DArray, Format = this.Format }; 

Bind this to your encrypted shader, set the desired texture as input and slice as output, and draw a full-screen square (or full-screen triangle).

Please note that this texture can also be saved in dds format (therefore, it saves you when restoring every time you start your program).

The texture search is as follows:

 Texture2DArray myarray; 

In the pixel shader:

 myarray.Sample(mySampler, float2(uv,SliceIndex); 

Now about rendering sprites, you also have the option of expanding GS.

So, you create a vertex buffer containing only the position / size / texture index / all you need, one vertex per sprite.

Send a draw call using n sprites (Topology must be set to a list of points).

Paste data from the vertex shader into the geometric shader.

Expand your point to a square in the geometric shader, you can find an example that ParticlesGS in the Microsoft SDK does, it will outsmart for your business, since you only need a part of the rendering, not animation. If you need some sort of cleaned code, let me know, I will quickly make a dx10 compatible sample (in my case I use StructuredBuffers instead of VertexBuffer)

Executing a pre-made Square and transferring the above data to Per Instance VertexBuffer is also possible, but if you have a large number of sprites, it will easily blow your graphics card (by and large, I mean something like more than 3 million particles, which is currently not there are so many, but if you have less than half a million sprites, you will be completely fine;)

+4
source

Include the texture index in the instance buffer and use this to select the correct texture from the texture array per instance:

 struct VS { float3 Position: POSITION; float2 TexCoord: TEXCOORD0; float TexIndex: TexIndex; // From the instance buffer not the vertex buffer } 

Then pass this value to the end in the pixel shader

 struct PS { float4 Positon: SV_POSITION; float3 TexCoord: TEXCOORD0; } .. vout.TexCoord = float3(vin.TexCoord, vin.TexIndex); 
0
source

Source: https://habr.com/ru/post/1489112/


All Articles