Cover

Guide to Contents

Table of Contents

The Story behind the Succinctly Series of Books

About the Author

Introduction

Part 1  Direct2D

Chapter 1: Direct2D (XAML) Template

Chapter 2: Debugging with a WinRT Device

Chapter 3: Beginning a Graph Rendering App

Chapter 4: Graph Backgrounds

Chapter 5: 2-D Data Plots

Chapter 6: Infinite Lines and the Axes

Chapter 7: Displaying FPS (Frames per Second)

Chapter 8: Line Charts

Chapter 9: Navigating between Multiple XAML Pages

Chapter 10: Printing Direct2D

Chapter 11: Margins

Chapter 12: Zooming

Chapter 13: Hit Testing or Picking

Chapter 14: Direct2D Geometry

Part 2  Direct3D

Chapter 15: Rendering Pipeline

Chapter 16: Starting a Direct3D Project

Chapter 17: Rendering a Triangle with Direct3D

Chapter 18: Rendering a Height Map

Chapter 19: Projection Options

Conclusion

Appendix A: Microsoft Limited Public License

MICROSOFT LIMITED PUBLIC LICENSE version 1.1

Appendix B: DirectXPage.xaml Class Listing

Appendix C: CDocSource Class Code Listing

Appendix D: Code Listing for SimpleTextRenderer Printing

Detailed Table of Contents

Introduction

Introduction

This book is an introduction to some of the capabilities of Direct2D and Direct3D. Direct2D and Direct3D are the graphics rendering components of DirectX. It is about leveraging the graphics card and DirectX to efficiently represent data. It is aimed at programmers already familiar with C++ (both managed and unmanaged) and Visual Studio 2012 Express. We will be using the version of Visual Studio designed for Windows 8 application development, not the desktop version. The desktop version is designed for building standard Windows Forms applications, and the version for Windows 8 is designed for Windows Store applications. This book presents methods for rendering vector graphics and visualizing different types of data on Windows 8 and Windows RT platforms using Direct2D and Direct3D. It is not an in-depth discussion of these topics; for further information, consult the appropriate MSDN library pages from Microsoft along with the specification of the graphics hardware for which you are programming.

This book provides a general introduction to Direct2D and Direct3D. It is written from the perspective of rendering data as nodes and lines, but the information presented is useful for any applications that require efficient rendering using DirectX. In the initial chapters of this book we will develop a small but scalable charting system that can be adapted to suit other projects or incorporated into an existing project. We will examine some common requirements of charting applications, such as detecting if the pointer is near a node, as well as printing Direct2D.

In the interest of keeping things as general as possible, I have generated random data in the examples. In a real situation this data would be loaded from some data source. I will also build on the standard project templates provided by Visual Studio 2012, rather than concentrate on the boilerplate code. The verbose DirectX boilerplate code is a barrier for any programmers hoping to become familiar with the API. Thankfully, the templates supplied with Visual Studio 2012 write all of the boilerplate code for us. We will largely take it for granted, and examine options in the boilerplate code as they arise.

The code in this book is designed for desktop PCs running Windows 8 and tablet PCs running Windows RT. It has been formatted to suit the page of this document. This means it is very difficult to read, and should be reformatted if it is copied and pasted for testing purposes.

Part 1 Direct2D

Part 1  Direct2D

Direct2D is a graphics API (Application Programming Interface) designed to render 2-D vector and raster graphics. It is built on top of the Direct3D API, which in turn is built on the DXGI (DirectX Graphics Infrastructure). It can be used in conjunction with Direct3D to render any 2-D portions of a scene. It is high performance, leveraging the GPU for efficient, complex 2-D graphics.

Note: Throughout this book I will refer to GPU many times (short for Graphics Processing Unit). The term GPU usually refers to a dedicated graphics card, however, I will use the term more generally to refer to the hardware which performs the majority of graphics processing in a computer. This includes a dedicated graphics card, an onboard graphics card, or the execution units in the NVidia Tegra chips in the WinRT devices.

The API consists of a number of interfaces (COM objects), which are used to communicate with the graphics hardware. It can render vector primitives, like lines and ellipses, and can also fill shapes with solid colors or gradients, as well as display raster images. Raster graphics are composed of pixels, one for each point on a screen (or image). The pixels each have values which determine their colors, and collectively they are arranged in a large grid.

Direct2D is important for visualizing data because many chart types (line charts, scatter plots, etc.) are fundamentally 2-D in design. The most important difference between using Direct2D and using Direct3D to render 2-D graphics is simplicity. Direct3D is orders of magnitude faster than Direct2D but it is more complicated to program. In addition to this, the Direct2D project template is a perfect combination of standard Windows 8 XAML and Direct2D. This allows programmers to use standard Windows 8 controls and XAML pages to deal with user input, while Direct2D handles all the graphics processing. This combination of DirectX and XAML is a feature only available in Windows 8 applications.

Figure 1: Relationship between Major DirectX Components

The graphics driver is the lowest level depicted; it controls the hardware directly. Above this is the DirectX Graphics Infrastructure (DXGI), then Direct3D and finally Direct2D. The software rasterizer is used in place of graphics hardware, it uses the CPU to render graphics when a dedicated GPU is not available.

Chapter 1: Direct2D (XAML) Template

Chapter 1: Direct2D (XAML) Template

We will begin by creating a standard Direct2D (XAML) template project and becoming familiar with its structure. Open Visual Studio 2012 and on the File Menu, click New Project.

Figure 2: Creating a new Direct2D App (XAML)

Click Visual C++ on the left panel, and then select Direct2D App (XAML) from the project templates in the center panel. Type a name for your project in the Name box, andthen click OK.

Visual Studio will create many files for the new project which contain the boilerplate code and some other useful helper methods. The Solution Explorer should look like Figure 3.

To run the application in debug mode press F5, or on the File menu click Debug > Start Debugging. After Visual Studio builds and links your project files, it will execute the application.

Figure 3: Direct2D App (XAML) Solution Explorer

Figure 4: Output of Direct2D App (XAML) Template

Assets Folder

This folder contains several PNG images for the new application:

  • Logo.png: This image appears as the tile on the Windows 8 Start page. It is similar to the desktop icon from previous versions of Windows.
  • SmallLogo.png: This is the icon image used when a smaller icon should be displayed, such as when the user is searching 'All Apps' in Windows 8.
  • SplashScreen.png: The splash screen appears briefly when your application is executed.
  • StoreLogo.png: This is the logo for your app as it appears in the Windows Store.

Common

This folder contains a single XAML file that describes common settings between the XAML files.

External Dependencies:

This folder contains a very large list of external files that your project may depend on. Some are generated per project, and others are the standard Windows C++ header files. You should not change the files in this list, especially the standard Windows headers.

App

The App.xaml, App.cpp, and App.h files define your application. The XAML file contains some global settings across your entire app. The CPP and H files define a class with the starting point for executing the program. This class owns a member variable called m_directXPage which is the main Direct2D rendering class. It also controls some important system-level operations, like saving and restoring the state of the application when the program is suspended.

BasicTimer.h

The basic timer header defines a class that can be used for any time-based tasks such as physics or animation.

xxx_TemporaryKey.pfx

This is the ClickOnce digital certificate for your application. It is used to help ensure that the application is not malicious software. If the application is not signed, Windows will warn users that the application "comes from an unknown publisher," and it will ask them if they are sure they wish to run the program.

DirectXBase

The DirectXBase class is defined in two files: DirectXBase.h and DirectXBase.cpp. This class contains most of the boilerplate code to get Direct2D up and running. It contains code to initialize the device, the factories, device context, and many other things. It can be used for both 2-D and 3-D graphics. It has many helper functions to enable us to quickly begin DirectX programming without typing the extremely verbose boilerplate code. The reader is encouraged to investigate this file thoroughly, as it shows exactly how DirectX should be initialized.

DirectXHelper

This file consists of a single function, DX::ThrowIfFailed. This is a helper function that converts an HRESULT to a managed C++ exception. DirectX function calls return an HRESULT. Many of the codes we will examine surround the DirectX function calls with a call to this method, such that the programmer has an opportunity to examine any errors that are thrown by DirectX. If you set a break point on this line, Visual Studio will break when an exception is thrown, and allow you to examine what went wrong. The errors will give you an error number and you can research the meaning of this, or look it up using the error search application that comes with the DirectX SDK.

DirectXPage

This is the main XAML page of your application. The Direct2D (XAML) template application contains a simple page with two sentences written on a XAML form. The top sentence is written using XAML and the lower one is written by DirectX. This is the class that renders the top line of code.

Package.appxmanifest

This is the main manifest of your application. It contains all the information about your app, including who the publisher is, and what capabilities the app requires (Internet access, access to the webcam, etc.).

PCH

The precompiled header file (pch.h) contains headers that are compiled to an intermediate format to save time when recompiling the entire project. Most of the classes you add to your project will include this file in order to work correctly.

SimpleTextRenderer

This is the core class of this DirectX application. This class renders the lower sentence on the screen. Because the SimpleTextRenderer class is the main class controlling what DirectX displays on the screen, we will examine it in detail.

SimpleTextRenderer Class

This class uses Direct2D to render a line of text to the screen. In this section, it is not the class itself we are examining, but rather the way that it operates. The Graph Renderer class we will build in future chapters will be heavily based on this class. Open the SimpleTextRenderer.h file.

The class derives from the DirectXBase class. It contains a default constructor and several methods, which are called during resource allocation (CreateDeviceIndependentResources,  CreateDeviceResources and CreateWindowSizeDependentResources).

Note: Resources is a general term referring to many different types of objects and structures that are stored in memory (either system memory or in the GPU's dedicated memory) and used by DirectX. Resources must be created and initialized prior to their use. Most of the resources we will examine are created shortly after the main DirectX objects. These resources are destroyed when the application closes. Resources can be created and destroyed at any time after the main DirectX objects are initialized, since the resource creation methods belong to these objects.

The Render method is where DirectX does all of its rendering. This class also defines an update method which can be used to perform calculations to determine where objects should be moved to in the scene. The UpdateTextPosition, BackgroundNextColor, and BackgroundPreviousColor functions are specific to this template, and not required when you develop your own. They allow the user to manipulate the position of the DirectX drawn text, as well as cycle through some predefined background colors.

The SaveInternalState and LoadInternalState methods are used to save and restore the state of the application; for example, when a WinRT tablet goes to sleep, and then is woken.

These methods are followed by several member variables that are used to maintain and manipulate the position of the text. Apart from the m_renderNeeded variable, most of these variables are application specific and most likely not required for your application. The m_renderNeeded variable is used by the application to determine if the Render method should be called. If nothing has changed in the scene, there is no point in rendering it again. The following diagram depicts the relationships between the most important classes of this application. Lines ending in diamond shapes indicate ownership (the AppXAML class owns a member of the type DirectXPage), and the lines ending with a triangle indicate inheritance (the SimpleTextRenderer inherits from the DirectXBase class).

Figure 5: Class Relationships of Direct2D App (XAML) Template

Real-time graphics applications often render frames at some predefined interval described with the metric of the number frames displayed per second (FPS). A frame is a single still image of a game or movie. In order to create the illusion of smooth animation, slightly different frames are displayed to the viewer in succession. DirectX applications often render frames at a fixed refresh rate, such as 60fps or even 100fps. It is not likely that the frames of a charting application need to be re-rendered every 60th or 100th of a second. They usually stay exactly the same for long periods of time. The user may pan or zoom into the chart which would require a re-rendering of the scene, but this action is not as time critical as updating the frames of a real-time game.

Note: The member variables for this and other classes in this template have an “m_” prefix. This signifies that they are member variables as opposed to local variables to a function. It is not necessary but it is a good idea to name all member variables with this prefix.

Next, open the SimpleTextRenderer.cpp file in the Solution Explorer. At the top of the file you will see an #include directive for the Precompiled Header (pch.h). Below this is the #include for the SimpleTextRenderer.h, and the list of namespaces the class uses. Under the using directives you will see the predefined order of the background colors that the user can cycle while running the application.

static const ColorF BackgroundColors[] = { …  }

The user can cycle through the colors and change the background of the application by right-clicking the mouse in the screen, or swiping the pointer if you are using a touch screen device, and selecting Next or Previous. This is an application-specific array, and it is unlikely that other applications will use it. Below this we see the default constructor for the class.

SimpleTextRenderer::SimpleTextRenderer():m_renderNeeded(true),     m_backgroundColorIndex(0),m_textPosition(0.0f, 0.0f) { }

The default constructor initializes several variables; it sets the backcolor to CornflowerBlue by selecting index 0 (this is a reference to the BackgroundColors array defined in the previous code sample). It also initializes the text position and sets the m_renderNeeded Boolean to true, such that the first frame will be drawn to the screen. Resources are not created or allocated at this point; the DirectX factories and context do not yet exist either.

Following this are three resource allocation methods. The first of which is the CreateDeviceIndependentResources method.

void SimpleTextRenderer::CreateDeviceIndependentResources() {

     DirectXBase::CreateDeviceIndependentResources();

     DX::ThrowIfFailed(

          m_dwriteFactory->CreateTextFormat(

                L"Segoe UI", nullptr, DWRITE_FONT_WEIGHT_NORMAL,

                DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL,

                42.0f, L"en-US", &m_textFormat));

     DX::ThrowIfFailed(

          m_textFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING)

          );

}

The CreateDeviceIndependentResources method is used to create and initialize any Direct2D objects that are device independent. This method begins by calling the base class's method of the same name. The base class method creates the DirectX factories, such as the m_dwriteFactory used on the next line, which can be used by the application to create more DirectX objects.

Note: Resources in DirectX are all from one of two broad categories: device resources or device independent resources. The device is the graphics card, and the device resources reside on the graphics card itself. Device independent resources reside in system RAM, and tend to render slower because they require CPU cycles to transfer to the video card.

void SimpleTextRenderer::CreateDeviceResources() {

     DirectXBase::CreateDeviceResources();

     DX::ThrowIfFailed(

          m_d2dContext->CreateSolidColorBrush(

                ColorF(ColorF::Black), &m_blackBrush));

     Platform::String^ text = "Hello, DirectX!";

     DX::ThrowIfFailed(m_dwriteFactory->CreateTextLayout(

                text->Data(), text->Length(),

                m_textFormat.Get(),

                700, // maxWidth.

                1000, // maxHeight.

                &m_textLayout));

     DX::ThrowIfFailed(m_textLayout->GetMetrics(&m_textMetrics));

}

The CreateDeviceResources method creates and initializes the device dependent resources. The method calls the base class method with the same name, which creates the instance of the Direct3D device and the context used by the application (m_d3dcontext and m_d3dDevice).

Note: Device and context are two important terms in DirectX. The device can be thought of as the graphics card itself; this class is used to initialize the hardware, query its capabilities, and create resources such as textures and shaders. A context is a particular use of the device; it renders things to the screen using the resources on the device. There is normally one device, but there may be more than one context. For instance, the printing sample uses three contexts: one for rendering, another for the print preview, and a third for the printing itself. Figure 6 shows some of the tasks each of these classes is responsible for.

Figure 6: Device versus Context

Brushes are device resources; this method creates a black brush for painting the text. The actual string to be written to the screen is created on the device as a TextLayout object using the CreateTextLayout method. After this, the measurements and proportions of the string are saved to m_textMetrics using the GetMetrics method.

Note: The CreateTextLayout method creates the IDWriteTextLayout device resource. This resource contains information about the string to be printed, the bounding box within which it is printed and its location. The CreateTextFormat method (in the CreateDeviceIndependentResources method) creates an IDWriteTextFormat object, which is used to specify the font, size, and attributes of the text to render.

 

void SimpleTextRenderer::CreateWindowSizeDependentResources() {

     DirectXBase::CreateWindowSizeDependentResources();

     // Add code to create window size dependent objects here.

}

 

void SimpleTextRenderer::Update(float timeTotal, float timeDelta) {

     (void) timeTotal; // Unused parameter.

     (void) timeDelta; // Unused parameter.

     // Add code to update time dependent objects here.

}

The previous two methods are empty in the template. The CreateWindowSizeDependentResources method is used to create any objects (device or device independent) whose settings are dependent on the size or orientation of the screen. The Update method is also empty; it controls the physics or other logic of the application, usually things that are time dependent. The following code is an example of the template's Render method.

void SimpleTextRenderer::Render() {

     m_d2dContext->BeginDraw();

     m_d2dContext->Clear(ColorF(BackgroundColors[m_backgroundColorIndex]));

     // Position the rendered text.

     Matrix3x2F translation = Matrix3x2F::Translation(

          m_windowBounds.Width / 2.0f –

          m_textMetrics.widthIncludingTrailingWhitespace / 2.0f +

          m_textPosition.X,

          m_windowBounds.Height / 2.0f –

          m_textMetrics.height / 2.0f + m_textPosition.Y

          );

 

     // Note that the m_orientationTransform2D matrix is post-multiplied here

     // in order to correctly orient the text to match the display orientation.

     // This post-multiplication step is required for any draw calls that are

     // made to the swap chain's target bitmap. For draw calls to other targets,

     // this transform should not be applied.

     m_d2dContext->SetTransform(translation * m_orientationTransform2D);

     m_d2dContext->DrawTextLayout(Point2F(0.0f, 0.0f),

          m_textLayout.Get(),  m_blackBrush.Get(),

          D2D1_DRAW_TEXT_OPTIONS_NO_SNAP);

 

     // Ignore D2DERR_RECREATE_TARGET. This error indicates that the device

     // is lost. It will be handled during the next call to Present.

     HRESULT hr = m_d2dContext->EndDraw();

     if (hr != D2DERR_RECREATE_TARGET) {

          DX::ThrowIfFailed(hr);

     }

     m_renderNeeded = false;

}

It is here in the Render method that actual drawing of the scene takes place. Most of the drawing of the scene is performed by the m_d2dContext object. The Render method begins by stating m_d2dcontext->BeginDraw; this line is coupled to the call to m_d2dContext->EndDraw method call near the bottom. You should place all of your Direct2D drawing between these two function calls. BeginDraw is used to specify the start of some code which builds a batch of rendering commands for a render target. EndDraw specifies that the batch of commands is finished and they can be rendered.

The next line calls the Clear method, passing the color the user currently has selected. This results in clearing the screen to a solid color, one that the BackgroundColors array defined previously, which the user can cycle through.

Tip: It is a good idea to clear the screen to some color other than black in a render method, even if your subsequent drawing will completely overwrite the cleared screen. If you do not do this and there is a problem with the program, you might be left staring at a black screen (or random flashing colors or pixels) with no way of telling whether the render method is being called at all.

Following the call to Clear, a matrix is set up. Transforms such as scaling, rotation, and translation (or panning, which is what we are doing here) are all controlled by matrices. This particular matrix is a translation matrix; it moves the text such that the user can drag it around the screen. The calculation in the definition of this matrix places the text in the middle of the screen with some offset when the user drags it around. It uses the TextMetrics object and the WindowBounds object to find where the text should go.

Once defined, the translation matrix must be applied to the context. This occurs on the next line with the call to SetTransform. After the appropriate transformations have been applied, the text itself can be rendered. This happens on the next line with the call to DrawTextLayout. Then the drawing is ended with the call to EndDraw, and the image is presented to the user.

Tip: The actual screen refresh of the rendered scene occurs in the DirectXPage.xaml.cpp file when the m_renderer object calls Present() in its OnRendering event handler method. It is very important to note that the DirectXPage class presents the scene. When you add more sophisticated rendering classes that call Present() themselves, it is important that you remove this Present() call from the DirectXPage class. Otherwise you might Present() twice which will result in first flipping the actual scene to the screen but immediately overwriting it with some other image.

The remaining methods are event handlers and other things which are specific to this. I recommend that programmers new to DirectX with Visual Studio 2012 spend some time altering the workings of this template before continuing on to the next section. A good familiarity with this template is essential to understanding the remaining chapters of the Direct2D portion of this book.

Tip: Direct2D is designed to use multiple cores of the CPU automatically when rendering geometry. If you use the D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS option when creating the device context in the DirectXBase.cpp file, automatic multithreading may provide a good speed boost to your code at the cost of utilizing more of the system's cores.

VSync, Swap Chain, and Buffering

Computer monitors update their display at a

Impressum

Verlag: BookRix GmbH & Co. KG

Tag der Veröffentlichung: 06.02.2016
ISBN: 978-3-7396-3598-9

Alle Rechte vorbehalten

Nächste Seite
Seite 1 /