Windows Store DirectX Game Template How Tos

Introduction

These how tos assume that you have downloaded the Windows Store DirectX Game Template and that you have started a new project in Visual Studio 2012 based upon the template. They further assume that you have followed the instructions that come with the template such that you have, e.g., configured the project dependencies and specified appropriate values in the app manifest and the RESW files.

Once you have learned how to do a task, you should find it easy to integrate what you have learned into your game. These how tos are intentionally limited in scope to a specific task and are intended mainly as a quick learning resource and a reference you can look back to when needed. There are also more complete samples on the Samples page which you can download and explore to learn more complicated tasks and techniques.

Table of Contents

How do I load and draw a sprite image?

  1. In Solution Explorer, right click on the WindowsStoreDirectXGame project and choose "Add"->"Existing Item...". Locate the image you wish to load, select it, and press OK. (Note: For Feature Level 9.1 compliance, images should be no larger than 2048x2048 and any image that uses BC1 or BC3 compression must have a width and height that is a power of 2).
  2. For our purposes we will assume that the image you added is named "CatTexture.dds" and that it is a DDS file. You can also load formats such as PNG, BMP, and JPEG. See the DirectX Tool Kit documentation on the WICTextureLoader component for more information.
  3. In Solution Explorer, right click on "CatTexture.dds" and choose "Properties". Ensure that "Content" is set to "Yes" and that "Item Type" is set to "Image". If not, change the "Configuration" to "All Configurations", the "Platform" to "All Platforms", and then modify the "Content" and "Item Type" values to match those specified above.
  4. In the WindowsStoreDirectXGame project, open Game.h and Game.cpp.
  5. At the end of the #include list in Game.h, add the following line:
    #include "Texture2D.h"
  6. In Game.h, at the end of the "private:" section of the Game class definition (right after m_gameRenderComponents but before the }; that ends the class definition), add a new member variable:
    Texture2D                      m_myTexture;
    Note that the tabbing is not mandatory; it is just used to make it easier to read and identify all of the member variables that are in the class. You can also use a name other than "m_myTexture". The m_ prefix is also optional and is used so that it is easy to identify that you are working with a class member variable rather than a local variable when you are writing code in a class member function.
  7. In Game.cpp, in the Game constructor's initialization list, after the default initialization of "m_gameRenderComponents()", add a comma and then on a new line add a default initialization of m_myTexture. It should now look like this:
    	m_gameRenderComponents(),
    	m_myTexture()
    {
    }
            
  8. In Game.cpp in the Game::CreateDeviceResources member function definition, after the definition of the local variable "std::vector<task<void>> allTasks;", add the following code:
    	allTasks.push_back(m_myTexture.LoadAsync(
    		m_device.Get(),
    		nullptr,
    		L"CatTexture.dds",
    		m_cancellationTokenSource.get_token(),
    		true
    		));
            
  9. The previous code is sufficient to load a texture. Next we will draw the texture.
  10. In Game.cpp, in the Game::Render member function definition, after the "// Draw stuff." comment, add the following code:
    	m_spriteBatch->Begin();
    	m_spriteBatch->Draw(m_myTexture.GetSRV(), XMFLOAT2(10.0f, 20.0f));
    	m_spriteBatch->End();
  11. The preceding code draws the texture at the (x,y) position (10,20). Note that SpriteBatch::Draw has several variations, many of which include optional parameters. The preceding code is the simplest form. You can now build the project and run it. You should see your image drawn at the coordinates (10,20) on top of the cornflower blue background.

How do I draw part of a sprite image?

  1. Refer to the "How do I load and draw a sprite image?" how to in order to load the image.
  2. At this point we should have a texture named m_myTexture loaded and we are ready to draw the texture.
  3. In Game.cpp, in the Game::Render member function definition, after the "// Draw stuff." comment, add the following code:
    	RECT sourceRect = {
    		static_cast<LONG>(m_myTexture.GetWidth() / 2.0f),	// left
    		static_cast<LONG>(m_myTexture.GetHeight() / 2.0f),	// top
    		static_cast<LONG>(m_myTexture.GetWidth()),		// right
    		static_cast<LONG>(m_myTexture.GetHeight())		// bottom
    	};
    
    	m_spriteBatch->Begin();
    	m_spriteBatch->Draw(m_myTexture.GetSRV(), XMFLOAT2(10.0f, 20.0f), &sourceRect);
    	m_spriteBatch->End();
    
  4. The preceding code draws the texture at the (x,y) position (10,20). In this case we are using a source rectangle to draw only a part of the image. Specifically, we are drawing the bottom right quarter of the image. Note that the RECT struct is not an {x, y, width, height } style setup but instead is a { left, top, right, bottom } structure. If you specified the desired width for the right parameter or the desired height for the bottom parameter then you could easily get into trouble.
    Imagine you wanted to draw a rectangle from a 2048x2048 texture. Let's say this rectangle starts at (128,256) and is 32 pixels wide by 64 pixels tall. If you passed in { 128, 256, 32, 64 } you would get an error because the right is less than the left (and the bottom is less than the top). Instead you should pass in { 128, 256, 128 + 32, 256 + 64 } (i.e. { 128, 256, 160, 320 } ) as the values for the RECT.

How do I play music?

  1. In Solution Explorer, right click on the WindowsStoreDirectXGame project and choose "Add"->"Existing Item...". Locate the music file you wish to load, select it, and press OK.
  2. For our purposes we will assume that the music file you added is named "My Song.wma" and that it is located in the main directory of the WindowsStoreDirectXGame project.
  3. In Solution Explorer, right click on "My Song.wma" and choose "Properties".
  4. Change the "Configuration" to "All Configurations" and the "Platform" to "All Platforms".
  5. Set "Content" to "Yes". Press OK.
  6. In the WindowsStoreDirectXGame project, open Game.cpp.
  7. In Game::CreateDeviceIndependentResources, after "m_audioEngine->InitializeMusicEngine();" add the following lines of code:
    		m_audioEngine->AddMusicToQueue(L"My Song.wma", -1);
    
    		m_audioEngine->PlayMusic();
  8. That's it. The -1 passed to AddMusicQueue is a loop count parameter that indicates that you wish for the music to loop indefinitely. If you passed 0, it would play once and move on (i.e. it would loop zero times). If you passed 1, it would play twice and move on (i.e. it would loop one time).

    The AudioEngine class also has a variety of other functions for managing the music queue. Note that playing music requires a version of Windows with Microsoft Media Foundation installed. N and KN versions might not have it installed. The template is setup to detect this and make the user aware of why music isn't playing and how to resolve the issue if they check the audio settings.

How should I organize my shaders?

When working with Direct3D 11 (especially for Windows Store games and apps), you should organize your custom HLSL shaders so that there is one shader per HLSL file. So if you have a vertex shader and a pixel shader, each should have its own HLSL file.

If you have code that should be shared between multiple shader files, you should put that code into an HLSLI file (a shader header file) and #include it at the beginning of each HLSL file that requires it. HLSLI files can include anything, such as cbuffer definitions, struct definitions (for shader input and output), Texture2D objects, SamplerState objects, and even function definitions.

Shaders for Windows Store apps and games must be pre-compiled. By convention, the entry point of a shader is the function named main. If you want to use a different entry point, then in Solution Explorer, right click on shader's HLSL file and choose "Properties". Change the "Configuration" to "All Configurations" and the "Platform" to "All Platforms". Then set the "Entrypoint Name" value to the name of the function at which you want the shader to begin execution.

How do I create an ID3D11Buffer to update an HLSL cbuffer? How do I update it? How do I bind it to the graphics pipeline?

  1. Add a new header file called "ShaderCBuffers.h" to the WindowsStoreDirectXGame project.
  2. Create a suitable CPU-side struct to mirror the cbuffer from the shader. For this example we will assume that you named the struct "MyCBuffer". Note: HLSL packing rules are different from C++ packing rules. You should keep these in mind when creating your CPU-side struct. Examples of how the HLSL compiler packs various structures can be found on the Packing Rules for Constant Variables page on MSDN. I prefer to use the packoffset keyword in my cbuffer definitions so that I know exactly how a cbuffer is packed. I then create a struct in C++ that is composed of public DirectX::XMFLOAT4 member variables. For example:
    cbuffer cbPerFrame : register(b0)
    {
        float BloomIntensity :  packoffset(c0.x);
        float BaseIntensity :   packoffset(c0.y);
    
        float BloomSaturation : packoffset(c0.z);
        float BaseSaturation :  packoffset(c0.w);
    }
    
    in HLSL becomes
    struct BloomCombineConstantBufferChangesEveryFrame
    {
    	// x - BloomIntensity ; y - BaseIntensity ;
    	// z - BloomSaturation ; w - BaseSaturation
    	DirectX::XMFLOAT4 values;
    };
    in C++. You could also use
    struct BloomCombineConstantBufferChangesEveryFrame
    {
        float32 BloomIntensity;
        float32 BaseIntensity;
        float32 BloomSaturation;
        float32 BaseSaturation;
    };
    in C++. Its a stylistic preference more than anything else; I prefer to see the XMFLOAT4 in my code since I know that that is how the GPU is interpreting the data.

    You can have up to 15 shader-constant buffers per shader stage (so if you had a vertex shader and a pixel shader bound, they each have their own 15 shader-constant buffers. That means you can use b0 through b15 with the register keyword. Within each of those cbuffers you can have up to 4096 four component constants. So you can use c0 through c4095 with the packoffset keyword. As you may have noticed earlier, with packoffset, you can specify individual components of each constant using .x .y .z and .w so you might put a float3 at c2.x followed by a float at c2.w. Or perhaps a float at c1.x followed by a float2 at c1.y followed by a float a c1.w. The components are always in xyzw order with packoffset.
  3. In the WindowsStoreDirectXGame project, open Game.h and Game.cpp.
  4. At the end of the #include list in Game.h, add the following line:
    #include "ShaderCBuffers.h"
  5. In Game.h, at the end of the "private:" section of the Game class definition (right after m_gameRenderComponents but before the }; that ends the class definition), add two new member variables:
        Microsoft::WRL::ComPtr<ID3D11Buffer>    m_bufferForMyCBufferShaderCBuffer;
        MyCBuffer                               m_myCBuffer;
                
    Note: The tabbing is not mandatory; it is just used to make it easier to read and identify all of the member variables that are in the class. The m_ prefix is also optional and is used so that it is easy to identify that you are working with a class member variable rather than a local variable when you are writing code in a class member function.
  6. In Game.cpp, in the Game constructor's initialization list, after the initialization of "m_gameRenderComponents", add a comma and then on a new line add a default initialization of m_bufferForMyCBufferShaderCBuffer and m_myCBuffer. It should now look like this:
    	m_gameRenderComponents(),
    	m_bufferForMyCBufferShaderCBuffer(),
    	m_myCBuffer()
    {
    }
            
  7. In Game.cpp in the Game::CreateDeviceResources member function definition, after the definition of the local variable "std::vector<task<void>> allTasks;", add the following code:
    	allTasks.push_back(concurrency::create_task([this]() -> void
    	{
    		// The = {} zeroes out the cbufferDesc struct's member variables.
    		D3D11_BUFFER_DESC cbufferDesc = {};
    		// We want to use this buffer as a cbuffer.
    		cbufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
    		// Default usage lets the GPU read and write, which fits our needs.
    		cbufferDesc.Usage = D3D11_USAGE_DEFAULT;
    		// The (size + 15) / 16 * 16 code ensures that the buffer size 
    		// will be a multiple of 16, as is required.
    		cbufferDesc.ByteWidth = (sizeof(MyCBuffer) + 15) / 16 * 16;
    
    		DX::ThrowIfFailed(
    			m_device->CreateBuffer(
    			&cbufferDesc,
    			nullptr,
    			&m_bufferForMyCBufferShaderCBuffer
    			), __FILEW__, __LINE__
    			);
    	}));
    That code creates a buffer in GPU memory that we can bind to the graphics pipeline when using a shader that uses the cbuffer. A cbuffer in HLSL code is a definition of variables you want to use in a shader, but just like a struct or class definition in C++, the declaration does not reserve any space in memory for an instance of the buffer. ID3D11Device::CreateBuffer is what reserves space in the GPU's memory for an instance of the cbuffer.
  8. When you want to update the buffer, first make sure that you have updated the value(s) in m_myCBuffer. Then, to update the data on the GPU use the following code:
    	// Update the cbuffer's data.
    	m_context->UpdateSubresource(
    		m_bufferForMyCBufferShaderCBuffer.Get(),
    		0, // A cbuffer buffer one has 1 subresource so this is always 0.
    		nullptr, // D3D11_BOX is not applicable for cbuffers so pass nullptr.
    		&m_myCBuffer, // The data we're updating the cbuffer with.
    		0, // Not applicable to cbuffers so always pass 0.
    		0  // Not applicable to cbuffers so always pass 0.
    		);
    Typically you would do this in
  9. To bind a buffer to the graphics pipeline for use by a pixel shader, use the following code:
    	// Set a pixel shader cbuffer. Use the VS, GS, DS, HS, or CS variants
    	// for the other shader types. For FL 9.x you can only use vertex and pixel shaders.
    	m_context->PSSetConstantBuffers(
    		0, // The which cbuffer register to set.
    		1, // Set one buffer.
    		m_bufferForMyCBufferShaderCBuffer.GetAddressOf()
    		);
    For a vertex shader, you would use ID3D11DeviceContext::VSSetConstantBuffers instead of PSSetConstantBuffers. Note also that the 0 above refers to the register number for the cbuffer.

How do I load a vertex shader? How do I bind it to the graphics pipeline?

  1. In Solution Explorer, right click on the WindowsStoreDirectXGame project and choose "Add"->"Existing Item...". Locate the vertex shader you wish to load, select it, and press OK.
    -OR-
    In Solution Explorer, right click on the WindowsStoreDirectXGame project and choose "Add"->"New Item...". Under "Visual C++", in "HLSL" select "Vertex Shader File (.hlsl)", then give it the name you desire, specify where it should be created, and press Add. Replace the default contents with your desired HLSL code.
  2. For our purposes we will assume that the shader you added is named "MyVertexShader.hlsl" and that it is located in the main directory of the WindowsStoreDirectXGame project. Note that "MyVertexShader.hlsl" will be compiled into a file called "MyVertexShader.cso".
  3. In Solution Explorer, right click on "MyVertexShader.hlsl" and choose "Properties".
  4. Change the "Configuration" to "All Configurations" and the "Platform" to "All Platforms".
  5. Ensure that the "Shader Type" is set to "Vertex Shader (/vs)".
  6. Set the "Shader Model" to the correct value. To support Feature Level 9.1 devices, all shaders that your game requires must be "Shader Model 4 Level 9_1 (/4_0_level_9_1)." You can have optional shaders that make use of higher shader models just so long as you check the feature level of the ID3D11Device (in the Game class it's stored in the m_featureLevel member variable) before loading any optional shaders to avoid loading them on devices that won't support them.
  7. If you are using a entry point that isn't named main then you should set the "Entrypoint Name". See How should I organize my shaders? for more details.
  8. If your shader contains any cbuffer buffers, follow the instructions at How do I create an ID3D11Buffer to update an HLSL cbuffer? How do I update it? How do I bind it to the graphics pipeline? to create any necessary buffers. That link also has instructions for updating and binding buffers to the graphics pipeline.
  9. In the WindowsStoreDirectXGame project, open Game.h and Game.cpp.
  10. At the end of the #include list in Game.h, add the following line:
    #include "VertexTypes.h"
  11. Unlike other shader types, a vertex shader has an input layout associated with it. The input layout is how you tell the graphics pipeline how the vertex data that is bound as the vertex buffer is laid out in memory. This lets the pipeline match up the semantics for the vertex data with the semantics for the input data that the vertex shader is expecting. It also helps catch errors in your program during resource creation (rather than at bind time) since the creation of the input layout will fail if the layout is missing elements that the shader requires.

    DirectXTK comes with a number of pre-defined vertex types complete with appropriate input layouts. You can find them in the VertexTypes.h header file. If your vertex data matches one of DirectXTK's types, you should just use that and save yourself the bother of creating your own D3D11_INPUT_ELEMENT_DESC array. If not then you will need to make one yourself. As an example, if you needed to declare an input layout that consisted of a position and two texture coordinates, you might create something like this:
    	D3D11_INPUT_ELEMENT_DESC inputElementPosTexTex[] =
    	{
    		{
    			"SV_Position",
    			0,
    			DXGI_FORMAT_R32G32B32_FLOAT,
    			0,
    			D3D11_APPEND_ALIGNED_ELEMENT,
    			D3D11_INPUT_PER_VERTEX_DATA,
    			0
    		},
    		{
    			"TEXCOORD",
    			0,
    			DXGI_FORMAT_R32G32_FLOAT,
    			0,
    			D3D11_APPEND_ALIGNED_ELEMENT,
    			D3D11_INPUT_PER_VERTEX_DATA,
    			0
    		},
    		{
    			"TEXCOORD",
    			1,
    			DXGI_FORMAT_R32G32_FLOAT,
    			0,
    			D3D11_APPEND_ALIGNED_ELEMENT,
    			D3D11_INPUT_PER_VERTEX_DATA,
    			0
    		},
    	};
    We shall use this example when creating the shader and input layout below. For more on input layouts, see: Create the Input-Layout Object
  12. In Game.h, at the end of the "private:" section of the Game class definition (right after m_gameRenderComponents but before the }; that ends the class definition), add two new member variables:
    	Microsoft::WRL::ComPtr<ID3D11InputLayout>	m_myVertexShaderInputLayout;
    	Microsoft::WRL::ComPtr<ID3D11VertexShader>	m_myVertexShader;
  13. In Game.cpp, in the Game constructor's initialization list, after the initialization of "m_gameRenderComponents", add a comma and then on a new line add a default initialization of m_myTexture. It should now look like this:
    	m_gameRenderComponents(),
    	m_myVertexShaderInputLayout(),
    	m_myVertexShader()
    {
    }
            
  14. In Game.cpp in the Game::CreateDeviceResources member function definition, after the definition of the local variable "std::vector<task<void>> allTasks;",
    1. If you are using a DirectXTK vertex type, e.g., the DirectXTK::VertexPositionTexture type, add the following code:
      	// Create and populate a vector of D3D11_INPUT_ELEMENT_DESC
      	// structs that matches the DirectXTK array but isn't const.
      	std::vector<D3D11_INPUT_ELEMENT_DESC> inputElementPosTexColVec(
      		DirectX::VertexPositionColorTexture::InputElements,
      		DirectX::VertexPositionColorTexture::InputElements +
      		DirectX::VertexPositionColorTexture::InputElementCount
      		);
      
      	// Create the vertex shader and corresponding input layout.
      	// The static_cast avoids warnings in x64 configurations
      	// about truncation which don't matter since we know we'll
      	// never exceed the uint32 size limit.
      	allTasks.push_back(m_basicLoader->LoadShaderAsync(
      		ref new Platform::String(L"MyVertexShader.cso"),
      		inputElementPosTexColVec.data(),
      		static_cast<uint32>(inputElementPosTexColVec.size()),
      		&m_myVertexShader,
      		&m_myVertexShaderInputLayout
      		)
      		);
      WinRT classes, such as BasicLoader, cannot take const parameters. Since DirectXTK declares the InputElements of all its vertex types as const we create a std::vector that we can pass.

      You could also use a const_cast on the DirectXTK vertex type's InputElements since the variant of BasicLoader::LoadShaderAsync that loads a vertex shader and creates its input layout does not modify the D3D11_INPUT_ELEMENT_DESC array that is passed in to it. Generally, though, you should always be suspicious of a const_cast and avoid them where possible. Here the small, one-time performance hit from creating the std::vector is preferable to worrying about the implementation details of BasicLoader and whether or not they will change in a way that would make a const_cast dangerous.
    2. If you are instead using your own input layout, add the following code:
      	// Create the vertex shader and corresponding input layout.
      	allTasks.push_back(m_basicLoader->LoadShaderAsync(
      		ref new Platform::String(L"SpriteVertexShader.cso"),
      		inputElementPosTexTex,
      		ARRAYSIZE(inputElementPosTexTex),
      		&m_myVertexShader,
      		&m_myVertexShaderInputLayout
      		)
      		);
    >
  15. You must bind the vertex shader to the graphics pipeline before you can use it to render anything. To bind the vertex shader to the graphics pipeline, use the following code:
    	// Set the type of primitive we'll be drawing. In this case
    	// we're drawing a triangle list, which is the most common
    	// primitive type.
    	m_context->IASetPrimitiveTopology(
    		D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST
    		);
    
    	// Set the input layout to be the input layout object that
    	// corresponds with our vertex shader.
    	m_context->IASetInputLayout(m_myVertexShaderInputLayout.Get());
    
    	// Bind the vertex shader to the graphics pipeline.
    	m_context->VSSetShader(
    		m_myVertexShader.Get(),
    		nullptr,
    		0
    		);
    If your shader contains any cbuffer buffers, follow the instructions at How do I create an ID3D11Buffer to update an HLSL cbuffer? How do I update it? How do I bind it to the graphics pipeline? to bind those to the graphics pipeline. The shader will expect that you have bound any cbuffers it requires. It will also expect that you have bound any textures it requires with one or more calls to ID3D11DeviceContext::VSSetShaderResources and also that you have bound any sampler states it requires with one or more calls to ID3D11DeviceContext::VSSetSamplers. Failure to properly bind something that is needed will result in either D3D errors (if nothing is bound at the required place) or in unexpected rendering results (if something is bound but not what is needed).

How do I load a pixel shader? How do I bind it to the graphics pipeline?

  1. In Solution Explorer, right click on the WindowsStoreDirectXGame project and choose "Add"->"Existing Item...". Locate the vertex shader you wish to load, select it, and press OK.
    -OR-
    In Solution Explorer, right click on the WindowsStoreDirectXGame project and choose "Add"->"New Item...". Under "Visual C++", in "HLSL" select "Vertex Shader File (.hlsl)", then give it the name you desire, specify where it should be created, and press Add. Replace the default contents with your desired HLSL code.
  2. For our purposes we will assume that the shader you added is named "MyPixelShader.hlsl" and that it is located in the main directory of the WindowsStoreDirectXGame project. Note that "MyPixelShader.hlsl" will be compiled into a file called "MyPixelShader.cso".
  3. In Solution Explorer, right click on "MyPixelShader.hlsl" and choose "Properties".
  4. Change the "Configuration" to "All Configurations" and the "Platform" to "All Platforms".
  5. Ensure that the "Shader Type" is set to "Vertex Shader (/vs)".
  6. Set the "Shader Model" to the correct value. To support Feature Level 9.1 devices, all shaders that your game requires must be "Shader Model 4 Level 9_1 (/4_0_level_9_1)." You can have optional shaders that make use of higher shader models just so long as you check the feature level of the ID3D11Device (in the Game class it's stored in the m_featureLevel member variable) before loading any optional shaders to avoid loading them on devices that won't support them.
  7. If you are using a entry point that isn't named main then you should set the "Entrypoint Name". See How should I organize my shaders? for more details.
  8. If your shader contains any cbuffer buffers, follow the instructions at How do I create an ID3D11Buffer to update an HLSL cbuffer? How do I update it? How do I bind it to the graphics pipeline? to create any necessary buffers. That link also has instructions for updating and binding buffers to the graphics pipeline.
  9. In the WindowsStoreDirectXGame project, open Game.h and Game.cpp.
  10. In Game.h, at the end of the "private:" section of the Game class definition (right after m_gameRenderComponents but before the }; that ends the class definition), add a new member variable:
    	Microsoft::WRL::ComPtr<ID3D11PixelShader>		m_myPixelShader;
  11. In Game.cpp, in the Game constructor's initialization list, after the initialization of "m_gameRenderComponents", add a comma and then on a new line add a default initialization of m_myTexture. It should now look like this:
    	m_gameRenderComponents(),
    	m_myPixelShader()
    {
    }
            
  12. In Game.cpp in the Game::CreateDeviceResources member function definition, after the definition of the local variable "std::vector<task<void>> allTasks;", add the following code:
    	allTasks.push_back(m_basicLoader->LoadShaderAsync(
    		ref new Platform::String(L"MyPixelShader.cso"),
    		&m_myPixelShader
    		)
    		);
  13. You must bind the pixel shader to the graphics pipeline before you can use it to render anything. To bind the pixel shader to the graphics pipeline, use the following code:
    	m_context->PSSetShader(
    		m_myPixelShader.Get(),
    		nullptr,
    		0
    		);
    While the pixel shader does not need to use all of the output of the shader that precedes it (typically the vertex shader), that preceding shader must output data with semantics that match up to the semantics for the input to the pixel shader. If it doesn't, you will get a D3D error.