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.