Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I have a buffer of about 134 million particles and a very powerful computer to render them smoothly, but I am getting an error when trying to render them as primitive lines. It says that I cannot render more than around 1 million. Is there a way I can do this? Is there a better way to render this other than with lines? I'm comfortable with having 1 pixel points or anything as long as the vertices are shown all the time. I'm basically just plotting the points.

share|improve this question
    
I can't imagine a situation where you'd need 134 million separate particles. I mean, that's WAY more pixels than the average monitor even has. Far fewer particles would likely be visually indistinguishable from what you're using. –  Byte56 Mar 28 '12 at 20:55
    
@Byte56 it's not a stretch of imagination to assume that he is doing a research project (with a dataset that size) and that he does have access to at least a 13.5 megapixel screen. Or he is trying to do 'poor man's voxels'. –  Jonathan Dickinson Apr 25 '12 at 8:43
    
@JonathanDickinson Well then I suppose I can imagine it now. –  Byte56 Apr 25 '12 at 14:12
add comment

2 Answers

up vote 3 down vote accepted

David indicated that you can likely do this by batching these into multiple draw calls. I assume (from experience using 16-bit index buffers) that XNA/your GPU won't care if the actual buffer (array) is much larger than what you are asking it to draw.

Your wireframe will likely be a LineStrip - which according to the documentation is an interconnected set of points.

I made some extension methods that will deal with the batching for you, requiring minimal code changes (just replace DrawUserPrimitives with DrawMassUserPrimitives). This may need tweaking as I haven't actually tested it (especially the calculations - LineStrip is correct though). In addition, if you need the other Draw* calls that has been left as an exercise for the student.

public static class GraphicsDeviceExtensions
{
    private const int MaxPrimitivesHiDef = 1048575;
    private const int MaxPrimitivesReach = 65535;

    // Given the number of primitives, calculate the number of vertices
    // required to create that number of primitives.
    private static readonly Func<int, int>[] CalculateVertices = new Func<int, int>[]
    {
        // TriangleList
        x => x * 3, 
        // TriangleStrip
        x => x + 2, 
        // LineList
        x => x * 2,
        // LineStrip
        x => x + 1
    };

    // Inverse of the above.
    private static readonly Func<int, int>[] CalculatePrimitives = new Func<int, int>[]
    {
        // TriangleList
        x => x / 3,
        // TriangleStrip
        x => x - 2, 
        // LineList
        x => x / 2,
        // LineStrip
        x => x - 1
    };

    public static void DrawMassUserPrimitives<T>(this GraphicsDevice device, PrimitiveType primitiveType, T[] vertexData, int vertexOffset, int primitiveCount) 
        where T : struct, IVertexType
    {
        DrawMass(device, primitiveType, vertexOffset, primitiveCount, (batchVertexOffset, batchPrimitiveCount) =>
        {
            device.DrawUserPrimitives<T>(primitiveType, vertexData, batchVertexOffset, batchPrimitiveCount);
        });
    }

    public static void DrawMassUserPrimitives<T>(this GraphicsDevice device, PrimitiveType primitiveType, T[] vertexData, int vertexOffset, int primitiveCount, VertexDeclaration vertexDeclaration) 
        where T : struct
    {
        DrawMass(device, primitiveType, vertexOffset, primitiveCount, (batchVertexOffset, batchPrimitiveCount) =>
        {
            device.DrawUserPrimitives<T>(primitiveType, vertexData, batchVertexOffset, batchPrimitiveCount, vertexDeclaration);
        });
    }

    private static void DrawMass(GraphicsDevice device, PrimitiveType primitiveType, int vertexOffset, int primitiveCount, Action<int, int> draw)
    {
        // Grab the calculation functions for our primitive type.
        var calculateVertices = CalculateVertices[(int)primitiveType];
        var calculatePrimitives = CalculatePrimitives[(int)primitiveType];

        // Work out:

        // The maximum number of primitives our profile supports.
        var maxPrimitives = device.GraphicsProfile == GraphicsProfile.HiDef ? MaxPrimitivesHiDef : MaxPrimitivesReach;
        // How many vertices would be rendered for that number of primitives.
        var maxVertices = calculateVertices(maxPrimitives);
        // The number of vertices the user wants to draw.
        var vertexCount = calculateVertices(primitiveCount);
        // The index of the last vertex the user wants to draw.
        var endVertex = vertexOffset + vertexCount;

        // Go over the range in steps of 'maxVertices'.
        for (var i = vertexOffset; i < endVertex; i += maxVertices)
        {
            // Make sure we don't draw too many vertices.
            var batchEnd = i + maxVertices;
            if (batchEnd > endVertex)
                batchEnd = endVertex;
            var batchCount = batchEnd - i;

            // Work out the number of primitives again.
            var batchPrimitives = calculatePrimitives(batchCount);

            // Ask the calling method to draw what we have worked out.
            draw(i, batchPrimitives);
        }
    }
}
share|improve this answer
    
Cool stuff Jonathan! :) –  David Gouveia Apr 25 '12 at 17:10
    
@DavidGouveia haven't seen you around here in a while :). Sorry to snipe your answer; but I recently learnt that adding information to an answer is discouraged :(. –  Jonathan Dickinson Apr 25 '12 at 18:53
    
No problem :) And as usual, very elegant solution :P –  David Gouveia Apr 25 '12 at 21:31
    
I tried it, and it freezes, even with a smaller data. I don't quite understand what is wrong –  luis Apr 28 '12 at 23:01
    
I think the problem is that I am using DrawPrimitives instead of DrawUserPrimitives because the data does not change at all, and I would like to keep it in the GPU for faster usage and rendering, but your example uses DrawUserPrimitives which doesn't use a VertexBuffer, instead it copies all the data everytime it renders, which is the reason why it is so slow in this case. Do you have an idea for an approach using DrawPrimitives instead, I tried something but I am running into problems that VertexBuffer can only handle a limited number of items and 137 million is too much for it –  luis Apr 29 '12 at 7:29
show 2 more comments

The maximum number of primitives that you can draw with a single call in XNA while using the HiDef profile is 1048575 according to this article.

I've never tried it but since that limit is defined per draw call I think you should be able to draw more primitives by using separate draw calls. All of the DrawPrimitives type of methods take an offset and count into the buffer (e.g. startVertex and primitiveCount) so you can play with those values to draw different portions of the buffer in each call and keep incrementing the offset until everything is drawn.

You can also take a look into the PrimitiveBatch class available in this sample as it's pretty easy to use. Just increase the buffer size inside the class first. And while you're at it check this one too.

share|improve this answer
    
what about the wireframe sort of thing so that I can show only the vertices (joins) of the lines made by all the plotting points –  luis Mar 24 '12 at 2:43
    
How did we go from particles (usually represented as points) to vertices and wire-frames which are ways to represent meshes? –  ClassicThunder Mar 25 '12 at 0:09
    
@ClassicThunder I think this might be some form of a research project. –  Jonathan Dickinson Apr 25 '12 at 8:05
    
it is a research project –  luis Apr 29 '12 at 8:51
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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