There are four kinds of overarching operations that we perform using the command buffer: applying render state, uploading data, binding resources, and performing draws. Let's go through each of these and then tie it all together.
var myCommandBuffer = GraphicsDevice.AcquireCommandBuffer();
```
Note that it is an **error** to use the same command buffer on multiple threads. If you want to do multithreaded draw calls, acquire one command buffer per thread.
You can upload data to buffers and textures using a command buffer. Uploads are guaranteed to be thread-safe. Note that it is an **error** to upload data in the middle of a render pass.
All bindings and draw calls must be made within a render pass. You should think of a render pass as a set of draw calls rendering to the same group of textures.
Beginning a render pass requires a minimum of one ColorAttachmentInfo struct. Up to four ColorAttachmentInfos can be provided, and an optional DepthStencilAttachmentInfo can be provided as well.
The ColorAttachmentInfo struct always includes a texture. Additional info can be provided, like depth, layer, and level regions. You can use these to do things like render into a cube map or different depths of a 3D texture. The most common case is just providing a texture and a clear color. Note that a texture used in ColorAttachmentInfo *must* either be a swapchain texture or have had `TextureUsageFlags.ColorTarget` set on creation.
In this example, the render pass will render to the swapchain texture we acquired, and we will clear it to the cornflower blue color at the start of the render pass.
You can also clear the framebuffer by calling `CommandBuffer.Clear`. It's strongly recommended to clear when beginning a render pass if you need the render target cleared, but it is possible to clear mid-pass if you wish.
When we wish to use a graphics pipeline, we "bind" it, meaning that we make it active. We can only have one bound graphics pipeline at a time per command buffer.
Note that if you use multiple graphics pipelines that all share a render pass, you can call this method again and it will bind a new graphics pipeline without needing to end the render pass.
"Binding" a buffer makes it available to be used by draw calls. If you are doing instanced rendering, you will bind multiple vertex buffers, otherwise you will just bind one. You will need to bind an index buffer if you are doing indexed or instanced rendering, otherwise you will not bind one.
"Binding" a sampler makes it available to be used by the shaders in the currently bound graphics pipeline. In order to bind a sampler, you must provide a sampler object and a texture object.
```cs
myCommandBuffer.BindVertexSamplers(
new TextureSamplerBinding(myVertexTexture, myVertexSampler)
);
myCommandBuffer.BindFragmentSamplers(
new TextureSamplerBinding(myFragmentSamplerOne, myFragmentSamplerTwo)
);
```
Note that you may provide one or more samplers to a shader, and the samplers you provide will be indexed by the shader in the order they are provided to the function.
If your shader stages take uniforms, you can push them after binding a pipeline.
```cs
var myVertUniformOffset = myCommandBuffer.PushVertexShaderUniforms(myVertUniforms);
var myFragUniformOffset = myCommandBuffer.PushFragmentShaderUniforms(myFragUniforms);
```
The offsets you receive from these functions should be passed to draw calls. You can push these one at a time, but if you want to be clever, you can upload multiple uniform buffers at once and then manually increment your offsets at the appropriate draw calls.
Now that we've set up all this render state, we can start issuing draw calls.
To draw using a single vertex buffer and no index buffer, call `DrawPrimitives`. You must provide a starting vertex, a primitive count, and offset values for each shader stage. (If you do not provide uniforms for a particular stage, just use `0`.)
Note that the existence of the uniform offset pattern enables you to loop over a bunch of draw calls and update the uniforms without having to push uniforms every loop. Remember that you can greatly increase your performance by packing as much data into a single vertex buffer as possible.
To draw using a single vertex buffer and an index buffer, call `DrawIndexedPrimitives`. You must provide a starting vertex, a starting index, a primitive count, and offset values for each shader stage.
To draw using multiple vertex buffers and an index buffer, call `DrawInstancedPrimitives`. You must provide a starting vertex, a starting index, a primitive count, an instance count, and offset values for each shader stage.
When you need to change framebuffers or present to the screen, it's time to end the render pass.
```cs
myCommandBuffer.EndRenderPass();
```
## Copying Data
Sometimes you might want to copy the contents of a texture to another texture.
```cs
myCommandBuffer.CopyTextureToTexture(
new TextureSlice(myTexture, rectOne),
new TextureSlice(myOtherTexture, rectTwo)
Filter.Linear
);
```
Sometimes you might want to read the contents of an image.
```cs
myCommandBuffer.CopyTextureToBuffer(
new TextureSlice(myTexture, myRectangle),
myBuffer
)
```
Remember that you have to call `GraphicsDevice.Submit` and `GraphicsDevice.Wait` before the contents of the buffer are guaranteed to be filled. Then you can call `myBuffer.GetData` to get the image data.