This tutorial is part of a Collection: 03. DirectX 11 - Braynzar Soft Tutorials
rate up
0
rate down
8834
views
bookmark
14. Simple Font

I waited for this lesson until we have covered some things that need to be implimented in this lesson, such as blending and textures. As you may or may not know already, font in direct3d 11 is a pain in the ass to say the least. I don't know what the developers were thinking exactly, but I believe they have taken out the ID3DXFont interface from direct3d in order to direct people to two new API's, Direct2D and DirectWrite. However, the worst part about this is that Direct2D is not "interopable" with D3D 11, meaning you can't use them together directly... (A big thumbs down for microsoft). So anyway, enough complaining. There are a couple ways we can impliment font into D3D 11, and the one we will be learning about uses the two new API's microsoft wants us to use, because in fact, they could be very usefull and pretty cool, not to mention flexible (except for the fact we can't use them directly with a D3D 11 device...). Since we are not able to use them directly with a D3D 11, we will need to use them with a D3D 10.1 device, and swap between the two devices when rendering.

1373 downloads
##Introduction## This lesson builds off the blending lesson, with some slight modifications, which I will try to go over. But to be safe, you should look through this lessons code and the blending lessons code to see whats actually different. This is a longer lesson, and probably a little boring, but if you don't want to actually read through it all, or have no desire to learn how to synchronize api's, you can use the three functions i have made to impliment the font. Just remember to modify the code creating the direct3d 11 device and swapchain, and change the format of the backbuffer, as Direct2D seems to only be compatible with the BRGA format, and not the one we've been using, RGBA. Rendering font to the screen in direct3d 11 is not very quick, and in fact, i had a hard time figuring out how to do it, as there doesn't seem to be much on the internet actually explaining step by step how to do it, mostly they just say use GDI, Direct2D and DirectWrite, or make your own. Before I go on, I want to give credit to someone's sample from gamedev (I believe it was gamedev, as I did a LOT of research on the web and put it all together) which I used to get a D3D 10.1, D2D, and D3D 11 device to interoperate. Unfortunately, I don't remember who's sample it was, But I will be sure to update this if I find out. ##Font Rendering in Direct3D 11 (Using Direct2D with Direct3D 11)## I condenced the new code to render font into three functions (with a little modification in the initializeD3D function, mostly when creating the device and swapchain). We now have one function which is called from the initializeD3D function, which will initialize our direct3d 10.1 device, direct2d and directdraw. Another function called from the initscene function which will create a square which will be used to render the direct2d render target texture to, and create a shader resource view from the texture direct2d and direct3d 10.1 renders to, which will be used to texture that square. Using Direct2D with Direct3D 11 is not as simple as... Using Direct2D with Direct3D 11. On Microsoft's website, they mention that to replace the ID3DXFont interface, we should use the GDI or Direct2D and DirectWrite API's. GDI's font is messy and ugly, so we are stuck with Direct2D if we want a more flexible and nice looking font. However, Microsoft was kind enough to not make Direct2D work directly with Direct3D 11 (As if taking out the ID3DXFont interface wasn't enough...). I was just kidding about the nice thing. So, how do we use D2D in a D3D 11 app? We need to use a technique called surface sharing. This way, we can use direct2d with a direct3d 10.1 device to render to a surface, then using the direct3d 11 device, render that shared surface (which direct3d 10.1 and direct2d renders to) onto a square in screen space which overlays the entire scene. First I will EXPLAIN how to do this, then go through the code to SHOW you how to do it. ##Synchronized Surface Sharing## D3D 11, D3D 10.1, and D2D all use the DXGI 1.1. Because of this, we can use DXGI to create a render target which can be shared between the three API's. **The Adapter** When using Surface Sharing, we must specify an adapter when creating the devices, as just setting the argument to NULL will cause errors. We can get an adapter (we will just use the first adapter here) by enumerating the available adapters using the EnumAdapters1() method of the IDXGIFactory1 interface. We will set the first parameter to "0", which is the first adapter. The second parameter is a IDXGIAdapter1 to store it in. **Creating the D3D 10.1 and 11 Devices** First we will create our D3D 11 device and swapchain like we have been doing, except we will set the first parameter (the adapter to use) to the enumerated adapter stored in the IDXGIAdapter1 object we found. MAKE SURE D3D 10.1 USES THE SAME ADAPTER. The second parameter, the driver type, we HAVE to specify D3D_DRIVER_TYPE_UNKNOWN for the D3D 11 device, and then specify D3D10_DRIVER_TYPE_HARDWARE for the D3D 10.1 device. And for the fourth parameter (we keep the third NULL), the flags, We HAVE to specify D3D11_CREATE_DEVICE_BGRA_SUPPORT to use direct2d. We can optionally use (like we will in this lesson) the D3D11_CREATE_DEVICE_DEBUG to get a lot of usefull information when debugging (it will appear in the debug window, which is by default at the bottom of the visual studio environment). The fifth parameter when creating the D3D 10.1 device, the features level, We will specify D3D10_FEATURE_LEVEL_9_3 as anything higher does not seem to work, at least on my laptop. **D3D 11 Shared Texture** Next we will create a shared texture from our D3D 11 device, which D3D 10.1 will render onto. When creating this texture, in the texture description, we must make sure we set a couple things right. We must specify DXGI_FORMAT_B8G8R8A8_UNORM for the format For the MiscFlags member, we must set D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX to allow both the D3D 11 and D3D 10.1 to use this surface. When specifying this flage, it will support the IDXGIKeyedMutex interface, which we can then call IDXGIKeyedMutex::AcquireSync and IDXGIKeyedMutex::ReleaseSync for each device so the other device knows when the other one is using the texture or not. **D3D 11 Keyed Mutex** To use a keyed mutex, we need to create a IDXGIKeyedMutex object. This interface has two methods, IDXGIKeyedMutex::AcquireSync and IDXGIKeyedMutex::ReleaseSync. We can acquire an IDXGIKeyedMutex object for a texture by calling the IUnknown::QueryInterface method from the texture we created. The first paramter is the identity of the interface we need, IDXGIKeyedMutex, and the second parameter is the IDXGIKeyedMutex interface object that will hold a pointer to this texture. We can create a keyed mutex directly for this texture using D3D 11. But to render to this texture using D3D 10.1, we will need to create a shared resource IDXGIResource to this texture, create a handle to this shared resource, then create an IDXGISurface1 object that uses that handle to the shared resource, and create a keyed mutex for that IDXGISurface1 object, since we can't directly create a keyed mutex to this texture using D3D 10.1. **Shared Resource from the Texture** Since we can't create a keyed mutex directly to this texture from D3D 10.1, since this texture is a D3D 11 one, we need to create a shared resource (IDXGIResource) object, which we can then get a handle to, then create a shared surface using that handle to that resource, giving D3D 10.1 access to the texture. Basically what we are doing is getting a pointer to this texture, and storing it in an IDXGIResource object. We can create a shared resource (get a pointer) to a texture by calling the QueryInterface and setting the second parameter to the interface object we wish to store the pointer, in this case, an IDXGIResource object. **Shared Handle to the Texture** We will now need to create a handle to this shared resource (or pointer to the texture) for D3D 10.1. Using this handle, we can open the shared resource (IDXGIResource object) to this texture from D3D 10.1. We will create an IDXGISurface1 object to store the pointer to the shared resource. So whats actually happening is D3D 10.1 will be rendering to this surface, which is connected to the D3D 11 texture. To create the handle, we can call the OpenSharedResource() method from the ID3D10Device1 object. The first parameter is the handle to the shared resource, and the second is the resource to store the pointer in. **D3D 10.1 Keyed Mutex** We can finally create the keyed mutex for D3D 10.1 using the shared resource to the D3D 11 texture. We can do this by callign the QueryInterface() method of the IDXGISurface1 object which is basically a pointer to the D3D 11 texture. **Initializing D2D** Now that we have a keyed mutex for D3D 11 and D3D 10.1 for the D3D 11 texture, we can initialize D2D, and set it's render target to the shared resource we created with D3D 10.1 (the IDXGISurface1 object). We start by creating a D2D Factory, or ID2D1Factory object. We can do this by calling the function D2D1CreateFactory(). **The D2D Render Target Properties** To initialize D2D, all we really have to do is set its render target and create a brush. To set it's render target, we need to define the render target's properties by filling out a D2D1_RENDER_TARGET_PROPERTIES structure. The two members of this structure we will fill out are type and pixelFormat For the type member, we will set D2D1_RENDER_TARGET_TYPE_HARDWARE, so that our GPU will do the processing for D2D. We will use the D2D1::PixelFormat function to get the D2D pixel format of the render target. The first parameter is the size and arrangement of the color channels of the pixels, we will set this to DXGI_FORMAT_UNKNOWN, which is the default anyway. The second is the alpha mode. We can ignore this if we want the alpha channel to be ignored, but then the texture will be opaque, and we will not be able to see through the texture which is overlayed on top of our D3D scene. So of course we don't want this. There are a couple values we could put here, but we will put D2D1_ALPHA_MODE_PREMULTIPLIED, so that when we blend the texture, the background of the texture will be invisible (as we will clear the backgrounds color to black with a 0 alpha value), and anything D2D writes to the texture will be opaque, or whatever we want. **Creating the D2D Render Target** We will now create the render target for D2D, which will draw to the shared DXGI surface, which is basically a pointer to the D3D 11 texture. We can create the render target by calling the method CreateDxgiSurfaceRenderTarget() of the ID2D1Factory object. The first parameter is the IDXGISurface object we will be rendering to, the second parameter is the render targets properties, and the third is the returned render target ID2D1RenderTarget object. **The D2D Brush** D2D uses a "brush" which is a ID2D1SolidColorBrush object that we can use to write to a part of the screen. We need to create a brush so we can write our text to the screen. To do this, we can call the CreateSolidColorBrush() method of the ID2D1RenderTarget interface. The only two parameters here are for the color and the returned object to store the brush in. The first is a D2D color, and the second is a ID2D1SolidColorBrush object to store the brush color in. **Initializing DirectWrite** To render text to the screen, we will use the DirectWrite API with the D2D API. DirectWrite will tell D2D how to draw, and specifically, how to draw our text. To Initialize DW, we will create a DW Factory, IDWriteFactory object. We can create this by calling the function DWriteCreateFactory(). The first parameter is specifying if DW will be shared or isolated. We need to share it with D2D, so we will specify DWRITE_FACTORY_TYPE_SHARED. The second is the interface ID we will store it in, IDWriteFactory, and the last is the interface object we will stor it in, the IDWriteFactory object. **DirectWrite Text Format** We can create the text format by calling the CreateTextFormat() method of the IDWriteFactory interface. We will explain this function in a bit when we are going over the code. **DirectWrite Text Alignment** We are able to specify where DW's text is aligned in the square it is being drawn to. We can do this by calling the functions SetTextAlignment() and SetParagraphAlignment of the IDWriteTextFormat interface we created above. **Getting that Rendered Font in D3D 11** PHEW! Ok, now the hard, long, and boring stuff is over. What we need to do now is get the text D2D renders to show up on our screen, in front of our D3D scene, and always in the corner of the screen. So how do we do this? Well, we can create a shader resource from the shared texture which D3D 10 and D2D render to, and texture a square in D3D 11 with that shader resource, just like the shader resource we create from an image in a file. We can create a shader resource view from this texture by calling the CreateShaderResourceView() method of the ID3D11Device interface, where the first parameter is the resource which we will take the data from (the D3D shared texture). The second parameter is the description of the resource view, we can set it to NULL, which says to take all the data from the resource, instead of certain things. The last parameter is the returned shader resource view, which is a ID3D11ShaderResourceView object. To create the square is really easy, we have already covered that in one of the first lessons. Just make sure to use a separate vertex and index buffer to hold the square if you are using a vertex and index buffer for different kinds of objects, like we do in this lesson (the cube). Then you will set the vertex and index buffer (bind them to the IA stage of the pipeline) by calling IASetIndexBuffer() and IASetVertexBuffers(). **Rendering the Square in Screen Space** When we created the square, we set the vertices to -1.0 to 1.0 for the x and y axis, which coveres the entire screen in screen space. How do we render to screen space though? Thats easy, all we have to do is reset the WVP matrix before we render the square! **Swapping Between the D3D 11 and D3D 10.1 Devices** Only one device can use that texture at one time. That is what the keyed mutex thing is all about. What it does is acquire the texture for use, then releases it for another keyed mutex. We have two keyed mutex's for this texture, one for D3D 10.1 and one for D3D 11. To acquire the texture for a device, we call the AcquireSync() method of the IDXGIKeyedMutex object. The first parameter is the acquire key, and the second is an integer representing the number of miliseconds to wait for the texture (if it's currently in use by another keyed mutex) before trying to acquire it again. The second parameter is used because of multi-threading, where in one thread the D3D 11 device might be using the texture, and in another thread, the D3D 10.1 device might try to acquire it while its already in use. If your application is using a single thread (D3D11_CREATE_DEVICE_SINGLETHREADED was specified as a flag when creating your device), then the second parameter doesn't really mean anything. After we are done using the texture, we need to release it so that another device can use it if it needs. We release the texture by calling the ReleaseSync() method of the IDXGIKeyedMutex interface. The only parameter here is the release key. Now to explain the release and acquire keys. They are an integer, which the ReleaseSync() function sets. This is used so if you have multiple keyed mutex's, and you need to call them in order in a multi-threaded application (where otherwise the first AcquireSync() called will get to use the texture, even if it's supposed to use it after another device). The acquire key in the AcquireSync() parameter needs to match the release key, set by the ReleaseSync() function. (This is a really simple concept, and it feels like i'm complicating it). An example of this, in our application, is the D3D 11 device will call the AcquireSync() with the key being "0" (since thats what it starts out as). When it is done using the texture, it calls the ReleaseSync() function, setting the key to "1". When the D3D 10.1 device calls AcquireSync(), it's key (first parameter) is "1", and when it calls the ReleaseSync() function, it sets the key back to "0". Look at the code and you should be able to understand if this is too confusing for you ;) A downfall to swapping between devices, is there's a small perfomance hit, about 2 ms per frame from what someone said on gamedev... but it's not really too bad **Moving On** I'm not going to lie, i'm kinda glad that all that explaining is over, hehe. So now, let's move on to actually doing this! ##New Includes and Libraries## We will be using a Direct3D 10.1 device, so we must make the necessary includes and linkers. Then we are using DXGI for surface sharing, and finally Direct2D and DirectWrite. //Include and link appropriate libraries and headers// #pragma comment(lib, "d3d11.lib") #pragma comment(lib, "d3dx11.lib") #pragma comment(lib, "d3dx10.lib") ///////////////**************new**************//////////////////// #pragma comment (lib, "D3D10_1.lib") #pragma comment (lib, "DXGI.lib") #pragma comment (lib, "D2D1.lib") #pragma comment (lib, "dwrite.lib") ///////////////**************new**************//////////////////// #include <windows.h> #include <d3d11.h> #include <d3dx11.h> #include <d3dx10.h> #include <xnamath.h> ///////////////**************new**************//////////////////// #include <D3D10_1.h> #include <DXGI.h> #include <D2D1.h> #include <sstream> #include <dwrite.h> ///////////////**************new**************//////////////////// ##Global Declarations## We have a whole bunch of new interfaces. We pretty much went through each of these new interfaces up top, and the others you can probably figure out what they are for. The last one though, is a wide string variable, which will hold our text and pass it to D2D. ID3D10Device1 *d3d101Device; IDXGIKeyedMutex *keyedMutex11; IDXGIKeyedMutex *keyedMutex10; ID2D1RenderTarget *D2DRenderTarget; ID2D1SolidColorBrush *Brush; ID3D11Texture2D *BackBuffer11; ID3D11Texture2D *sharedTex11; ID3D11Buffer *d2dVertBuffer; ID3D11Buffer *d2dIndexBuffer; ID3D11ShaderResourceView *d2dTexture; IDWriteFactory *DWriteFactory; IDWriteTextFormat *TextFormat; std::wstring printText; ##New Functions## Like I mentioned above, I tried to separate the code so you can put it into your own application easily. We have three new functions, but I could have condensed them into two. The first function will initailize D3D10.1, D2D, and DirectWrite, using a shared surface. The only parameter here is the Adapter, since we need to make sure we use the same adapter as the D3D 11 Device. The second function will create a square and a shader resource view from the shared texture so we can render it onto the square. bool InitD2D_D3D101_DWrite(IDXGIAdapter1 *Adapter); void InitD2DScreenTexture(); void RenderText(std::wstring text); ##Enumerate the First Adapter## Jump down to the initializeD3D11 function. This is where the bulk and most important things of our lesson are. The first thing new that we do is enumerate a device. When we synchronize two devices, we must not specify NULL for the adapter parameter when creating our devices (usually this means the device will use the default adapter). We need to create a DXGI Factory, which we can then enumerate an adapter. We will just use the first adapter, which is the primary adapter. We can specify the first adapter by setting "0" as the first parameter in the EnumAdapters1() function. IDXGIFactory1 *DXGIFactory; HRESULT hr = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&DXGIFactory); IDXGIAdapter1 *Adapter; hr = DXGIFactory->EnumAdapters1(0, &Adapter); DXGIFactory->Release(); ##Initialize D3D 11## Here we initailize our D3D 11 device and swapchain. This function is not new, as we covered it in a previous lesson, but we have changed three of the parameters. Like a said a moment ago, We must actually specify an adapter when creating the device, as we are not able to use NULL to get the default adapter when synching devices. We will use the IDXGIAdapter1 object holding a pointer to the first adapter we found above. The second parameter is the type of device we will use. Before we had a hardware device, but we now need to use D3D_DRIVER_TYPE_UNKNOWN, as specifying hardware will cause errors (we will use hardware when creating our D3D 10.1 device though). The fourth parameter we have also changed from NULL. We now specify two flags (we really only need one, but using the debug flag will allow us to see extra information when debugging our app, very handy when we are running into unkown problems). The first flag is telling the device to spit out extra information about the device when debugging (be sure to remove it before the release build). The second flag is used when we will be using D2D, since D2D has a different format, this flag will make sure our device is compatible with the format of D2D (BGRA). Then we call our InitD2D_D3D101_DWrite() function, passing in our Adapter as the only parameter. After we call that, we release our adapter object (as D3D 11 and D3D 10.1 are both now initialized). //Create our Direct3D 11 Device and SwapChain////////////////////////////////////////////////////////////////////////// hr = D3D11CreateDeviceAndSwapChain(Adapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, D3D11_CREATE_DEVICE_DEBUG | D3D11_CREATE_DEVICE_BGRA_SUPPORT, NULL, NULL, D3D11_SDK_VERSION, &swapChainDesc, &SwapChain, &d3d11Device, NULL, &d3d11DevCon); //Initialize Direct2D, Direct3D 10.1, DirectWrite InitD2D_D3D101_DWrite(Adapter); //Release the Adapter interface Adapter->Release(); ##SwapChain Format## I forgot to mention that we need to change the format of the swapchain to DXGI_FORMAT_B8G8R8A8_UNORM. This is because D2D is only compatable with this format. bufferDesc.Width = Width; bufferDesc.Height = Height; bufferDesc.RefreshRate.Numerator = 60; bufferDesc.RefreshRate.Denominator = 1; bufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; ##Initializing D3D 10.1, D2D, and DirectWrite Using a Shared Surface## ( InitD2D_D3D101_DWrite() ) Ok, here is the toughest part of this lesson, initializing D3D 10.1, D2D, and DirectWrite, and have them use a shared surface to render to. I put all this into a function so you can use it easily in your own application. This is the entire function: bool InitD2D_D3D101_DWrite(IDXGIAdapter1 *Adapter) { //Create our Direc3D 10.1 Device/////////////////////////////////////////////////////////////////////////////////////// hr = D3D10CreateDevice1(Adapter, D3D10_DRIVER_TYPE_HARDWARE, NULL,D3D10_CREATE_DEVICE_DEBUG | D3D10_CREATE_DEVICE_BGRA_SUPPORT, D3D10_FEATURE_LEVEL_9_3, D3D10_1_SDK_VERSION, &d3d101Device ); //Create Shared Texture that Direct3D 10.1 will render on////////////////////////////////////////////////////////////// D3D11_TEXTURE2D_DESC sharedTexDesc; ZeroMemory(&sharedTexDesc, sizeof(sharedTexDesc)); sharedTexDesc.Width = Width; sharedTexDesc.Height = Height; sharedTexDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; sharedTexDesc.MipLevels = 1; sharedTexDesc.ArraySize = 1; sharedTexDesc.SampleDesc.Count = 1; sharedTexDesc.Usage = D3D11_USAGE_DEFAULT; sharedTexDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET; sharedTexDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX; hr = d3d11Device->CreateTexture2D(&sharedTexDesc, NULL, &sharedTex11); // Get the keyed mutex for the shared texture (for D3D11)/////////////////////////////////////////////////////////////// hr = sharedTex11->QueryInterface(__uuidof(IDXGIKeyedMutex), (void**)&keyedMutex11); // Get the shared handle needed to open the shared texture in D3D10.1/////////////////////////////////////////////////// IDXGIResource *sharedResource10; HANDLE sharedHandle10; hr = sharedTex11->QueryInterface(__uuidof(IDXGIResource), (void**)&sharedResource10); hr = sharedResource10->GetSharedHandle(&sharedHandle10); sharedResource10->Release(); // Open the surface for the shared texture in D3D10.1/////////////////////////////////////////////////////////////////// IDXGISurface1 *sharedSurface10; hr = d3d101Device->OpenSharedResource(sharedHandle10, __uuidof(IDXGISurface1), (void**)(&sharedSurface10)); hr = sharedSurface10->QueryInterface(__uuidof(IDXGIKeyedMutex), (void**)&keyedMutex10); // Create D2D factory/////////////////////////////////////////////////////////////////////////////////////////////////// ID2D1Factory *D2DFactory; hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(ID2D1Factory), (void**)&D2DFactory); D2D1_RENDER_TARGET_PROPERTIES renderTargetProperties; ZeroMemory(&renderTargetProperties, sizeof(renderTargetProperties)); renderTargetProperties.type = D2D1_RENDER_TARGET_TYPE_HARDWARE; renderTargetProperties.pixelFormat = D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED); hr = D2DFactory->CreateDxgiSurfaceRenderTarget(sharedSurface10, &renderTargetProperties, &D2DRenderTarget); sharedSurface10->Release(); D2DFactory->Release(); // Create a solid color brush to draw something with hr = D2DRenderTarget->CreateSolidColorBrush(D2D1::ColorF(1.0f, 1.0f, 0.0f, 1.0f), &Brush); //DirectWrite/////////////////////////////////////////////////////////////////////////////////////////////////////////// hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown**>(&DWriteFactory)); hr = DWriteFactory->CreateTextFormat( L"Script", NULL, DWRITE_FONT_WEIGHT_REGULAR, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, 24.0f, L"en-us", &TextFormat ); hr = TextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING); hr = TextFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_NEAR); d3d101Device->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_POINTLIST); return true; } ##Initializing D3D 10.1## The first thing we do in this function is initialize the D3D 10.1 device. Its pretty much the same as initializing D3D 11's device and swapchain, except with less parameters. You can probably guess what most of them do. Anyway, The first parameter is the passed in adapter, the second we will use specify D3D10_DRIVER_TYPE_HARDWARE to create a hardware device, and the fifth is the feature level. My laptop at least only lets me use feature level 9.3, you can try a higher level if you want. The other parameters are pretty easy. hr = D3D10CreateDevice1(Adapter, D3D10_DRIVER_TYPE_HARDWARE, NULL,D3D10_CREATE_DEVICE_DEBUG | D3D10_CREATE_DEVICE_BGRA_SUPPORT, D3D10_FEATURE_LEVEL_9_3, D3D10_1_SDK_VERSION, &d3d101Device ); ##Create a D3D 11 2D Texture## Next we will create the ID3D11Texture2D that will be used as the shared texture between API's. The only thing different about this texture is we must specify D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX for the MiscFlags member, and DXGI_FORMAT_B8G8R8A8_UNORM for the Format of the texture (more important saying to use DXGI_FORMAT_B8G8R8A8_UNORM as the format, is make sure its the SAME format as the D3D 11 Render Target). D3D11_TEXTURE2D_DESC sharedTexDesc; ZeroMemory(&sharedTexDesc, sizeof(sharedTexDesc)); sharedTexDesc.Width = Width; sharedTexDesc.Height = Height; sharedTexDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; sharedTexDesc.MipLevels = 1; sharedTexDesc.ArraySize = 1; sharedTexDesc.SampleDesc.Count = 1; sharedTexDesc.Usage = D3D11_USAGE_DEFAULT; sharedTexDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET; sharedTexDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX; hr = d3d11Device->CreateTexture2D(&sharedTexDesc, NULL, &sharedTex11); ##D3D 11 Keyed Mutex## Now we need to get a keyed mutex for this texture that D3D 11 will use to access it. We do this by calling the QueryInterface() function from the ID3D11Texture2D object. hr = sharedTex11->QueryInterface(__uuidof(IDXGIKeyedMutex), (void**)&keyedMutex11); ##Getting a Handle to the D3D 11 Texture## Now, since D3D 10.1 can't access a D3D 11 Texture directly, we need to do it indirectly, by using DXGI. We can create a IDXGIResource object which basically hold a pointer the the D3D 11 texture, by calling the QueryInterface() function again. Then we need to create a handle to this resource so that D3D 10.1 can access it. We create the handle to this resource by calling the GetSharedHandle method of the IDXGIResource interface. Then we release the shared resource since we now have a handle to the D3D 11 texture. IDXGIResource *sharedResource10; HANDLE sharedHandle10; hr = sharedTex11->QueryInterface(__uuidof(IDXGIResource), (void**)&sharedResource10); hr = sharedResource10->GetSharedHandle(&sharedHandle10); sharedResource10->Release(); ##Get a Keyed Mutex to the D3D 11 Texture for D3D 10.1## Next we have to get the keyed mutex for the D3D 11 texture for D3D 10.1. We do this by first opening the shared resource (The D3D 11 Texture) using the handle to the D3D 11 texture, and storing the pointer to it in an IDXGISurface1 object, which is what D3D 10.1 and D2D will render onto. We can do that by calling the function ID3D10Device1::OpenSharedResource. The first parameter is the handle to the shared resource, the second is the interface id of the object we will store the pointer in, and the third is the object we store the pointer in. After we have opened the shared resource and stored the pointer into the shared surface, we get a keyed mutex for the shared surface for D3D 10.1. We do that by calling the QueryInterface() method from the shared surface, and storing the pointer into a IDXGIKeyedMutex object. IDXGISurface1 *sharedSurface10; hr = d3d101Device->OpenSharedResource(sharedHandle10, __uuidof(IDXGISurface1), (void**)(&sharedSurface10)); hr = sharedSurface10->QueryInterface(__uuidof(IDXGIKeyedMutex), (void**)&keyedMutex10); ##Initializing D2D## Now we have both the D3D 10.1 and 11 devices initialized ok, we can now continue to initialize D2D and DirectWrite. The D2D resource is an ID2D1Factory object. To create a D2D resource (called Factory), we call the D2D1CreateFactory function. The first parameter specifies if D2D will use multiple threads or a single thread. Using a single thread says that no D2D function can return without finishing. In multiple threads, functions can return without actually finishing. The second parameter is the interface ID of the object which we will store the resource pointer into. The last parameter is the interface object we will store the pointer in. ID2D1Factory *D2DFactory; hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(ID2D1Factory), (void**)&D2DFactory); ##D2D Render Target Properties## Next we need to set the D2D render target properties. We do this by filling out a D2D1_RENDER_TARGET_PROPERTIES structure. The structure looks like this: struct D2D1_RENDER_TARGET_PROPERTIES { D2D1_RENDER_TARGET_TYPE type; D2D1_PIXEL_FORMAT pixelFormat; FLOAT dpiX; FLOAT dpiY; D2D1_RENDER_TARGET_USAGE usage; D2D1_FEATURE_LEVEL minLevel; }; Where each member is described below: **type -** *This is where we can specify if D2D will use hardware or software rendering. We will use hardware, so we set this to D2D1_RENDER_TARGET_TYPE_HARDWARE.* **pixelFormat -** *This is the pixel format and alpha mode of the render target. We need to create a D2D pixel format, so we will use the function D2D1::PixelFormat(). We will explain more about this function in a moment.* **dpiX -** *This is the horizontal dpi for the render target. We can specify 0 to use the default, or leave it blank, which we will do.* **dpiY -** *This is the vertical dpi for the render target. We can specify 0 to use the default, or leave it blank, which we will do.* **usage -** *This specifies how the render target is "remoted" acording to msdn. We can just leave it alone for our app.* **minLevel -** *This has to do with feature levels. We can set the minimum feature level to use for hardware rendering. If the feature level of the device is lower than what we specify here, D2D will use software rendering. If we leave this alone or set this to D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D will decide if the device has a high enough feature level, or is capable of rendering D2D, and if the hardware is not capable of rendering D2D, software rendering will be used. If we were to specify D2D1_RENDER_TARGET_TYPE_HARDWARE, and the hardware did not have a high enough feature level for D2D, or was not capable of rendering D2D, the create render target would fail. However, this field is not used for our particular render target, since we are creating a DXGI render target, as D3D 11, 10.1, and D2D are all built off the DXGI interface (which is why we can share surfaces between the devices using a DXGI surface and resource).* Ok, so the D2D1::PixelFormat() function will create and return a D2D pixel format, which is why we are able to set this function directly in the pixelFormat field. This function has two parameters, the first being the pixel format, which is the size and arrangment of the color channels in the render target (RED GREEN BLUE ALPHA, not particularly in that order when using D2D). We can specify DXGI_FORMAT_UNKNOWN, which is the default. The second parameter is the alpha mode. We can use a couple different values here. This parameter explains how the alpha channel in the render target is interpreted. We need to specify D2D1_ALPHA_MODE_PREMULTIPLIED so that whatever D2D draws will be opaque, but where D2D doesn't draw (D2D's background) will be invisible, since we are going to overlay its render target on our scene. There are three other values we could put here, but the value we put is the only one that works exactly the way we need it to. D2D1_RENDER_TARGET_PROPERTIES renderTargetProperties; ZeroMemory(&renderTargetProperties, sizeof(renderTargetProperties)); renderTargetProperties.type = D2D1_RENDER_TARGET_TYPE_HARDWARE; renderTargetProperties.pixelFormat = D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED); ##Creating the D2D Render Target## Since we need to share the render target with D3D 10.1, we need to create a DXGI render target, as both the D2D and D3D 10.1 api's are compatible with it. We can do this by calling the function ID2D1Factory::CreateDxgiSurfaceRenderTarget(). The first parameter is the DXGI surface which D2D will draw to. This is the shared surface we created above with D3D 10.1 (which is "linked" to the D3D 11 Texture). The second parameter is a pointer to the D2D1_RENDER_TARGET_PROPERTIES structure we filled out. The last is the returned D2D render target. So, To render onto the D3D 11 texture using D2D, we will first render to the D2D render target using D2D, which is a pointer to the DXGI surface (our shared surface between D2D and D3D 10.1), which is a pointer to the D3D 11 Texture. So by rendering onto the D2D render target, we are indirectly rendering to the D3D 11 texture. It really would be great if microsoft updated D2D to be compatible with D3D 11 so we don't have to be so indirect. After we create our D2D render target, we don't need to shared surface anymore (as it was basicaly the D3D 11 texture's pointer that was passed down all the way to the D2D render target), so we can release it, and we are also done creating the D2D stuff, so we can release the D2D factory. hr = D2DFactory->CreateDxgiSurfaceRenderTarget(sharedSurface10, &renderTargetProperties, &D2DRenderTarget); sharedSurface10->Release(); D2DFactory->Release(); ##The D2D Brush## You could look at D2D as being a sort of art kit, which contains the paint bucket, brush, and canvas. It uses whats called a brush to render a color to a section of the screen. We need to create the brush now, and all that involves doing is calling a method from the D2D render target, which creates a new brush (ID2D1SolidColorBrush object) and giving it a color. We can change the color of this brush any time by calling the ID2D1SolidColorBrush::SetColor() function, where the only parameter is a D2D color (D2D1_COLOR_F). hr = D2DRenderTarget->CreateSolidColorBrush(D2D1::ColorF(1.0f, 1.0f, 0.0f, 1.0f), &Brush); Initializing DirectWrite D2D could be looked at as the painters tools, while DirectWrite could be looked at as the artist in a certain way. DirectWrite will "guide" the D2D brush to color the scene. Specifically though, DirectWrite will tell D2D how to write text or font to the "canvas" (render target). This is where we will initialize DirectWrite (DW). The first thing we need to do is create a DW Factory, which will create our text format. We can do this by calling the function DWriteCreateFactory(), where the first parameter (like when we created the D2D Factory) specifies whether the factory will be shared or isolated. We specify shared, as we will share DW with D2D. The second parameter is the Interface ID of the object we are giving the pointer to the DW Factory object. And the last parameter is a pointer to the interface object we are sending the DW Factory pointer to. hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast(&DWriteFactory)); ##Creating a Font Format## Next we will use the created DW Factory to create a format for our font. We call the CreateTextFormat() method of the IDWriteFactory interface to do this. The function looks like this: HRESULT CreateTextFormat( [in] const WCHAR * fontFamilyName, IDWriteFontCollection * fontCollection, DWRITE_FONT_WEIGHT fontWeight, DWRITE_FONT_STYLE fontStyle, DWRITE_FONT_STRETCH fontStretch, FLOAT fontSize, [in] const WCHAR * localeName, [out] IDWriteTextFormat ** textFormat ) Where each parameter is described below: **fontFamilyName -** *This is a string of the font family we want to use. The example we use is "Script".* **fontCollection -** *This is a pointer to a font collection object we are getting the font from. We specify NULL to use the system fonts.* **fontWeight -** *This is the weight of the font (a higher value is more bold). It is a DWRITE_FONT_WEIGHT enumerated type. We use DWRITE_FONT_WEIGHT_REGULAR to just use the normal weight of the font family.* **fontStyle -** *This is a DWRITE_FONT_STYLE enumerated type, where the possible values are: DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STYLE_OBLIQUE, and DWRITE_FONT_STYLE_ITALIC.* **fontStretch -** *A value from the DWRITE_FONT_STRETCH enumerated type. This is a value from 0 to 9 (0 being undefined) where the smaller the value is, the more condenced the width of the characters are, and the higher the value, the more expanded or stretched the width of the characters are. Try playing with it.* **fontSize -** *This is a value representing the font size in DIP ("device-independent pixel). DIP is 1/96 of an inch (according to msdn, hehe). Basically if you've ever used microsoft word or wordpad or some other sort of text editing software like that, the font size is the same as the font size in those, like where the default is size 12 or whatever.* **localeName -** *A string specifying the language of the font. If you are able to follow this lesson, it's probably safe to put "en-us" here.* **textFormat -** *This is the returned pointer to a IDWriteTextFormat object which will store the font format in.* hr = DWriteFactory->CreateTextFormat( L"Script", NULL, DWRITE_FONT_WEIGHT_REGULAR, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, 24.0f, L"en-us", &TextFormat ); ##Font Alignment## Next we will tell DW how to align our font in the rectangle we will specify later. First we specify the horizontal alignment of our text. We do this by calling the function IDWriteTextFormat::SetTextAlignment() and specifying a value from the DWRITE_TEXT_ALIGNMENT enumeration type. To align the text to the left of the rectangle, we can specify DWRITE_TEXT_ALIGNMENT_LEADING, otherwise we could center it or align it to the right of the triangle. You can look the unermation values up though. Next we specify the vertical alignment, which they call paragraph alignment. We do this by calling the function IDWriteTextFormat::SetParagraphAlignment(). We set the parameter to a value from the DWRITE_PARAGRAPH_ALIGNMENT enumeration type. To get the paragraph to align to the top of the rectangle, we specify DWRITE_PARAGRAPH_ALIGNMENT_NEAR. We could have also specified DWRITE_PARAGRAPH_ALIGNMENT_CENTER to center the paragraph in the center of the rectangle, or DWRITE_PARAGRAPH_ALIGNMENT_FAR to align the paragraph to the bottom of the rectangle. hr = TextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING); hr = TextFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_NEAR); ##Keeping D3D 10.1 Debug Output Quiet ;)## Although we will not be drawing anything to the screen directly with the D3D 10.1 device in this lesson, we will need to set the primitive topology anyway to stop the Debug window from spitting out warnings about the topology not being set./p> d3d101Device->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_POINTLIST); ##Initializing the Scene to Display D2D## ( InitD2DScreenTexture() ) This is where we will create a square which will overlay our scene and store it in an index and vertex buffer. Then create a shader resource view from the shared texture which D2D and D3D 10.1 will render to. We can then use this shader resource view to texture our square which overlays our scene, and apply blending so that only whatever D2D paints to the texture (the font) will be visible. (blending will be done in the next new function) void InitD2DScreenTexture() { //Create the vertex buffer Vertex v[] = { // Front Face Vertex(-1.0f, -1.0f, -1.0f, 0.0f, 1.0f), Vertex(-1.0f, 1.0f, -1.0f, 0.0f, 0.0f), Vertex( 1.0f, 1.0f, -1.0f, 1.0f, 0.0f), Vertex( 1.0f, -1.0f, -1.0f, 1.0f, 1.0f), }; DWORD indices[] = { // Front Face 0, 1, 2, 0, 2, 3, }; D3D11_BUFFER_DESC indexBufferDesc; ZeroMemory( &indexBufferDesc, sizeof(indexBufferDesc) ); indexBufferDesc.Usage = D3D11_USAGE_DEFAULT; indexBufferDesc.ByteWidth = sizeof(DWORD) * 2 * 3; indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; indexBufferDesc.CPUAccessFlags = 0; indexBufferDesc.MiscFlags = 0; D3D11_SUBRESOURCE_DATA iinitData; iinitData.pSysMem = indices; d3d11Device->CreateBuffer(&indexBufferDesc, &iinitData, &d2dIndexBuffer); D3D11_BUFFER_DESC vertexBufferDesc; ZeroMemory( &vertexBufferDesc, sizeof(vertexBufferDesc) ); vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT; vertexBufferDesc.ByteWidth = sizeof( Vertex ) * 4; vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; vertexBufferDesc.CPUAccessFlags = 0; vertexBufferDesc.MiscFlags = 0; D3D11_SUBRESOURCE_DATA vertexBufferData; ZeroMemory( &vertexBufferData, sizeof(vertexBufferData) ); vertexBufferData.pSysMem = v; hr = d3d11Device->CreateBuffer( &vertexBufferDesc, &vertexBufferData, &d2dVertBuffer); //Create A shader resource view from the texture D2D will render to, //So we can use it to texture a square which overlays our scene d3d11Device->CreateShaderResourceView(sharedTex11, NULL, &d2dTexture); } ##Creating the Overlying Square## The first thing we do in this new function is create the square which will overylay on top of our scene. None of this is new, but we are storing it in another vertex and index buffer, which is not the same vertex and index buffer we use to hold our cubes vertices and indices. Because of this, we must set the correct vertex and index buffer to the IA stage of the pipeline before we render the geometry we want. You will see this when we get to rendering our scene. Vertex v[] = { Vertex(-1.0f, -1.0f, -1.0f, 0.0f, 1.0f), Vertex(-1.0f, 1.0f, -1.0f, 0.0f, 0.0f), Vertex( 1.0f, 1.0f, -1.0f, 1.0f, 0.0f), Vertex( 1.0f, -1.0f, -1.0f, 1.0f, 1.0f), }; DWORD indices[] = { 0, 1, 2, 0, 2, 3, }; D3D11_BUFFER_DESC indexBufferDesc; ZeroMemory( &indexBufferDesc, sizeof(indexBufferDesc) ); indexBufferDesc.Usage = D3D11_USAGE_DEFAULT; indexBufferDesc.ByteWidth = sizeof(DWORD) * 2 * 3; indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; indexBufferDesc.CPUAccessFlags = 0; indexBufferDesc.MiscFlags = 0; D3D11_SUBRESOURCE_DATA iinitData; iinitData.pSysMem = indices; d3d11Device->CreateBuffer(&indexBufferDesc, &iinitData, &d2dIndexBuffer); D3D11_BUFFER_DESC vertexBufferDesc; ZeroMemory( &vertexBufferDesc, sizeof(vertexBufferDesc) ); vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT; vertexBufferDesc.ByteWidth = sizeof( Vertex ) * 4; vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; vertexBufferDesc.CPUAccessFlags = 0; vertexBufferDesc.MiscFlags = 0; D3D11_SUBRESOURCE_DATA vertexBufferData; ZeroMemory( &vertexBufferData, sizeof(vertexBufferData) ); vertexBufferData.pSysMem = v; hr = d3d11Device->CreateBuffer( &vertexBufferDesc, &vertexBufferData, &d2dVertBuffer); ##Create a Shader Resource View From the Shared Texture## Shader resource views are not necessarily a new concept, as we have already covered this in the textures lesson, when we created a new shader resource view from an image in a file. However, this time we are not creating it from a file (which actually is a tiny bit more simple). We are creating it from the shared D3D 11 texture that D2D and D3D 10.1 renders to. We can do this by calling the function ID3D11Device::CreateShaderResourceView(), where the first parameter is the resource we are getting the shader resource view from (our shared texture), the second parameter is used if we were to fill out a D3D11_SHADER_RESOURCE_VIEW_DESC structure describing how we want the resource data. We can set this parameter to NULL to get all the data from the resource. The last parameter is a pointer to an ID3D11ShaderResourceView object which will store a pointer to the resource (our shared texture). Now we can use that ID3D11ShaderResourceView to texture the square we just made. d3d11Device->CreateShaderResourceView(sharedTex11, NULL, &d2dTexture); ##Calling the Function to Initialize Rendering D2D In Our Scene## Here we just call that function from above in our InitScene() function. InitD2DScreenTexture(); ##Modify the Blending Description## For the shared texture to be rendered the way we want on the square (only the text shows up, and the rest of the texture does "cover up" our scene), we need to change the blending description to make sure that the alpha value in the shared texture makes the texture's background "transparent". We can do this by setting the DestBlend member to D3D11_BLEND_INV_SRC_ALPHA. You are definitely free to play around with this to see if theres something better, but from what i've played with, this works the best. D3D11_RENDER_TARGET_BLEND_DESC rtbd; ZeroMemory( &rtbd, sizeof(rtbd) ); rtbd.BlendEnable = true; rtbd.SrcBlend = D3D11_BLEND_SRC_COLOR; ///////////////**************new**************//////////////////// rtbd.DestBlend = D3D11_BLEND_INV_SRC_ALPHA; ///////////////**************new**************//////////////////// rtbd.BlendOp = D3D11_BLEND_OP_ADD; rtbd.SrcBlendAlpha = D3D11_BLEND_ONE; rtbd.DestBlendAlpha = D3D11_BLEND_ZERO; rtbd.BlendOpAlpha = D3D11_BLEND_OP_ADD; rtbd.RenderTargetWriteMask = D3D10_COLOR_WRITE_ENABLE_ALL; ##Clean Up## I forgot to mention not to forget to clean up all the new interfaces we have. void CleanUp() { //Release the COM Objects we created SwapChain->Release(); d3d11Device->Release(); d3d11DevCon->Release(); renderTargetView->Release(); squareVertBuffer->Release(); squareIndexBuffer->Release(); VS->Release(); PS->Release(); VS_Buffer->Release(); PS_Buffer->Release(); vertLayout->Release(); depthStencilView->Release(); depthStencilBuffer->Release(); cbPerObjectBuffer->Release(); Transparency->Release(); CCWcullMode->Release(); CWcullMode->Release(); ///////////////**************new**************//////////////////// d3d101Device->Release(); keyedMutex11->Release(); keyedMutex10->Release(); D2DRenderTarget->Release(); Brush->Release(); BackBuffer11->Release(); sharedTex11->Release(); DWriteFactory->Release(); TextFormat->Release(); d2dTexture->Release(); ///////////////**************new**************//////////////////// } ##Rendering the Font## We can call this function when we want to render the font to our scene. Of course you can change the parameters to whatever you'd like, such as accepting a float value (which we will do in the next lesson, to keep track of FPS), or maybe the position of the text (that way you could call this function more than once if you want text on your screen in different places). void RenderText(std::wstring text) { //Release the D3D 11 Device keyedMutex11->ReleaseSync(0); //Use D3D10.1 device keyedMutex10->AcquireSync(0, 5); //Draw D2D content D2DRenderTarget->BeginDraw(); //Clear D2D Background D2DRenderTarget->Clear(D2D1::ColorF(0.0f, 0.0f, 0.0f, 0.0f)); //Create our string std::wostringstream printString; printString << text; printText = printString.str(); //Set the Font Color D2D1_COLOR_F FontColor = D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f); //Set the brush color D2D will use to draw with Brush->SetColor(FontColor); //Create the D2D Render Area D2D1_RECT_F layoutRect = D2D1::RectF(0, 0, Width, Height); //Draw the Text D2DRenderTarget->DrawText( printText.c_str(), wcslen(printText.c_str()), TextFormat, layoutRect, Brush ); D2DRenderTarget->EndDraw(); //Release the D3D10.1 Device keyedMutex10->ReleaseSync(1); //Use the D3D11 Device keyedMutex11->AcquireSync(1, 5); //Use the shader resource representing the direct2d render target //to texture a square which is rendered in screen space so it //overlays on top of our entire scene. We use alpha blending so //that the entire background of the D2D render target is "invisible", //And only the stuff we draw with D2D will be visible (the text) //Set the blend state for D2D render target texture objects d3d11DevCon->OMSetBlendState(Transparency, NULL, 0xffffffff); //Set the d2d Index buffer d3d11DevCon->IASetIndexBuffer( d2dIndexBuffer, DXGI_FORMAT_R32_UINT, 0); //Set the d2d vertex buffer UINT stride = sizeof( Vertex ); UINT offset = 0; d3d11DevCon->IASetVertexBuffers( 0, 1, &d2dVertBuffer, &stride, &offset ); WVP = XMMatrixIdentity(); cbPerObj.WVP = XMMatrixTranspose(WVP); d3d11DevCon->UpdateSubresource( cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0 ); d3d11DevCon->VSSetConstantBuffers( 0, 1, &cbPerObjectBuffer ); d3d11DevCon->PSSetShaderResources( 0, 1, &d2dTexture ); d3d11DevCon->PSSetSamplers( 0, 1, &CubesTexSamplerState ); d3d11DevCon->RSSetState(CWcullMode); //Draw the second cube d3d11DevCon->DrawIndexed( 6, 0, 0 ); } ##Let D3D 11 Release the Texture So D3D 10.1 Can Acquire It## The first thing this function does is release the shared texture from D3D 11's use. Notice we Release it with the "0" key for the parameter. After D3D 11 has released the shared texture, we acquire it with our D3D 10.1 device. Notice how we Acquire it with the "0" key for the parameter. The second parameter says wait 5 milliseconds before trying to acquire it again if its current release key is not "0". If our program was running in multiple threads, and rendering with, say, three devices (D3D 11, D3D 10.1-1, D3D 10.1-2), and our program was set up so that first D3D 11 renders to the texture, then we need D3D 10.1-1 to render some things, and finally D3D 10.1-2 needs to use the shared texture to finish the rendering (in that order). Well, maybe in one thread, D3D 11 calls AcquireSync(0, 5); and acquires the texture, so it's using the texture now. In another thread, D3D 10.1-1 calls AcquireSync(1, 5);, but D3D 11 is still using the texture, so it waits 5 milliseconds before calling AcquireSync(1, 5); again (so it keeps calling this function every 5 ms). Finally, D3D 11 finishes using the texture and calls ReleaseSync(1);, but in another thread, D3D 10.1-2 calls AcquireSync(2, 5); while D3D 10.1-1 is still waiting the 5 ms before calling the function again. Luckily, we have these acquire and release keys, so that since D3D 11 has released the texture with the "1" key, the texture is waiting to be acquired with the "1" key. D3D 10.1-2 called the AcquireSync(2, 5);, so it will not get to acquire the texture until the texture has been released with the "2" key. Now D3D 10.1-1 calls the AcquireSync(1, 5); again, and is able to acquire the texture since it was finally released with the "1" key. After using the texture, D3D 10.1-1 calls ReleaseSync(2);. Since now the texture has been released with the "2" key, D3D 10.1-2 will finally be able to acquire the texture after it calls the AcquireSync(2, 5); again. D3D 10.1-2 does its stuff with the texture and calls ReleaseSync(0); to release the texture with the "0" key, which D3D 11 can then acquire the texture again by calling AcquireSync(0, 5);. This ensures that the devices will not acquire the texture out of order when using multiple threads. Whew, I hope my explination didn't complicate your understanding of the purpose of acquiring and releasing a shared texture. //Release the D3D 11 Device keyedMutex11->ReleaseSync(0); //Use D3D10.1 device keyedMutex10->AcquireSync(0, 5); ##Start Drawing With D2D## Now that we have acquired the texture with our D3D 10.1 device, we need to tell D2D to start drawing. We do this with the ID2D1RenderTarget::BeginDraw() function. //Draw D2D content D2DRenderTarget->BeginDraw(); ##Clear the Shared Textures Background## Now we use D2D to cleare the shared targets background to black with a ZERO alpha. Try using 0.5f or 1.0f as the alpha value to see what happenes (the last of the four color values). We can clear the render target (in this case the shared texture) by calling the ID2DRenderTarget::Clear() function, where the only parameter is a D2D1_COLOR_F structure created by calling the function D2D1::ColorF(). //Clear D2D Background D2DRenderTarget->Clear(D2D1::ColorF(0.0f, 0.0f, 0.0f, 0.0f)); ##Create the String (Text) That Will Be Rendered To the Screen## This is where we create the string which will be rendered to the screen. If you don't know much about strings, we are using the wostringstream type which will allow us to input a "stream" of strings into it (this is so we can put strings together, like some text, then maybe an intiger, then maybe a line break to start a new line of text ("\n"). We will do this in the next lesson when displaying the FPS). Then we put that string stream into the printText string, which will be drawn to the screen, or more, "painted". //Create our string std::wostringstream printString; printString << text; printText = printString.str(); ##Set the D2D Brush Color## The next thing we will do is create a color, and set the D2D brush to that color. We create a D2D1_COLOR_F called FontColor, and set it by calling the function D2D1::ColorF(). We give it a white color (make sure the alpha value, the last of the 4 values is not 0.0f, or you will not see the font because of our blending). After we create the color, we set the D2D brush to that color by calling the function ID2D1SolidColorBrush::SetColor(), and using the color we just created as the only parameter. //Set the Font Color D2D1_COLOR_F FontColor = D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f); //Set the brush color D2D will use to draw with Brush->SetColor(FontColor); ##Create a Rectangle That the Font Will Be Drawn In## This line creates a rectangle (D2D1_RECT_F) that will be used to specify where on the screen the text is rendered to. We create the rectangle by calling D2D1::RectF(), where the first parameter is the left side of the rectangle from the left side of the client area in pixels. The second parameter is the distance in pixels from the top of the rectangle to the top of the client area. The third is basically the width of the rectangle, while the last parameter is the height of the rectangle. //Create the Text Render Area D2D1_RECT_F layoutRect = D2D1::RectF(0, 0, Width, Height); ##Draw the Font## Now, after ALL that, we can FINALLY draw the font! (however this is not the last step unfortunately...). We can draw the text by calling the function ID2D1RenderTarget::DrawText(): void DrawText( [in] WCHAR *string, UINT stringLength, [in] IDWriteTextFormat *textFormat, [ref] const D2D1_RECT_F &layoutRect, [in] ID2D1Brush *defaultForegroundBrush, D2D1_DRAW_TEXT_OPTIONS options = D2D1_DRAW_TEXT_OPTIONS_NONE, DWRITE_MEASURING_MODE measuringMode = DWRITE_MEASURING_MODE_NATURAL ); Where each parameter is described below: **string -** *This is the string we want to display.* **stringLength -** *This is the length of the string. We find the length of the string by calling scslen().* **textFormat -** *This is where DirectWrite finally comes in. This is the text format we created with DirectWrite when initializing.* **layoutRect -** *This is a rectangle describing a rectangle on the client area we are to render our text to. We will put the rectangle we created above here.* **defaultForegroundBrush -** *This is a pointer to the brush we created when initializing D2D. We set its color above.* **options -** *This is a D2D1_DRAW_TEXT_OPTIONS enumerated type saying if the text should be snapped to the pixels and if the text should be clipped if it goes outside the rectangle in the above parameter. As you can see, this method is option, where the default value D2D1_DRAW_TEXT_OPTIONS_NONE is used if this parameter is not specified. The default parameter says that the text should be snapped to pixels, and that the text will not be clipped if it goes outside the rectangle.* **measuringMode -** *This is a DWRITE_MEASURING_MODE enumerated type, and has to do with measuring the text with glyph metrics for formating. It is also an optional parameter, where the default value DWRITE_MEASURING_MODE_NATURAL is used if it is not specified.* //Draw the Text D2DRenderTarget->DrawText( printText.c_str(), wcslen(printText.c_str()), TextFormat, layoutRect, Brush ); ##Stop Drawing With D2D## Next we tell D2D to stop drawing by calling the function ID2D1RenderTarget->EndDraw(). D2DRenderTarget->EndDraw(); Release the Shared Texture From D3D 10.1 and Acquire It With D3D 11 We explained above whats going on when releasing and acquiring the texture. Here we release the texture from D3D 10.1 with the "1" key, and acquire it with the "1" key by D3D 11. //Release the D3D10.1 Device keyedMutex10->ReleaseSync(1); //Use the D3D11 Device keyedMutex11->AcquireSync(1, 5); ##Set the Blend State## After we have drawn the text to the shared texture with D2D, we need to present it to the screen. We will do this by texturing a square with the shader resource created from that shared texture, and put the square "over" our scene, overlay it. Just overlaying the textured square isn't enough though, we need to make sure that its "see through", except for the text we rendered to it. This is where we will set the blending state which will blend the texture to whatever's behind it depending on the alpha value of the shader resource we are using to texture the cube. Remember we cleared the shared texture to 0 alpha, and drew the text to the texture using a 1 alpha. This way, only the text will be visible, while the rest will be completely transparent! We already know about how to set the blend state, so this is where we do it. //Set the blend state for D2D render target texture objects d3d11DevCon->OMSetBlendState(Transparency, NULL, 0xffffffff); ##Bind the Square to the IA## Before when we bound the vertex and index buffer of our geometry to the IA, we did it when initializing our scene. However, We have more than one vertex and index buffer now, so we need to set the vertex and index buffer every time right before we draw our geometry. //Set the d2d Index buffer d3d11DevCon->IASetIndexBuffer( d2dIndexBuffer, DXGI_FORMAT_R32_UINT, 0); //Set the d2d vertex buffer UINT stride = sizeof( Vertex ); UINT offset = 0; d3d11DevCon->IASetVertexBuffers( 0, 1, &d2dVertBuffer, &stride, &offset ); ##Render the Textured Square In "Screen Space"## Now, we talked about (in previous lessons) how to render geometry and position it in world space. But how do we actually draw geometry in screen space, where it will always stay in screen space no matter where we move or go in our 3D world? Well, the answer is actually much more simple than you might think. All we have to do, is reset the WVP and send it to the shaders! Our screen space goes from -1.0f to 1.0f on the x-axis and -1.0f to 1.0f on the y axis. In screen space there is no depth, so the value you put for the z-axis when creating the vertex buffer shouldn't matter. WVP = XMMatrixIdentity(); cbPerObj.WVP = XMMatrixTranspose(WVP); d3d11DevCon->UpdateSubresource( cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0 ); d3d11DevCon->VSSetConstantBuffers( 0, 1, &cbPerObjectBuffer ); ##Set the Shader Resource (Shared Texture) and Sampler State## One last thing before we render our square, we need to make sure its textured with our shared texture. All we have to do is send the shader resource view created with the shared texture to the PS stage of the pipeline. Then we set the sampler state. All the geometry in our scene use the same sampler state, but we are being explicit by making sure its set before rendering. If the sampler state is not set, it will use the default sampler state. d3d11DevCon->PSSetShaderResources( 0, 1, &d2dTexture ); d3d11DevCon->PSSetSamplers( 0, 1, &CubesTexSamplerState ); ##Set the Render State, Then Draw the Square!## The last thing we do in this function, is render the square. First we set the Render state to do Clockwise backface culling, meaning geometry rendered Clockwise is facing away from the camera, and will be culled. We have rendered our geometry in Counter Clockwise order (actually by mistake, but it doesn't really matter), so that means it is facing us by default (according to the newly set clockwise backface culling render state). Then we finally render our square. d3d11DevCon->RSSetState(CWcullMode); //Draw the second cube d3d11DevCon->DrawIndexed( 6, 0, 0 ); ##The RenderScene() Function!## The last of our new code is in this function, and... (i'm really happy about this...) theres not much new code! I'm not even going to go through it line by line, i'm going to let you look through the code to see. The first thing thats new compared to our old lessons, is that we have now set the vertex and index buffer for our cubes in the render scene, before we draw them. This is because like i state above, we now have more than one vertex and index buffer, and must set the correct vertex and index buffer before we draw our geometry. Towards the bottom of this function, you can see the new line, which makes a call to the render text function, which will use D2D to draw the text to the shared device, then use the shared device (or actually the shader resource made from it) to texture a square that will overlay our scene! In this lesson, we just call the function with a single string for the parameter. However, next lesson, we will display the FPS, so we will need to add another parameter to take an intiger variable, representing the FPS. void DrawScene() { //Clear our render target and depth/stencil view float bgColor[4] = {(0.0f, 0.0f, 0.0f, 0.0f)}; d3d11DevCon->ClearRenderTargetView(renderTargetView, bgColor); d3d11DevCon->ClearDepthStencilView(depthStencilView, D3D11_CLEAR_DEPTH|D3D11_CLEAR_STENCIL, 1.0f, 0); //Set our Render Target d3d11DevCon->OMSetRenderTargets( 1, &renderTargetView, depthStencilView ); //Set the default blend state (no blending) for opaque objects d3d11DevCon->OMSetBlendState(0, 0, 0xffffffff); ///////////////**************new**************//////////////////// //Set the cubes index buffer d3d11DevCon->IASetIndexBuffer( squareIndexBuffer, DXGI_FORMAT_R32_UINT, 0); //Set the cubes vertex buffer UINT stride = sizeof( Vertex ); UINT offset = 0; d3d11DevCon->IASetVertexBuffers( 0, 1, &squareVertBuffer, &stride, &offset ); ///////////////**************new**************//////////////////// //Set the WVP matrix and send it to the constant buffer in effect file WVP = cube1World * camView * camProjection; cbPerObj.WVP = XMMatrixTranspose(WVP); d3d11DevCon->UpdateSubresource( cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0 ); d3d11DevCon->VSSetConstantBuffers( 0, 1, &cbPerObjectBuffer ); d3d11DevCon->PSSetShaderResources( 0, 1, &CubesTexture ); d3d11DevCon->PSSetSamplers( 0, 1, &CubesTexSamplerState ); d3d11DevCon->RSSetState(CWcullMode); d3d11DevCon->DrawIndexed( 36, 0, 0 ); WVP = cube2World * camView * camProjection; cbPerObj.WVP = XMMatrixTranspose(WVP); d3d11DevCon->UpdateSubresource( cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0 ); d3d11DevCon->VSSetConstantBuffers( 0, 1, &cbPerObjectBuffer ); d3d11DevCon->PSSetShaderResources( 0, 1, &CubesTexture ); d3d11DevCon->PSSetSamplers( 0, 1, &CubesTexSamplerState ); d3d11DevCon->RSSetState(CWcullMode); d3d11DevCon->DrawIndexed( 36, 0, 0 ); ///////////////**************new**************//////////////////// RenderText(L"Hello World"); ///////////////**************new**************//////////////////// //Present the backbuffer to the screen SwapChain->Present(0, 0); } This lesson took a lot longer than I wish it would have needed, but now we can finally render text to the screen! PLEASE feel free to coment or let me know if i've done something wrong, you've found errors, or know more efficient ways of doing things. I would GREATLY appreciate any sort of feed back on this one, as it took a while to put together ;) ##Exercise:## 1. Play with the format of the text. 2. Try playing with the D2D brush, and rendering the shared texture to the cubes. Here's the final code: main.cpp: //Include and link appropriate libraries and headers// #pragma comment(lib, "d3d11.lib") #pragma comment(lib, "d3dx11.lib") #pragma comment(lib, "d3dx10.lib") ///////////////**************new**************//////////////////// #pragma comment (lib, "D3D10_1.lib") #pragma comment (lib, "DXGI.lib") #pragma comment (lib, "D2D1.lib") #pragma comment (lib, "dwrite.lib") ///////////////**************new**************//////////////////// #include <windows.h> #include <d3d11.h> #include <d3dx11.h> #include <d3dx10.h> #include <xnamath.h> ///////////////**************new**************//////////////////// #include <D3D10_1.h> #include <DXGI.h> #include <D2D1.h> #include <sstream> #include <dwrite.h> ///////////////**************new**************//////////////////// //Global Declarations - Interfaces// IDXGISwapChain* SwapChain; ID3D11Device* d3d11Device; ID3D11DeviceContext* d3d11DevCon; ID3D11RenderTargetView* renderTargetView; ID3D11Buffer* squareIndexBuffer; ID3D11DepthStencilView* depthStencilView; ID3D11Texture2D* depthStencilBuffer; ID3D11Buffer* squareVertBuffer; ID3D11VertexShader* VS; ID3D11PixelShader* PS; ID3D10Blob* VS_Buffer; ID3D10Blob* PS_Buffer; ID3D11InputLayout* vertLayout; ID3D11Buffer* cbPerObjectBuffer; ID3D11BlendState* Transparency; ID3D11RasterizerState* CCWcullMode; ID3D11RasterizerState* CWcullMode; ID3D11ShaderResourceView* CubesTexture; ID3D11SamplerState* CubesTexSamplerState; ///////////////**************new**************//////////////////// ID3D10Device1 *d3d101Device; IDXGIKeyedMutex *keyedMutex11; IDXGIKeyedMutex *keyedMutex10; ID2D1RenderTarget *D2DRenderTarget; ID2D1SolidColorBrush *Brush; ID3D11Texture2D *BackBuffer11; ID3D11Texture2D *sharedTex11; ID3D11Buffer *d2dVertBuffer; ID3D11Buffer *d2dIndexBuffer; ID3D11ShaderResourceView *d2dTexture; IDWriteFactory *DWriteFactory; IDWriteTextFormat *TextFormat; std::wstring printText; ///////////////**************new**************//////////////////// //Global Declarations - Others// LPCTSTR WndClassName = L"firstwindow"; HWND hwnd = NULL; HRESULT hr; const int Width = 300; const int Height = 300; XMMATRIX WVP; XMMATRIX cube1World; XMMATRIX cube2World; XMMATRIX camView; XMMATRIX camProjection; XMMATRIX d2dWorld; XMVECTOR camPosition; XMVECTOR camTarget; XMVECTOR camUp; XMMATRIX Rotation; XMMATRIX Scale; XMMATRIX Translation; float rot = 0.01f; //Function Prototypes// bool InitializeDirect3d11App(HINSTANCE hInstance); void CleanUp(); bool InitScene(); void UpdateScene(); void DrawScene(); ///////////////**************new**************//////////////////// bool InitD2D_D3D101_DWrite(IDXGIAdapter1 *Adapter); void InitD2DScreenTexture(); void RenderText(std::wstring text); ///////////////**************new**************//////////////////// bool InitializeWindow(HINSTANCE hInstance, int ShowWnd, int width, int height, bool windowed); int messageloop(); LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); //Create effects constant buffer's structure// struct cbPerObject { XMMATRIX WVP; }; cbPerObject cbPerObj; //Vertex Structure and Vertex Layout (Input Layout)// struct Vertex //Overloaded Vertex Structure { Vertex(){} Vertex(float x, float y, float z, float u, float v) : pos(x,y,z), texCoord(u, v){} XMFLOAT3 pos; XMFLOAT2 texCoord; }; D3D11_INPUT_ELEMENT_DESC layout[] = { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }, }; UINT numElements = ARRAYSIZE(layout); int WINAPI WinMain(HINSTANCE hInstance, //Main windows function HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { if(!InitializeWindow(hInstance, nShowCmd, Width, Height, true)) { MessageBox(0, L"Window Initialization - Failed", L"Error", MB_OK); return 0; } if(!InitializeDirect3d11App(hInstance)) //Initialize Direct3D { MessageBox(0, L"Direct3D Initialization - Failed", L"Error", MB_OK); return 0; } if(!InitScene()) //Initialize our scene { MessageBox(0, L"Scene Initialization - Failed", L"Error", MB_OK); return 0; } messageloop(); CleanUp(); return 0; } bool InitializeWindow(HINSTANCE hInstance, int ShowWnd, int width, int height, bool windowed) { typedef struct _WNDCLASS { UINT cbSize; UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HANDLE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR lpszClassName; } WNDCLASS; WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; wc.cbClsExtra = NULL; wc.cbWndExtra = NULL; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 2); wc.lpszMenuName = NULL; wc.lpszClassName = WndClassName; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); if (!RegisterClassEx(&wc)) { MessageBox(NULL, L"Error registering class", L"Error", MB_OK | MB_ICONERROR); return 1; } hwnd = CreateWindowEx( NULL, WndClassName, L"Lesson 4 - Begin Drawing", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, width, height, NULL, NULL, hInstance, NULL ); if (!hwnd) { MessageBox(NULL, L"Error creating window", L"Error", MB_OK | MB_ICONERROR); return 1; } ShowWindow(hwnd, ShowWnd); UpdateWindow(hwnd); return true; } bool InitializeDirect3d11App(HINSTANCE hInstance) { //Describe our SwapChain Buffer DXGI_MODE_DESC bufferDesc; ZeroMemory(&bufferDesc, sizeof(DXGI_MODE_DESC)); bufferDesc.Width = Width; bufferDesc.Height = Height; bufferDesc.RefreshRate.Numerator = 60; bufferDesc.RefreshRate.Denominator = 1; bufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; //Describe our SwapChain DXGI_SWAP_CHAIN_DESC swapChainDesc; ZeroMemory(&swapChainDesc, sizeof(DXGI_SWAP_CHAIN_DESC)); swapChainDesc.BufferDesc = bufferDesc; swapChainDesc.SampleDesc.Count = 1; swapChainDesc.SampleDesc.Quality = 0; swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapChainDesc.BufferCount = 1; swapChainDesc.OutputWindow = hwnd; swapChainDesc.Windowed = TRUE; swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; ///////////////**************new**************//////////////////// // Create DXGI factory to enumerate adapters/////////////////////////////////////////////////////////////////////////// IDXGIFactory1 *DXGIFactory; HRESULT hr = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&DXGIFactory); // Use the first adapter IDXGIAdapter1 *Adapter; hr = DXGIFactory->EnumAdapters1(0, &Adapter); DXGIFactory->Release(); //Create our Direct3D 11 Device and SwapChain////////////////////////////////////////////////////////////////////////// hr = D3D11CreateDeviceAndSwapChain(Adapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, D3D11_CREATE_DEVICE_DEBUG | D3D11_CREATE_DEVICE_BGRA_SUPPORT, NULL, NULL, D3D11_SDK_VERSION, &swapChainDesc, &SwapChain, &d3d11Device, NULL, &d3d11DevCon); //Initialize Direct2D, Direct3D 10.1, DirectWrite InitD2D_D3D101_DWrite(Adapter); //Release the Adapter interface Adapter->Release(); ///////////////**************new**************//////////////////// //Create our BackBuffer and Render Target hr = SwapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), (void**)&BackBuffer11 ); hr = d3d11Device->CreateRenderTargetView( BackBuffer11, NULL, &renderTargetView ); //Describe our Depth/Stencil Buffer D3D11_TEXTURE2D_DESC depthStencilDesc; depthStencilDesc.Width = Width; depthStencilDesc.Height = Height; depthStencilDesc.MipLevels = 1; depthStencilDesc.ArraySize = 1; depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; depthStencilDesc.SampleDesc.Count = 1; depthStencilDesc.SampleDesc.Quality = 0; depthStencilDesc.Usage = D3D11_USAGE_DEFAULT; depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL; depthStencilDesc.CPUAccessFlags = 0; depthStencilDesc.MiscFlags = 0; //Create the Depth/Stencil View d3d11Device->CreateTexture2D(&depthStencilDesc, NULL, &depthStencilBuffer); d3d11Device->CreateDepthStencilView(depthStencilBuffer, NULL, &depthStencilView); return true; } ///////////////**************new**************//////////////////// bool InitD2D_D3D101_DWrite(IDXGIAdapter1 *Adapter) { //Create our Direc3D 10.1 Device/////////////////////////////////////////////////////////////////////////////////////// hr = D3D10CreateDevice1(Adapter, D3D10_DRIVER_TYPE_HARDWARE, NULL,D3D10_CREATE_DEVICE_DEBUG | D3D10_CREATE_DEVICE_BGRA_SUPPORT, D3D10_FEATURE_LEVEL_9_3, D3D10_1_SDK_VERSION, &d3d101Device ); //Create Shared Texture that Direct3D 10.1 will render on////////////////////////////////////////////////////////////// D3D11_TEXTURE2D_DESC sharedTexDesc; ZeroMemory(&sharedTexDesc, sizeof(sharedTexDesc)); sharedTexDesc.Width = Width; sharedTexDesc.Height = Height; sharedTexDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; sharedTexDesc.MipLevels = 1; sharedTexDesc.ArraySize = 1; sharedTexDesc.SampleDesc.Count = 1; sharedTexDesc.Usage = D3D11_USAGE_DEFAULT; sharedTexDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET; sharedTexDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX; hr = d3d11Device->CreateTexture2D(&sharedTexDesc, NULL, &sharedTex11); // Get the keyed mutex for the shared texture (for D3D11)/////////////////////////////////////////////////////////////// hr = sharedTex11->QueryInterface(__uuidof(IDXGIKeyedMutex), (void**)&keyedMutex11); // Get the shared handle needed to open the shared texture in D3D10.1/////////////////////////////////////////////////// IDXGIResource *sharedResource10; HANDLE sharedHandle10; hr = sharedTex11->QueryInterface(__uuidof(IDXGIResource), (void**)&sharedResource10); hr = sharedResource10->GetSharedHandle(&sharedHandle10); sharedResource10->Release(); // Open the surface for the shared texture in D3D10.1/////////////////////////////////////////////////////////////////// IDXGISurface1 *sharedSurface10; hr = d3d101Device->OpenSharedResource(sharedHandle10, __uuidof(IDXGISurface1), (void**)(&sharedSurface10)); hr = sharedSurface10->QueryInterface(__uuidof(IDXGIKeyedMutex), (void**)&keyedMutex10); // Create D2D factory/////////////////////////////////////////////////////////////////////////////////////////////////// ID2D1Factory *D2DFactory; hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(ID2D1Factory), (void**)&D2DFactory); D2D1_RENDER_TARGET_PROPERTIES renderTargetProperties; ZeroMemory(&renderTargetProperties, sizeof(renderTargetProperties)); renderTargetProperties.type = D2D1_RENDER_TARGET_TYPE_HARDWARE; renderTargetProperties.pixelFormat = D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED); hr = D2DFactory->CreateDxgiSurfaceRenderTarget(sharedSurface10, &renderTargetProperties, &D2DRenderTarget); sharedSurface10->Release(); D2DFactory->Release(); // Create a solid color brush to draw something with hr = D2DRenderTarget->CreateSolidColorBrush(D2D1::ColorF(1.0f, 1.0f, 0.0f, 1.0f), &Brush); //DirectWrite/////////////////////////////////////////////////////////////////////////////////////////////////////////// hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown**>(&DWriteFactory)); hr = DWriteFactory->CreateTextFormat( L"Script", NULL, DWRITE_FONT_WEIGHT_REGULAR, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, 24.0f, L"en-us", &TextFormat ); hr = TextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING); hr = TextFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_NEAR); d3d101Device->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_POINTLIST); return true; } ///////////////**************new**************//////////////////// void CleanUp() { //Release the COM Objects we created SwapChain->Release(); d3d11Device->Release(); d3d11DevCon->Release(); renderTargetView->Release(); squareVertBuffer->Release(); squareIndexBuffer->Release(); VS->Release(); PS->Release(); VS_Buffer->Release(); PS_Buffer->Release(); vertLayout->Release(); depthStencilView->Release(); depthStencilBuffer->Release(); cbPerObjectBuffer->Release(); Transparency->Release(); CCWcullMode->Release(); CWcullMode->Release(); ///////////////**************new**************//////////////////// d3d101Device->Release(); keyedMutex11->Release(); keyedMutex10->Release(); D2DRenderTarget->Release(); Brush->Release(); BackBuffer11->Release(); sharedTex11->Release(); DWriteFactory->Release(); TextFormat->Release(); d2dTexture->Release(); ///////////////**************new**************//////////////////// } ///////////////**************new**************//////////////////// void InitD2DScreenTexture() { //Create the vertex buffer Vertex v[] = { // Front Face Vertex(-1.0f, -1.0f, -1.0f, 0.0f, 1.0f), Vertex(-1.0f, 1.0f, -1.0f, 0.0f, 0.0f), Vertex( 1.0f, 1.0f, -1.0f, 1.0f, 0.0f), Vertex( 1.0f, -1.0f, -1.0f, 1.0f, 1.0f), }; DWORD indices[] = { // Front Face 0, 1, 2, 0, 2, 3, }; D3D11_BUFFER_DESC indexBufferDesc; ZeroMemory( &indexBufferDesc, sizeof(indexBufferDesc) ); indexBufferDesc.Usage = D3D11_USAGE_DEFAULT; indexBufferDesc.ByteWidth = sizeof(DWORD) * 2 * 3; indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; indexBufferDesc.CPUAccessFlags = 0; indexBufferDesc.MiscFlags = 0; D3D11_SUBRESOURCE_DATA iinitData; iinitData.pSysMem = indices; d3d11Device->CreateBuffer(&indexBufferDesc, &iinitData, &d2dIndexBuffer); D3D11_BUFFER_DESC vertexBufferDesc; ZeroMemory( &vertexBufferDesc, sizeof(vertexBufferDesc) ); vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT; vertexBufferDesc.ByteWidth = sizeof( Vertex ) * 4; vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; vertexBufferDesc.CPUAccessFlags = 0; vertexBufferDesc.MiscFlags = 0; D3D11_SUBRESOURCE_DATA vertexBufferData; ZeroMemory( &vertexBufferData, sizeof(vertexBufferData) ); vertexBufferData.pSysMem = v; hr = d3d11Device->CreateBuffer( &vertexBufferDesc, &vertexBufferData, &d2dVertBuffer); //Create A shader resource view from the texture D2D will render to, //So we can use it to texture a square which overlays our scene d3d11Device->CreateShaderResourceView(sharedTex11, NULL, &d2dTexture); } ///////////////**************new**************//////////////////// bool InitScene() { ///////////////**************new**************//////////////////// InitD2DScreenTexture(); ///////////////**************new**************//////////////////// //Compile Shaders from shader file hr = D3DX11CompileFromFile(L"Effects.fx", 0, 0, "VS", "vs_4_0", 0, 0, 0, &VS_Buffer, 0, 0); hr = D3DX11CompileFromFile(L"Effects.fx", 0, 0, "PS", "ps_4_0", 0, 0, 0, &PS_Buffer, 0, 0); //Create the Shader Objects hr = d3d11Device->CreateVertexShader(VS_Buffer->GetBufferPointer(), VS_Buffer->GetBufferSize(), NULL, &VS); hr = d3d11Device->CreatePixelShader(PS_Buffer->GetBufferPointer(), PS_Buffer->GetBufferSize(), NULL, &PS); //Set Vertex and Pixel Shaders d3d11DevCon->VSSetShader(VS, 0, 0); d3d11DevCon->PSSetShader(PS, 0, 0); //Create the vertex buffer Vertex v[] = { // Front Face Vertex(-1.0f, -1.0f, -1.0f, 0.0f, 1.0f), Vertex(-1.0f, 1.0f, -1.0f, 0.0f, 0.0f), Vertex( 1.0f, 1.0f, -1.0f, 1.0f, 0.0f), Vertex( 1.0f, -1.0f, -1.0f, 1.0f, 1.0f), // Back Face Vertex(-1.0f, -1.0f, 1.0f, 1.0f, 1.0f), Vertex( 1.0f, -1.0f, 1.0f, 0.0f, 1.0f), Vertex( 1.0f, 1.0f, 1.0f, 0.0f, 0.0f), Vertex(-1.0f, 1.0f, 1.0f, 1.0f, 0.0f), // Top Face Vertex(-1.0f, 1.0f, -1.0f, 0.0f, 1.0f), Vertex(-1.0f, 1.0f, 1.0f, 0.0f, 0.0f), Vertex( 1.0f, 1.0f, 1.0f, 1.0f, 0.0f), Vertex( 1.0f, 1.0f, -1.0f, 1.0f, 1.0f), // Bottom Face Vertex(-1.0f, -1.0f, -1.0f, 1.0f, 1.0f), Vertex( 1.0f, -1.0f, -1.0f, 0.0f, 1.0f), Vertex( 1.0f, -1.0f, 1.0f, 0.0f, 0.0f), Vertex(-1.0f, -1.0f, 1.0f, 1.0f, 0.0f), // Left Face Vertex(-1.0f, -1.0f, 1.0f, 0.0f, 1.0f), Vertex(-1.0f, 1.0f, 1.0f, 0.0f, 0.0f), Vertex(-1.0f, 1.0f, -1.0f, 1.0f, 0.0f), Vertex(-1.0f, -1.0f, -1.0f, 1.0f, 1.0f), // Right Face Vertex( 1.0f, -1.0f, -1.0f, 0.0f, 1.0f), Vertex( 1.0f, 1.0f, -1.0f, 0.0f, 0.0f), Vertex( 1.0f, 1.0f, 1.0f, 1.0f, 0.0f), Vertex( 1.0f, -1.0f, 1.0f, 1.0f, 1.0f), }; DWORD indices[] = { // Front Face 0, 1, 2, 0, 2, 3, // Back Face 4, 5, 6, 4, 6, 7, // Top Face 8, 9, 10, 8, 10, 11, // Bottom Face 12, 13, 14, 12, 14, 15, // Left Face 16, 17, 18, 16, 18, 19, // Right Face 20, 21, 22, 20, 22, 23 }; D3D11_BUFFER_DESC indexBufferDesc; ZeroMemory( &indexBufferDesc, sizeof(indexBufferDesc) ); indexBufferDesc.Usage = D3D11_USAGE_DEFAULT; indexBufferDesc.ByteWidth = sizeof(DWORD) * 12 * 3; indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; indexBufferDesc.CPUAccessFlags = 0; indexBufferDesc.MiscFlags = 0; D3D11_SUBRESOURCE_DATA iinitData; iinitData.pSysMem = indices; d3d11Device->CreateBuffer(&indexBufferDesc, &iinitData, &squareIndexBuffer); D3D11_BUFFER_DESC vertexBufferDesc; ZeroMemory( &vertexBufferDesc, sizeof(vertexBufferDesc) ); vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT; vertexBufferDesc.ByteWidth = sizeof( Vertex ) * 24; vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; vertexBufferDesc.CPUAccessFlags = 0; vertexBufferDesc.MiscFlags = 0; D3D11_SUBRESOURCE_DATA vertexBufferData; ZeroMemory( &vertexBufferData, sizeof(vertexBufferData) ); vertexBufferData.pSysMem = v; hr = d3d11Device->CreateBuffer( &vertexBufferDesc, &vertexBufferData, &squareVertBuffer); //Create the Input Layout hr = d3d11Device->CreateInputLayout( layout, numElements, VS_Buffer->GetBufferPointer(), VS_Buffer->GetBufferSize(), &vertLayout ); //Set the Input Layout d3d11DevCon->IASetInputLayout( vertLayout ); //Set Primitive Topology d3d11DevCon->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST ); //Create the Viewport D3D11_VIEWPORT viewport; ZeroMemory(&viewport, sizeof(D3D11_VIEWPORT)); viewport.TopLeftX = 0; viewport.TopLeftY = 0; viewport.Width = Width; viewport.Height = Height; viewport.MinDepth = 0.0f; viewport.MaxDepth = 1.0f; //Set the Viewport d3d11DevCon->RSSetViewports(1, &viewport); //Create the buffer to send to the cbuffer in effect file D3D11_BUFFER_DESC cbbd; ZeroMemory(&cbbd, sizeof(D3D11_BUFFER_DESC)); cbbd.Usage = D3D11_USAGE_DEFAULT; cbbd.ByteWidth = sizeof(cbPerObject); cbbd.BindFlags = D3D11_BIND_CONSTANT_BUFFER; cbbd.CPUAccessFlags = 0; cbbd.MiscFlags = 0; hr = d3d11Device->CreateBuffer(&cbbd, NULL, &cbPerObjectBuffer); //Camera information camPosition = XMVectorSet( 0.0f, 3.0f, -8.0f, 0.0f ); camTarget = XMVectorSet( 0.0f, 0.0f, 0.0f, 0.0f ); camUp = XMVectorSet( 0.0f, 1.0f, 0.0f, 0.0f ); //Set the View matrix camView = XMMatrixLookAtLH( camPosition, camTarget, camUp ); //Set the Projection matrix camProjection = XMMatrixPerspectiveFovLH( 0.4f*3.14f, (float)Width/Height, 1.0f, 1000.0f); D3D11_BLEND_DESC blendDesc; ZeroMemory( &blendDesc, sizeof(blendDesc) ); D3D11_RENDER_TARGET_BLEND_DESC rtbd; ZeroMemory( &rtbd, sizeof(rtbd) ); rtbd.BlendEnable = true; rtbd.SrcBlend = D3D11_BLEND_SRC_COLOR; ///////////////**************new**************//////////////////// rtbd.DestBlend = D3D11_BLEND_INV_SRC_ALPHA; ///////////////**************new**************//////////////////// rtbd.BlendOp = D3D11_BLEND_OP_ADD; rtbd.SrcBlendAlpha = D3D11_BLEND_ONE; rtbd.DestBlendAlpha = D3D11_BLEND_ZERO; rtbd.BlendOpAlpha = D3D11_BLEND_OP_ADD; rtbd.RenderTargetWriteMask = D3D10_COLOR_WRITE_ENABLE_ALL; blendDesc.AlphaToCoverageEnable = false; blendDesc.RenderTarget[0] = rtbd; hr = D3DX11CreateShaderResourceViewFromFile( d3d11Device, L"braynzar.jpg", NULL, NULL, &CubesTexture, NULL ); // Describe the Sample State D3D11_SAMPLER_DESC sampDesc; ZeroMemory( &sampDesc, sizeof(sampDesc) ); sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; sampDesc.ComparisonFunc = D3D11_COMPARISON_NEVER; sampDesc.MinLOD = 0; sampDesc.MaxLOD = D3D11_FLOAT32_MAX; //Create the Sample State hr = d3d11Device->CreateSamplerState( &sampDesc, &CubesTexSamplerState ); d3d11Device->CreateBlendState(&blendDesc, &Transparency); D3D11_RASTERIZER_DESC cmdesc; ZeroMemory(&cmdesc, sizeof(D3D11_RASTERIZER_DESC)); cmdesc.FillMode = D3D11_FILL_SOLID; cmdesc.CullMode = D3D11_CULL_BACK; cmdesc.FrontCounterClockwise = true; hr = d3d11Device->CreateRasterizerState(&cmdesc, &CCWcullMode); cmdesc.FrontCounterClockwise = false; hr = d3d11Device->CreateRasterizerState(&cmdesc, &CWcullMode); return true; } void UpdateScene() { //Keep the cubes rotating rot += .005f; if(rot > 6.28f) rot = 0.0f; //Reset cube1World cube1World = XMMatrixIdentity(); //Define cube1's world space matrix XMVECTOR rotaxis = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f); Rotation = XMMatrixRotationAxis( rotaxis, rot); Translation = XMMatrixTranslation( 0.0f, 0.0f, 4.0f ); //Set cube1's world space using the transformations cube1World = Translation * Rotation; //Reset cube2World cube2World = XMMatrixIdentity(); //Define cube2's world space matrix Rotation = XMMatrixRotationAxis( rotaxis, -rot); Scale = XMMatrixScaling( 1.3f, 1.3f, 1.3f ); //Set cube2's world space matrix cube2World = Rotation * Scale; } ///////////////**************new**************//////////////////// void RenderText(std::wstring text) { //Release the D3D 11 Device keyedMutex11->ReleaseSync(0); //Use D3D10.1 device keyedMutex10->AcquireSync(0, 5); //Draw D2D content D2DRenderTarget->BeginDraw(); //Clear D2D Background D2DRenderTarget->Clear(D2D1::ColorF(0.0f, 0.0f, 0.0f, 0.0f)); //Create our string std::wostringstream printString; printString << text; printText = printString.str(); //Set the Font Color D2D1_COLOR_F FontColor = D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f); //Set the brush color D2D will use to draw with Brush->SetColor(FontColor); //Create the D2D Render Area D2D1_RECT_F layoutRect = D2D1::RectF(0, 0, Width, Height); //Draw the Text D2DRenderTarget->DrawText( printText.c_str(), wcslen(printText.c_str()), TextFormat, layoutRect, Brush ); D2DRenderTarget->EndDraw(); //Release the D3D10.1 Device keyedMutex10->ReleaseSync(1); //Use the D3D11 Device keyedMutex11->AcquireSync(1, 5); //Use the shader resource representing the direct2d render target //to texture a square which is rendered in screen space so it //overlays on top of our entire scene. We use alpha blending so //that the entire background of the D2D render target is "invisible", //And only the stuff we draw with D2D will be visible (the text) //Set the blend state for D2D render target texture objects d3d11DevCon->OMSetBlendState(Transparency, NULL, 0xffffffff); //Set the d2d Index buffer d3d11DevCon->IASetIndexBuffer( d2dIndexBuffer, DXGI_FORMAT_R32_UINT, 0); //Set the d2d vertex buffer UINT stride = sizeof( Vertex ); UINT offset = 0; d3d11DevCon->IASetVertexBuffers( 0, 1, &d2dVertBuffer, &stride, &offset ); WVP = XMMatrixIdentity(); cbPerObj.WVP = XMMatrixTranspose(WVP); d3d11DevCon->UpdateSubresource( cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0 ); d3d11DevCon->VSSetConstantBuffers( 0, 1, &cbPerObjectBuffer ); d3d11DevCon->PSSetShaderResources( 0, 1, &d2dTexture ); d3d11DevCon->PSSetSamplers( 0, 1, &CubesTexSamplerState ); d3d11DevCon->RSSetState(CWcullMode); //Draw the second cube d3d11DevCon->DrawIndexed( 6, 0, 0 ); } ///////////////**************new**************//////////////////// void DrawScene() { //Clear our render target and depth/stencil view float bgColor[4] = {(0.0f, 0.0f, 0.0f, 0.0f)}; d3d11DevCon->ClearRenderTargetView(renderTargetView, bgColor); d3d11DevCon->ClearDepthStencilView(depthStencilView, D3D11_CLEAR_DEPTH|D3D11_CLEAR_STENCIL, 1.0f, 0); //Set our Render Target d3d11DevCon->OMSetRenderTargets( 1, &renderTargetView, depthStencilView ); //Set the default blend state (no blending) for opaque objects d3d11DevCon->OMSetBlendState(0, 0, 0xffffffff); ///////////////**************new**************//////////////////// //Set the cubes index buffer d3d11DevCon->IASetIndexBuffer( squareIndexBuffer, DXGI_FORMAT_R32_UINT, 0); //Set the cubes vertex buffer UINT stride = sizeof( Vertex ); UINT offset = 0; d3d11DevCon->IASetVertexBuffers( 0, 1, &squareVertBuffer, &stride, &offset ); ///////////////**************new**************//////////////////// //Set the WVP matrix and send it to the constant buffer in effect file WVP = cube1World * camView * camProjection; cbPerObj.WVP = XMMatrixTranspose(WVP); d3d11DevCon->UpdateSubresource( cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0 ); d3d11DevCon->VSSetConstantBuffers( 0, 1, &cbPerObjectBuffer ); d3d11DevCon->PSSetShaderResources( 0, 1, &CubesTexture ); d3d11DevCon->PSSetSamplers( 0, 1, &CubesTexSamplerState ); d3d11DevCon->RSSetState(CWcullMode); d3d11DevCon->DrawIndexed( 36, 0, 0 ); WVP = cube2World * camView * camProjection; cbPerObj.WVP = XMMatrixTranspose(WVP); d3d11DevCon->UpdateSubresource( cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0 ); d3d11DevCon->VSSetConstantBuffers( 0, 1, &cbPerObjectBuffer ); d3d11DevCon->PSSetShaderResources( 0, 1, &CubesTexture ); d3d11DevCon->PSSetSamplers( 0, 1, &CubesTexSamplerState ); d3d11DevCon->RSSetState(CWcullMode); d3d11DevCon->DrawIndexed( 36, 0, 0 ); ///////////////**************new**************//////////////////// RenderText(L"Hello World"); ///////////////**************new**************//////////////////// //Present the backbuffer to the screen SwapChain->Present(0, 0); } int messageloop(){ MSG msg; ZeroMemory(&msg, sizeof(MSG)); while(true) { BOOL PeekMessageL( LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg ); if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) break; TranslateMessage(&msg); DispatchMessage(&msg); } else{ // run game code UpdateScene(); DrawScene(); } } return msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch( msg ) { case WM_KEYDOWN: if( wParam == VK_ESCAPE ){ DestroyWindow(hwnd); } return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, msg, wParam, lParam); } Effects.fx: cbuffer cbPerObject { float4x4 WVP; }; Texture2D ObjTexture; SamplerState ObjSamplerState; struct VS_OUTPUT { float4 Pos : SV_POSITION; float2 TexCoord : TEXCOORD; }; VS_OUTPUT VS(float4 inPos : POSITION, float2 inTexCoord : TEXCOORD) { VS_OUTPUT output; output.Pos = mul(inPos, WVP); output.TexCoord = inTexCoord; return output; } float4 PS(VS_OUTPUT input) : SV_TARGET { return ObjTexture.Sample( ObjSamplerState, input.TexCoord ); }