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

47 lines
3.3 KiB
Markdown

---
title: "Buffer"
date: 2021-01-25T14:41:43-08:00
weight: 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:
```cs
var myIndexBuffer = new Buffer(GraphicsDevice, BufferUsageFlags.Index, 64);
```
This creates a 64-byte buffer intended for use as an [index buffer](https://vulkan-tutorial.com/Vertex_buffers/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.
```cs
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.
```cs
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.