MoonWorks-docs/content/Graphics/Resources/Buffer.md

3.3 KiB

title date weight
Buffer 2021-01-25T14:41:43-08:00 1

Buffers are basically containers for data. They are the bread and butter of any renderer.

3D models are represented as a series of vertices. Vertices always have a 3D position, but they can also contain additional information, such as normal vectors or texture coordinates. Buffers are how you let the renderer use this data.

Buffers have a pre-defined length and cannot be resized. You also need to tell MoonWorks.Graphics how you intend to use the buffer.

To create a buffer, you do this:

    var myIndexBuffer = new Buffer(GraphicsDevice, BufferUsageFlags.Index, 64);

This creates a 64-byte buffer intended for use as an index buffer. Index buffers allow the renderer to reuse vertices when drawing in 3D so you don't need to put a bunch of redundant data in your vertex buffer. Every 3D model format and exporter will give you index data along with vertex data.

To upload data to a buffer, you must use the SetBufferData method on a command buffer. We'll get to command buffers later, just know that you must upload data via command buffer so that you can safely use multithreading.

var myVertices = new VertexPositionNormalTexture[128];
var vertexSizeInBytes = Marshal.SizeOf<VertexPositionNormalTexture>();

// set your vertex data here!

var myVertexBuffer = new Buffer(GraphicsDevice, BufferUsageFlags.Vertex, 128 * vertexSizeInBytes);

myCommandBuffer.SetBufferData(myVertexBuffer, myVertices);

To get data from a buffer, you use the GetData method.

var pixelSizeInBytes = Marshal.SizeOf<Color>();
var myPixelData = new Color[width * height];
myPixelBuffer.GetData(myPixelData, width * height * pixelSizeInBytes);

There are some important things to note here. The buffer is not guaranteed to actually contain your data until Submit is called. Transferring memory to and from the GPU is expensive, so MoonWorks batches these operations aggressively. It is safe to assume that any command buffer commands you use after calling SetData will have access to the data, but it is not safe to assume that the buffer will contain the data you expect right after calling SetData. You need to call GraphicsDevice.Submit and then GraphicsDevice.Wait before the data is guaranteed to exist in the buffer. You should have a very good reason for doing this - this kind of behavior is called a "synchronization point" and it is extremely detrimental to performance.

Let's use an analogy: when you store data in a buffer using SetData, this is not equivalent to writing something down in a notebook and being able to look it up whenever you want. It's more like you just sealed a letter in an envelope and put it in the mailbox. Eventually when you call Submit the mailperson will come along, pick up your letter, and the postal service will deliver it to where it needs to go. When you use Wait this is basically equivalent to waiting around for the postal service to deliver you a response. You could have been walking your dog, cooking, playing a video game, or doing literally anything other than just sitting around by your mailbox! This is why using Wait is generally a bad idea unless you absolutely need that information, and if you do absolutely need it, that might indicate a need to structure your program differently anyway.