In this section, we will delve into the DirectX API, which is a collection of APIs designed to handle tasks related to multimedia, especially game programming and video, on Microsoft platforms. Understanding the DirectX API is crucial for developing high-performance applications that leverage the power of modern graphics hardware.

Key Concepts

  1. What is an API?

  • API (Application Programming Interface): A set of functions and protocols that allow different software components to communicate with each other.
  • DirectX API: A collection of APIs from Microsoft that handle multimedia tasks, including Direct3D for graphics, DirectSound for audio, and DirectInput for input devices.

  1. Components of DirectX

  • Direct3D: Handles 3D graphics rendering.
  • Direct2D: Handles 2D graphics rendering.
  • DirectWrite: Manages text rendering.
  • DirectSound: Manages audio playback and recording.
  • DirectInput: Handles input from devices like keyboards, mice, and game controllers.
  • DirectCompute: Allows for general-purpose computing on the GPU.

  1. DirectX Versions

  • DirectX 9: Widely used in older games and applications.
  • DirectX 10: Introduced significant improvements in graphics rendering.
  • DirectX 11: Added support for tessellation, multithreading, and compute shaders.
  • DirectX 12: Focuses on low-level programming, providing more control over hardware and improving performance.

Practical Example: Initializing DirectX

Let's walk through a simple example of initializing DirectX in a Windows application. This example will focus on setting up Direct3D 11.

Step-by-Step Guide

  1. Include Necessary Headers

    #include <d3d11.h>
    #include <d3dcompiler.h>
    #include <DirectXMath.h>
    #include <windows.h>
    
  2. Define Global Variables

    ID3D11Device* g_pd3dDevice = nullptr;
    ID3D11DeviceContext* g_pImmediateContext = nullptr;
    IDXGISwapChain* g_pSwapChain = nullptr;
    ID3D11RenderTargetView* g_pRenderTargetView = nullptr;
    
  3. Initialize Direct3D

    HRESULT InitD3D(HWND hWnd)
    {
        // Create a swap chain description
        DXGI_SWAP_CHAIN_DESC sd;
        ZeroMemory(&sd, sizeof(sd));
        sd.BufferCount = 1;
        sd.BufferDesc.Width = 800;
        sd.BufferDesc.Height = 600;
        sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
        sd.BufferDesc.RefreshRate.Numerator = 60;
        sd.BufferDesc.RefreshRate.Denominator = 1;
        sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
        sd.OutputWindow = hWnd;
        sd.SampleDesc.Count = 1;
        sd.SampleDesc.Quality = 0;
        sd.Windowed = TRUE;
    
        // Create the device, device context, and swap chain
        HRESULT hr = D3D11CreateDeviceAndSwapChain(
            nullptr,
            D3D_DRIVER_TYPE_HARDWARE,
            nullptr,
            0,
            nullptr,
            0,
            D3D11_SDK_VERSION,
            &sd,
            &g_pSwapChain,
            &g_pd3dDevice,
            nullptr,
            &g_pImmediateContext
        );
    
        if (FAILED(hr))
            return hr;
    
        // Create a render target view
        ID3D11Texture2D* pBackBuffer = nullptr;
        g_pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer);
        g_pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &g_pRenderTargetView);
        pBackBuffer->Release();
    
        g_pImmediateContext->OMSetRenderTargets(1, &g_pRenderTargetView, nullptr);
    
        // Setup the viewport
        D3D11_VIEWPORT vp;
        vp.Width = (FLOAT)800;
        vp.Height = (FLOAT)600;
        vp.MinDepth = 0.0f;
        vp.MaxDepth = 1.0f;
        vp.TopLeftX = 0;
        vp.TopLeftY = 0;
        g_pImmediateContext->RSSetViewports(1, &vp);
    
        return S_OK;
    }
    
  4. Clean Up Direct3D

    void CleanupDevice()
    {
        if (g_pImmediateContext) g_pImmediateContext->ClearState();
        if (g_pRenderTargetView) g_pRenderTargetView->Release();
        if (g_pSwapChain) g_pSwapChain->Release();
        if (g_pImmediateContext) g_pImmediateContext->Release();
        if (g_pd3dDevice) g_pd3dDevice->Release();
    }
    

Explanation

  • Headers: We include the necessary DirectX headers for Direct3D and Windows.
  • Global Variables: We define global pointers to the Direct3D device, device context, swap chain, and render target view.
  • InitD3D Function: This function initializes Direct3D by creating a swap chain, device, and device context. It also sets up a render target view and viewport.
  • CleanupDevice Function: This function releases all Direct3D resources to avoid memory leaks.

Practical Exercise

Task

Create a simple Direct3D application that initializes Direct3D and clears the screen to a solid color.

Steps

  1. Set up a Windows application with a main window.
  2. Initialize Direct3D as shown in the example.
  3. In the render loop, clear the screen to a solid color using g_pImmediateContext->ClearRenderTargetView.

Solution

#include <d3d11.h>
#include <d3dcompiler.h>
#include <DirectXMath.h>
#include <windows.h>

ID3D11Device* g_pd3dDevice = nullptr;
ID3D11DeviceContext* g_pImmediateContext = nullptr;
IDXGISwapChain* g_pSwapChain = nullptr;
ID3D11RenderTargetView* g_pRenderTargetView = nullptr;

HRESULT InitD3D(HWND hWnd);
void CleanupDevice();
void Render();

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // Register class
    WNDCLASSEX wcex;
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
    wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszMenuName = nullptr;
    wcex.lpszClassName = L"DirectXExample";
    wcex.hIconSm = LoadIcon(wcex.hInstance, IDI_APPLICATION);
    RegisterClassEx(&wcex);

    // Create window
    HWND hWnd = CreateWindow(L"DirectXExample", L"DirectX Example", WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, 0, 800, 600, nullptr, nullptr, hInstance, nullptr);

    if (!hWnd)
        return FALSE;

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    if (FAILED(InitD3D(hWnd)))
    {
        CleanupDevice();
        return 0;
    }

    // Main message loop
    MSG msg = { 0 };
    while (WM_QUIT != msg.message)
    {
        if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        else
        {
            Render();
        }
    }

    CleanupDevice();
    return (int)msg.wParam;
}

HRESULT InitD3D(HWND hWnd)
{
    DXGI_SWAP_CHAIN_DESC sd;
    ZeroMemory(&sd, sizeof(sd));
    sd.BufferCount = 1;
    sd.BufferDesc.Width = 800;
    sd.BufferDesc.Height = 600;
    sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    sd.BufferDesc.RefreshRate.Numerator = 60;
    sd.BufferDesc.RefreshRate.Denominator = 1;
    sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    sd.OutputWindow = hWnd;
    sd.SampleDesc.Count = 1;
    sd.SampleDesc.Quality = 0;
    sd.Windowed = TRUE;

    HRESULT hr = D3D11CreateDeviceAndSwapChain(
        nullptr,
        D3D_DRIVER_TYPE_HARDWARE,
        nullptr,
        0,
        nullptr,
        0,
        D3D11_SDK_VERSION,
        &sd,
        &g_pSwapChain,
        &g_pd3dDevice,
        nullptr,
        &g_pImmediateContext
    );

    if (FAILED(hr))
        return hr;

    ID3D11Texture2D* pBackBuffer = nullptr;
    g_pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer);
    g_pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &g_pRenderTargetView);
    pBackBuffer->Release();

    g_pImmediateContext->OMSetRenderTargets(1, &g_pRenderTargetView, nullptr);

    D3D11_VIEWPORT vp;
    vp.Width = (FLOAT)800;
    vp.Height = (FLOAT)600;
    vp.MinDepth = 0.0f;
    vp.MaxDepth = 1.0f;
    vp.TopLeftX = 0;
    vp.TopLeftY = 0;
    g_pImmediateContext->RSSetViewports(1, &vp);

    return S_OK;
}

void CleanupDevice()
{
    if (g_pImmediateContext) g_pImmediateContext->ClearState();
    if (g_pRenderTargetView) g_pRenderTargetView->Release();
    if (g_pSwapChain) g_pSwapChain->Release();
    if (g_pImmediateContext) g_pImmediateContext->Release();
    if (g_pd3dDevice) g_pd3dDevice->Release();
}

void Render()
{
    float ClearColor[4] = { 0.0f, 0.125f, 0.3f, 1.0f }; // RGBA
    g_pImmediateContext->ClearRenderTargetView(g_pRenderTargetView, ClearColor);

    g_pSwapChain->Present(0, 0);
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_PAINT:
        PAINTSTRUCT ps;
        HDC hdc;
        hdc = BeginPaint(hWnd, &ps);
        EndPaint(hWnd, &ps);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

Explanation

  • Main Function: Sets up the window and initializes Direct3D.
  • Render Function: Clears the screen to a solid color and presents the back buffer.
  • WndProc Function: Handles window messages.

Summary

In this section, we covered the basics of the DirectX API, including its components and versions. We also walked through a practical example of initializing Direct3D and provided an exercise to reinforce the concepts. Understanding the DirectX API is fundamental for developing high-performance multimedia applications on Windows platforms. In the next module, we will dive deeper into Direct3D and explore its capabilities in more detail.

© Copyright 2024. All rights reserved