Advertisement

Dynamic Vertex Streams in SharpDX

Started by October 05, 2015 03:47 PM
8 comments, last by myvraccount 9 years, 2 months ago

Hi, I'm using SharpDX (virtually identical to SlimDX), and I'm having a bit of a problem with my vertex processing:

Initially, I set it up in a very brute-force method that just makes the vertex buffer and stream, and then draws everything, but it did this for every triangle for every frame! This was alright at first, because I just wanted to get it working as simply as possible, and I was just drawing a few triangles, but needless to say, as I started to get lots of triangles it began to slow way down.

So I tweaked some things, so that now it is supposed to draw a list of all the triangles in any one object at a time, and also should be creating the stream and buffer ahead of time, and potentially altering the data each frame, but not re-creating the whole thing.

However, now the objects don't get drawn. It's not drawing them black, it's just not drawing them at all. I know this, because when I draw other objects the old way, they still appear, even when they're behind other objects that I'm telling to display using the new method.

Anyway, I can't get you all the source code, but I wrote down a sort of a sampling of the significant changes I've made, and I can't tell what's caused the problem:

In the initialization function:

totalBytes = bytesPerVertex * triangles.Count() * 3;

stream = new DataStream(totalBytes, true, true);

buffer = new Buffer(device, stream, totalBytes, ResourceUsage.Dynamic, BindFlags.VertexBuffer, CpuAccessFlags.Write, ResourceOptionFlags.None, 0);

triangles is just a list of triangles I store in my own format.

I changed the ResourceUsage from Default to Dynamic.

I changed the CpuAccessFlags from None to Write.

I'm still using the same device and context and stuff as before, and they're already set up at this point, and still work with the old method.

I'm still using the same old matrix transformations for movement and projections as before, and I'm not backface culling, so none of that should affect it.

In the drawing function:

First, I change the data in the Point[] points array however necessary.

Point is a custom structure but just holds data, and hasn't changed since before.

Also, I'm sure the data is being calculated how I want because I'm getting the positions, colors, normals, etc. that I expect.

Then:

stream.Position = 0; // Reset the position each time

stream.WriteRange<Point>(points);

context.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList;

context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(buffer, bytesPerVertex, 0));

context.Draw(triangles.Count() * 3, 0);

This is all working the way it was before, except that I'm resetting the position (before I didn't need to because I recreated the whole thing each frame).

Anyway, if anyone can find any obvious flaw in this that would explain why it doesn't draw anything, I would really appreciate it. Thanks!

Looks like you are trying to keep an open handle into your vertex buffer and this is most likely the thing that is causing you problems. Every time you want to update your dynamic buffer (this could be every frame) you will need to call MapSubresource to get a writable handle (or stream) fill it with data and then call UnmapSubresource before attempting to use it in any Draw calls. I could be wrong, but I believe that the Buffer constructor that takes a stream expects a readable stream to populate the buffer at creation time and I am surprised it works at all with a dynamic buffer as they tend to be pretty fickle about when and how you can write them. Typically you will also want to use the MapMode.WriteDiscard flag in your map call indicating to direct3d that it can throw away the previous contents of the buffer. Hope this helps.
Advertisement

As @turnpast suggested, the `points` array is on the CPU and won't update anything on GPU side. You need to upload your CPU vertex buffer to GPU using `MapSubresource`, get a pointer to the writeable buffer, stream data to this memory location, then `UnMap` the buffer before drawing anything.

So let me see if I have this right. Are you saying that I should change the drawing function to this:

// (superfluous) stream.Position = 0; // Reset the position each time

DataBox box = context.MapSubresource(buffer, MapMode.Write, MapFlags.None);

box.Position = 0;

box.Data.WriteRange<Point>(points); // instead of stream.WriteRange<Point>(points);

box.Position = 0; // Why reset the position again? Won't it be reset the next time?

context.UnmapSubresource(buffer, 0);

context.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList;

context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(buffer, bytesPerVertex, 0));

context.Draw(triangles.Count() * 3, 0);

And then that should work? What about the DataStream stream = new DataStream(totalBytes, true, true); that I declared earlier - is it now obsolete? Then how will the new one know the parameters (like totalBytes)?

Note: I wrote this from an example I got at:

http://www.gamedev.net/topic/638642-dx11-issue-trying-to-call-contextmapsubresource/

If this is wrong, please correct me. Thanks.

Yeah you will want to get rid of your top level data stream and keep instead a top level buffer size variable (totalBytes). Here is some code I have used in the past:


DataStream ds;
context.MapSubresource(InstanceBuffer, MapMode.WriteDiscard, SharpDX.Direct3D11.MapFlags.None, out ds);
ds.WriteRange(sourceData, 0, count);
context.UnmapSubresource(InstanceBuffer, 0);

Would you mind please explaining:

- What's the InstanceBuffer, and why should I use that instead of my buffer?

- Why WriteDiscard instead of Write?

- Do I need to use the count in the WriteRange? Normally I just tell it where to start and it writes the whole thing.

Advertisement
Sorry, I just coppied my code and did not rename things to match yours. InstanceBuffer is the dynamic vertex buffer ('buffer' in your code I believe). You don't need to specify the offset/count in WriteRange if you are writing the entire contents of the source array. The data stream will be invalid will be invalid after the Unmap call so don't try to use it again.

The discard mode is a little more advanced, and related to performance. Typically dynamic graphics resources are notably slower than non-dynamic resources and the device driver dose a fair bit of extra work to keep them sane across the GPU/CPU boundary. Telling direct3d that it can throw away the previous contents of the buffer by specifying the discard flag allows for a number of internal optimizations that you will want to take advantage of unless you have a good reason not to. Probably not something you need to be too concerned with at first.

Do you mean that it would throw away all my vertex data so I'd have to overwrite it all? In that case I wouldn't want to, because for the vertices I'm trying to write at the moment anyway, they don't move and hardly anything changes about them, so most of the data doesn't need to be overwritten, just a little, and that's the only reason I need it writable. Am I interpreting this correctly?

Do you mean that it would throw away all my vertex data so I'd have to overwrite it all? ... Am I interpreting this correctly?

Yes, you have it right. And honestly it is not that important if you are just getting started, just do what seems right and worry about performance when it becomes a problem.

Well that improved the efficiency a LOT! Although it's still inefficient because if I have a detailed room to draw, it slows to be a little choppy, and if I wanted to make something very big it would probably slow way down.

However, it had previously been slowed down even more and for drawing only a very small amount of stuff, so it's still a big improvement, thanks.

This topic is closed to new replies.

Advertisement