There are three kinds of overarching operations that we perform using the command buffer: applying render state, binding resources, and issuing draw commands. 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.
## Beginning the render pass
All bindings and draw calls must be made within a render pass. You should think of a render pass as being a set of draw calls made to the same group of render targets.
Beginning a render pass requires a minimum of three parameters: a render pass object, a framebuffer object, a rectangle representing the area of the framebuffer that will be rendered to. You may also optionally provide clear colors per color target, and a depth/stencil clear value for the depth/stencil target.
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();
```
## Presenting the Frame
"Presenting" is when we provide a final image that we want to show in our game window.
To present, we must provide a texture or texture slice, a filter, and an optional destination rectangle. If a destination rectangle is not provided the resulting image will be automatically scaled to the size of the game window.
Note that if you use multiple command buffers, you still only want to call `QueuePresent` exactly once per frame.
## 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.