Page MenuHomePhabricator

Evas Direct3D 11 Engine
Updated 167 Days AgoPublic

Introduction

This tutorial aims at presenting the usage of Direct3D 11 in 2D, without any help of Direct2D, SpriteBatch or any other library. Each part of this tutorial will add some Direct3D 11 features, to obtain finally the display of a rectangle. Then, writing a software Direct3D engine for Evas will be easy.

All the code in this tutorial can compile with Visual Studio, MSYS2 + mingw-w64, and even with cross-compilation on Unix provided the mingw-w64 toolchain is installed.

The code will use the Direct3D and DXGI C API when it is possible, and will target Windows 7 or Windows 10 (or above).

At the top of each example, a compilation of the code is added.

References:

Creating a window

There are numerous tutorials and examples on Internet about creating a window on Windows. So the code is presented below, with some remarks at the end. Here we also use a header file, win.h to decrease all the source files below.

win.h
#ifndef WIN_H
#define WIN_H

/* comment for no debug informations */
#define _DEBUG

typedef struct Window Window;
typedef struct D3d D3d;

struct Window
{
    HINSTANCE instance;
    RECT rect;
    HWND win;
    D3d *d3d;
    int rotation; /* rotation (clockwise) in degrees */
    unsigned int fullscreen: 1;
};

D3d *d3d_init(Window *win, int vsync);

void d3d_shutdown(D3d *d3d);

void d3d_resize(D3d *d3d, UINT width, UINT height);

void d3d_render(D3d *d3d);

#endif
win_1.c
/*
 * Windows 10:

 gcc -g -O2 -Wall -Wextra -o d3d d3d_2.c win_2.c -ld3d11 -ld3dcompiler -ldxgi -luuid -D_WIN32_WINNT=0x0A00

 * Windows 7:

 gcc -g -O2 -Wall -Wextra -o d3d d3d_2.c win_2.c -ld3d11 -ld3dcompiler -ldxgi -luuid -D_WIN32_WINNT=0x0601

 */

#if defined _WIN32_WINNT && _WIN32_WINNT >= 0x0A00
# define HAVE_WIN10
#endif

#include <stdlib.h>
#include <stdio.h>

#ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
#endif

#include <windows.h>

#include "win.h"

void window_fullscreen_set(Window *win, unsigned int fullscreen);

void window_rotation_set(Window *win, int rotation);

/************************* Window *************************/

LRESULT CALLBACK
_window_procedure(HWND   window,
                  UINT   message,
                  WPARAM window_param,
                  LPARAM data_param)
{
  switch (message)
    {
    case WM_CLOSE:
        PostQuitMessage(0);
        return 0;
    case WM_KEYUP:
        if (window_param == 'Q')
        {
            PostQuitMessage(0);
        }
        if (window_param == 'F')
        {
            Window *win;

#ifdef _DEBUG
            printf("fullscreen\n");
            fflush(stdout);
#endif
            win = (Window *)GetWindowLongPtr(window, GWLP_USERDATA);
            window_fullscreen_set(win, !win->fullscreen);
        }
        if (window_param == 'R')
        {
            Window *win;

#ifdef _DEBUG
            printf("rotation\n");
            fflush(stdout);
#endif
            win = (Window *)GetWindowLongPtr(window, GWLP_USERDATA);
            window_rotation_set(win, (win->rotation + 90) % 360);
        }
        return 0;
    case WM_ERASEBKGND:
        /* no need to erase back */
        return 1;
    /* GDI notifications */
    case WM_CREATE:
#ifdef _DEBUG
        printf(" * WM_CREATE\n");
        fflush(stdout);
#endif
        return 0;
    case WM_SIZE:
    {
#ifdef _DEBUG
        printf(" * WM_SIZE\n");
        fflush(stdout);
#endif

        /*
         * here, we will change the size of the needed d3d objects:
         * - the viewport
         * - the swap chain
         * - the render target view
         */

        return 0;
    }
    case WM_PAINT:
    {
#ifdef _DEBUG
        printf(" * WM_PAINT\n");
        fflush(stdout);
#endif

        if (GetUpdateRect(window, NULL, FALSE))
        {
            PAINTSTRUCT ps;
            BeginPaint(window, &ps);

            /* here, we will render a frame */

            EndPaint(window, &ps);
        }

        return 0;
      }
    default:
      return DefWindowProc(window, message, window_param, data_param);
    }
}

Window *window_new(int x, int y, int w, int h)
{
    WNDCLASS wc;
    RECT r;
    Window *win;

    win = (Window *)calloc(1, sizeof(Window));
    if (!win)
        return NULL;

    win->instance = GetModuleHandle(NULL);
    if (!win->instance)
        goto free_win;

    memset (&wc, 0, sizeof (WNDCLASS));
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = _window_procedure;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = win->instance;
    wc.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor (NULL, IDC_ARROW);
    wc.hbrBackground = NULL;
    wc.lpszMenuName =  NULL;
    wc.lpszClassName = "D3D";

    if(!RegisterClass(&wc))
        goto free_library;

    r.left = 0;
    r.top = 0;
    r.right = w;
    r.bottom = h;
    if (!AdjustWindowRectEx(&r,
                            WS_OVERLAPPEDWINDOW | WS_SIZEBOX,
                            FALSE,
                            0U))
        goto unregister_class;

    win->win = CreateWindowEx(0U,
                              "D3D", "Test",
                              WS_OVERLAPPEDWINDOW | WS_SIZEBOX,
                              x, y,
                              r.right - r.left,
                              r.bottom - r.top,
                              NULL,
                              NULL, win->instance, NULL);
    if (!win->win)
        goto unregister_class;

    return win;

  unregister_class:
    UnregisterClass("D2D", win->instance);
  free_library:
    FreeLibrary(win->instance);
  free_win:
    free(win);

    return NULL;
}

void window_del(Window *win)
{
    if (!win)
        return;

    DestroyWindow(win->win);
    UnregisterClass("D2D", win->instance);
    FreeLibrary(win->instance);
    free(win);
}

void window_show(Window *win)
{
    ShowWindow(win->win, SW_SHOWNORMAL);
}

void window_fullscreen_set(Window *win, unsigned int on)
{
    HWND prev;
    DWORD style;
    DWORD exstyle;
    UINT flags;
    int x;
    int y;
    int w;
    int h;

    on =  !!on;
    if ((win->fullscreen && on) ||
        (!win->fullscreen && !on))
        return;

    if (on)
    {
        MONITORINFO mi;
        HMONITOR monitor;

        if (!GetWindowRect(win->win, &win->rect))
        {
            printf("GetWindowRect() failed\n");
            return;
        }

        monitor = MonitorFromWindow(win->win, MONITOR_DEFAULTTONEAREST);
        mi.cbSize = sizeof(MONITORINFO);
        if (!GetMonitorInfo(monitor, &mi))
            return;

        style = WS_VISIBLE | WS_POPUP;
        exstyle = WS_EX_TOPMOST;
        prev = HWND_TOPMOST;
        x = 0;
        y = 0;
        w = mi.rcMonitor.right - mi.rcMonitor.left;
        h = mi.rcMonitor.bottom - mi.rcMonitor.top;
        flags = SWP_NOCOPYBITS | SWP_SHOWWINDOW;
    }
    else
    {

        style = WS_OVERLAPPEDWINDOW | WS_SIZEBOX;
        exstyle = 0U;
        prev = HWND_NOTOPMOST;
        x = win->rect.left;
        y = win->rect.top;
        w = win->rect.right - win->rect.left;
        h = win->rect.bottom - win->rect.top;
        flags = SWP_NOCOPYBITS | SWP_SHOWWINDOW;
    }

    SetLastError(0);
    if (!SetWindowLongPtr(win->win, GWL_STYLE, style) &&
        (GetLastError() != 0))
    {
        printf("SetWindowLongPtr() failed\n");
        return;
    }
    SetLastError(0);
    if (!SetWindowLongPtr(win->win, GWL_EXSTYLE, exstyle) &&
        (GetLastError() != 0))
    {
        printf("SetWindowLongPtr() failed\n");
        return;
    }
    if (!SetWindowPos(win->win, prev, x, y, w, h, flags))
    {
        printf("SetWindowPos() failed\n");
        return;
    }

    win->fullscreen = on;
}

void window_rotation_set(Window *win, int rotation)
{
    int rdiff;

    if (win->rotation == rotation)
        return;

    rdiff = win->rotation - rotation;
    if (rdiff < 0) rdiff = -rdiff;

    if (rdiff != 180)
    {
        RECT r;

        if (!GetWindowRect(win->win, &r))
            return;

        if (!MoveWindow(win->win,
                        r.left, r.top,
                        r.bottom - r.top, r.right - r.left,
                        TRUE))
        {
            printf("MoveWindow() failed\n");
            return;
        }
    }

    win->rotation = rotation;
}

int main()
{
    Window *win;

    /* remove scaling on HiDPI */
#if _WIN32_WINNT >= 0x0A00
    SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_SYSTEM_AWARE);
#endif

    win = window_new(100, 100, 800, 480);
    if (!win)
        return 1;

    SetWindowLongPtr(win->win, GWLP_USERDATA, (LONG_PTR)win);

    window_show(win);

    /* mesage loop */
    while(1)
    {
        MSG msg;
        BOOL ret;

        ret = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
        if (ret)
        {
            do
            {
                if (msg.message == WM_QUIT)
                  goto beach;
                TranslateMessage(&msg);
                DispatchMessageW(&msg);
            } while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE));
        }
    }

  beach:
    window_del(win);

    return 0;
}

When the program is launched, you should obtain a window with a white background, like this:

Some actions are implemented:

  • Press the key 'q' to quit the program.
  • Press the key 'f' to switch fullscreen / windowed mode
  • Press the key 'r' to rotate clock-wise the window with steps of 90 °. This will be useful when something will be drawn in the window. For now, the only change will be the orientation of the window.

Some remarks:

  1. The macro HAVE_WIN10 will be defined when the program is compiled for Windows 10. Otherwise we suppose Windows 7.
  2. The macro _WIN32_WINNT is set to 0x0A00 to specify the version of Windows. Comment it to specify Windows 7.
  3. The macro _DEBUG will be used for Direct3D only.
  4. The High DPI scaling is removed on Windows 10 (line 176).
  5. The WM_SIZE message will be used to adapt the size of some of the Direct3D objects, namely:
    • the viewport,
    • the swap chain,
    • the render target view.
  6. The WM_PAINT will be used to render the scene.
  7. There are some optimizations to avoid the redraw of the window background when it is resized:
    • the WM_ERASEBKGND massage is managed,
    • the brush of the window class is set to NULL.

DirectX 11 overview

Up to Direct3D 9, the interface between the hardware and the applications was the Direct3D DLL. Since Direct3D 10, this DLL has been splitted into two libraries:

  • The DirectX Graphics Infrastructure (DXGI): it manages low-level tasks like enumeration of hardware devices, presenting rendered frames or managing fullscreen.
  • The Direct3D DLL: it manages the pipeline to create 3D (or 2D) graphics.

Here is a diagram that shows the link between the DirectX components (diagram taken from MSDN 'DXGI overview' page):

In this tutorial, DXGI will be used for:

  • Create a swap chain
  • Handling window resizing and fullscreen
  • Debugging

What will be needed to use Direct3D is at least

  • A D3D11 device and a device context (the latter also called immediate context)
  • A DXGI swap chain
  • A D3D11 render target view, which is the output image. Several render target views can be created, to have effect like the portal game.

Normally, the devices will not be touched anymore, except if the driver has some issues or the graphic card is removed.

Direct3D is using a pipeline to render the final output. Here is the different components (diagram taken from MSDN 'Graphics pipeline' page):

  1. The input Assembler (IA) stage: it reads the geometric data, vertices and indices, to create lines, rectangles, cubes, etc...
  2. The Vertex Shader (VS) stage: a shader program that can transform (scale, ..) the primitives built in the IA stage
  3. The Hull Shader (HS) stage (not used in that tutorial)
  4. The Tesselator Shader (TS) stage (not used in that tutorial)
  5. The Domain Shader (DS) stage (not used in that tutorial)
  6. The Geometry Shader (GS) stage (not used in that tutorial)
  7. The Stream Output (SO) stage (not used in that tutorial)
  8. The Rasterizer (RS) stage: turn the vector informations into pixels.
  9. The Pixel Shader (PS) stage: a shader program that modifies each pixel that will be seen
  10. The Output Merger (OMS) stage: determines which pixels will be written to the render target

Fill window background with Direct3D 11

It is time to add some Direct3D code:

  • a structure which will contain the necessary Direct3D variables,
  • the d3d_init() function which will create resources,
  • the d3d_shutdown() function which will release resources,
  • the d3d_resize() function which will handle window resize
  • the d3d_render() function which will set the resources of the needed pipeline stages, fill the background with a color and display the result.

Note that filling the background will be useless for an EFL-based application as the background will be anyway filled with a rectangle or image. It is used only to show something in this tutorial.

Direct3D structure

Here is the structure that holds the Direct3D objects, depending we are on Windows 7 or not:

struct D3d
struct D3d
{
    /* DXGI */
#ifdef HAVE_WIN10
    IDXGIFactory2 *dxgi_factory;
    IDXGISwapChain1 *dxgi_swapchain;
#else
    IDXGIFactory *dxgi_factory;
    IDXGISwapChain *dxgi_swapchain;
#endif
    /* D3D11 */
    ID3D11Device *d3d_device;
    ID3D11DeviceContext *d3d_device_ctx;
    ID3D11RenderTargetView *d3d_render_target_view;
    ID3D11InputLayout *d3d_input_layout;
    ID3D11VertexShader *d3d_vertex_shader;
    ID3D11RasterizerState *d3d_rasterizer_state;
    ID3D11PixelShader *d3d_pixel_shader;
    D3D11_VIEWPORT viewport;
    unsigned int vsync : 1;
};
  • The DXGI factory is used to get the vertical synchronization frequency (function d3d_refresh_rate_get()) and to create the swap chain.
  • The D3D11 device is used to create the render target view.
  • The D3D11 device context is used to bind the Direct3D objects to the pipeline.
  • The swap chain and render target view has been briefly described above.
  • The viewport is the rectangle (in pixels) in which the 3D scene is projected.

The d3d_init() function

The steps are:

  1. Create the DXGI factory, depends if we are on Windows 10 or not.
  2. Create the D3D11 device and device context.
  3. Get the vertical synchronization frequency by calling the function d3d_refresh_rate_get(). The frequency is a rational number, meaning that it has a numerator and a denominator. If the user does not want to honor vsync, the frequency has numerator equal to 0 and denominator equal to 1.
  4. Create the DXGI swap chain. It requires some settings, mainly:
    • the Win32 window,
    • the size of the Win32 window,
    • the vertical synchronization frequency,
    • the output format, here, BGRA,
    • the swap model, here the front and back buffers are flipped, meaning that the pointers of the front and back buffer pointers are swapped.
    • if the scene if fullscreen or not.
  5. Create the rasterizer state.

The d3d_shutdown() function

It just release the DXGI and D3D resources. Note that the swapchain must be set to windowed before releasing it.

The d3d_resize() function

This function is called when the window is resized. The steps are:

  1. set the size of the viewport,
  2. release the render target view if any,
  3. unbind the render target from the D3D pipeline,
  4. resize the buffers of the DXGI swap chain,
  5. get the back buffer from the swap chain,
  6. create the render target view with this buffer, the rendering is done on a 2D texture,
  7. release the back buffer.

The d3d_render() function

This function set the D3D pipeline:

  1. set rasterizer state in the Rasterizer Stage (RS),
  2. set viewport in the Rasterizer Stage (RS),
  3. set the render target view in the Output Merger (OM),
  4. flip back and front buffer to display the scene created in the back buffer.

The full code

for now, we do not support the fullscreen and rotation in Direct3D, it will be done later.

First, the new win32 code, followed by the diff between win_1.c and win_2.c

win_2.c
#include <stdlib.h>
#include <stdio.h>

#ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
#endif

#include <windows.h>

#include "win.h"

void window_fullscreen_set(Window *win, unsigned int fullscreen);

void window_rotation_set(Window *win, int rotation);

/************************* Window *************************/

LRESULT CALLBACK
_window_procedure(HWND   window,
                  UINT   message,
                  WPARAM window_param,
                  LPARAM data_param)
{
  switch (message)
    {
    case WM_CLOSE:
        PostQuitMessage(0);
        return 0;
    case WM_KEYUP:
        if (window_param == 'Q')
        {
            PostQuitMessage(0);
        }
        if (window_param == 'F')
        {
            Window *win;

#ifdef _DEBUG
            printf("fullscreen\n");
            fflush(stdout);
#endif
            win = (Window *)GetWindowLongPtr(window, GWLP_USERDATA);
            window_fullscreen_set(win, !win->fullscreen);
        }
        if (window_param == 'R')
        {
            Window *win;

#ifdef _DEBUG
            printf("rotation\n");
            fflush(stdout);
#endif
            win = (Window *)GetWindowLongPtr(window, GWLP_USERDATA);
            window_rotation_set(win, (win->rotation + 90) % 360);
        }
        return 0;
    case WM_ERASEBKGND:
        /* no need to erase back */
        return 1;
    /* GDI notifications */
    case WM_CREATE:
#ifdef _DEBUG
        printf(" * WM_CREATE\n");
        fflush(stdout);
#endif
        return 0;
    case WM_SIZE:
    {
        Window * win;

#ifdef _DEBUG
        printf(" * WM_SIZE\n");
        fflush(stdout);
#endif

        win = (Window *)GetWindowLongPtr(window, GWLP_USERDATA);
        d3d_resize(win->d3d,
                   (UINT)LOWORD(data_param), (UINT)HIWORD(data_param));

        return 0;
    }
    case WM_PAINT:
    {
#ifdef _DEBUG
        printf(" * WM_PAINT\n");
        fflush(stdout);
#endif

        if (GetUpdateRect(window, NULL, FALSE))
        {
            PAINTSTRUCT ps;
            Window *win;

            BeginPaint(window, &ps);

            win = (Window *)GetWindowLongPtr(window, GWLP_USERDATA);
            d3d_render(win->d3d);

            EndPaint(window, &ps);
        }

        return 0;
      }
    default:
      return DefWindowProc(window, message, window_param, data_param);
    }
}

Window *window_new(int x, int y, int w, int h)
{
    WNDCLASS wc;
    RECT r;
    Window *win;

    win = (Window *)calloc(1, sizeof(Window));
    if (!win)
        return NULL;

    win->instance = GetModuleHandle(NULL);
    if (!win->instance)
        goto free_win;

    memset (&wc, 0, sizeof (WNDCLASS));
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = _window_procedure;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = win->instance;
    wc.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor (NULL, IDC_ARROW);
    wc.hbrBackground = NULL;
    wc.lpszMenuName =  NULL;
    wc.lpszClassName = "D3D";

    if(!RegisterClass(&wc))
        goto free_library;

    r.left = 0;
    r.top = 0;
    r.right = w;
    r.bottom = h;
    if (!AdjustWindowRectEx(&r,
                            WS_OVERLAPPEDWINDOW | WS_SIZEBOX,
                            FALSE,
                            0U))
        goto unregister_class;

    win->win = CreateWindowEx(0U,
                              "D3D", "Test",
                              WS_OVERLAPPEDWINDOW | WS_SIZEBOX,
                              x, y,
                              r.right - r.left,
                              r.bottom - r.top,
                              NULL,
                              NULL, win->instance, NULL);
    if (!win->win)
        goto unregister_class;

    return win;

  unregister_class:
    UnregisterClass("D2D", win->instance);
  free_library:
    FreeLibrary(win->instance);
  free_win:
    free(win);

    return NULL;
}

void window_del(Window *win)
{
    if (!win)
        return;

    DestroyWindow(win->win);
    UnregisterClass("D2D", win->instance);
    FreeLibrary(win->instance);
    free(win);
}

void window_show(Window *win)
{
    ShowWindow(win->win, SW_SHOWNORMAL);
}

void window_fullscreen_set(Window *win, unsigned int on)
{
    HWND prev;
    DWORD style;
    DWORD exstyle;
    UINT flags;
    int x;
    int y;
    int w;
    int h;

    on =  !!on;
    if ((win->fullscreen && on) ||
        (!win->fullscreen && !on))
        return;

    if (on)
    {
        MONITORINFO mi;
        HMONITOR monitor;

        if (!GetWindowRect(win->win, &win->rect))
        {
            printf("GetWindowRect() failed\n");
            return;
        }

        monitor = MonitorFromWindow(win->win, MONITOR_DEFAULTTONEAREST);
        mi.cbSize = sizeof(MONITORINFO);
        if (!GetMonitorInfo(monitor, &mi))
            return;

        style = WS_VISIBLE | WS_POPUP;
        exstyle = WS_EX_TOPMOST;
        prev = HWND_TOPMOST;
        x = 0;
        y = 0;
        w = mi.rcMonitor.right - mi.rcMonitor.left;
        h = mi.rcMonitor.bottom - mi.rcMonitor.top;
        flags = SWP_NOCOPYBITS | SWP_SHOWWINDOW;
    }
    else
    {

        style = WS_OVERLAPPEDWINDOW | WS_SIZEBOX;
        exstyle = 0U;
        prev = HWND_NOTOPMOST;
        x = win->rect.left;
        y = win->rect.top;
        w = win->rect.right - win->rect.left;
        h = win->rect.bottom - win->rect.top;
        flags = SWP_NOCOPYBITS | SWP_SHOWWINDOW;
    }

    SetLastError(0);
    if (!SetWindowLongPtr(win->win, GWL_STYLE, style) &&
        (GetLastError() != 0))
    {
        printf("SetWindowLongPtr() failed\n");
        return;
    }
    SetLastError(0);
    if (!SetWindowLongPtr(win->win, GWL_EXSTYLE, exstyle) &&
        (GetLastError() != 0))
    {
        printf("SetWindowLongPtr() failed\n");
        return;
    }
    if (!SetWindowPos(win->win, prev, x, y, w, h, flags))
    {
        printf("SetWindowPos() failed\n");
        return;
    }

    win->fullscreen = on;
}

void window_rotation_set(Window *win, int rotation)
{
    int rdiff;

    if (win->rotation == rotation)
        return;

    rdiff = win->rotation - rotation;
    if (rdiff < 0) rdiff = -rdiff;

    if (rdiff != 180)
    {
        RECT r;

        if (!GetWindowRect(win->win, &r))
            return;

        if (!MoveWindow(win->win,
                        r.left, r.top,
                        r.bottom - r.top, r.right - r.left,
                        TRUE))
        {
            printf("MoveWindow() failed\n");
            return;
        }
    }

    win->rotation = rotation;
}

int main()
{
    Window *win;
    D3d *d3d;
    int ret = 0;

    /* remove scaling on HiDPI */
#if _WIN32_WINNT >= 0x0A00
    SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_SYSTEM_AWARE);
#endif

    win = window_new(100, 100, 800, 480);
    if (!win)
        return ret;

    d3d = d3d_init(win, 0);
    if (!d3d)
    {
        printf(" * d3d_init() failed\n");
        fflush(stdout);
        goto del_window;
    }

    ret = 1;

    SetWindowLongPtr(win->win, GWLP_USERDATA, (LONG_PTR)win);

    window_show(win);

    /* mesage loop */
    while(1)
    {
        MSG msg;
        BOOL ret;

        ret = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
        if (ret)
        {
            do
            {
                if (msg.message == WM_QUIT)
                  goto beach;
                TranslateMessage(&msg);
                DispatchMessageW(&msg);
            } while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE));
        }
    }

  beach:
    d3d_shutdown(d3d);
  del_window:
    window_del(win);

    return ret;
}
win.diff
--- win_1.c	2022-04-12 13:22:22.688617800 +0200
+++ win_2.c	2022-04-12 13:20:28.238974400 +0200
@@ -1,19 +1,4 @@
 
-/*
- * Windows 10:
-
- gcc -g -O2 -Wall -Wextra -o d3d d3d_2.c win_2.c -ld3d11 -ld3dcompiler -ldxgi -luuid -D_WIN32_WINNT=0x0A00
-
- * Windows 7:
-
- gcc -g -O2 -Wall -Wextra -o d3d d3d_2.c win_2.c -ld3d11 -ld3dcompiler -ldxgi -luuid -D_WIN32_WINNT=0x0601
-
- */
-
-#if defined _WIN32_WINNT && _WIN32_WINNT >= 0x0A00
-# define HAVE_WIN10
-#endif
-
 #include <stdlib.h>
 #include <stdio.h>
 
@@ -82,17 +67,16 @@
         return 0;
     case WM_SIZE:
     {
+        Window * win;
+
 #ifdef _DEBUG
         printf(" * WM_SIZE\n");
         fflush(stdout);
 #endif
 
-        /*
-         * here, we will change the size of the needed d3d objects:
-         * - the viewport
-         * - the swap chain
-         * - the render target view
-         */
+        win = (Window *)GetWindowLongPtr(window, GWLP_USERDATA);
+        d3d_resize(win->d3d,
+                   (UINT)LOWORD(data_param), (UINT)HIWORD(data_param));
 
         return 0;
     }
@@ -106,9 +90,12 @@
         if (GetUpdateRect(window, NULL, FALSE))
         {
             PAINTSTRUCT ps;
+            Window *win;
+
             BeginPaint(window, &ps);
 
-            /* here, we will render a frame */
+            win = (Window *)GetWindowLongPtr(window, GWLP_USERDATA);
+            d3d_render(win->d3d);
 
             EndPaint(window, &ps);
         }
@@ -308,6 +295,8 @@
 int main()
 {
     Window *win;
+    D3d *d3d;
+    int ret = 0;
 
     /* remove scaling on HiDPI */
 #if _WIN32_WINNT >= 0x0A00
@@ -316,7 +305,17 @@
 
     win = window_new(100, 100, 800, 480);
     if (!win)
-        return 1;
+        return ret;
+
+    d3d = d3d_init(win, 0);
+    if (!d3d)
+    {
+        printf(" * d3d_init() failed\n");
+        fflush(stdout);
+        goto del_window;
+    }
+
+    ret = 1;
 
     SetWindowLongPtr(win->win, GWLP_USERDATA, (LONG_PTR)win);
 
@@ -342,7 +341,9 @@
     }
 
   beach:
+    d3d_shutdown(d3d);
+  del_window:
     window_del(win);
 
-    return 0;
+    return ret;
 }
d3d_1.c
/*
 * Windows 10:

 gcc -g -O2 -Wall -Wextra -o d3d d3d_1.c win_2.c -ld3d11 -ldxgi -luuid -D_WIN32_WINNT=0x0A00

 * Windows 7:

 gcc -g -O2 -Wall -Wextra -o d3d d3d_1.c win_2.c -ld3d11 -ldxgi -luuid -D_WIN32_WINNT=0x0601

 */

#include <stdio.h>

#if defined _WIN32_WINNT && _WIN32_WINNT >= 0x0A00
# define HAVE_WIN10
#endif

/* C API for d3d11 */
#define COBJMACROS

#ifdef HAVE_WIN10
# include <dxgi1_3.h>
#else
# include <dxgi.h>
#endif
#include <d3d11.h>

#include "d3d_1.h"

#ifdef _DEBUG
# define FCT \
do { printf(" * %s\n", __FUNCTION__); fflush(stdout); } while (0)
#else
# define FCT \
do { } while (0)
#endif

struct D3d
{
    /* DXGI */
#ifdef HAVE_WIN10
    IDXGIFactory2 *dxgi_factory;
    IDXGISwapChain1 *dxgi_swapchain;
#else
    IDXGIFactory *dxgi_factory;
    IDXGISwapChain *dxgi_swapchain;
#endif
    /* D3D11 */
    ID3D11Device *d3d_device;
    ID3D11DeviceContext *d3d_device_ctx;
    ID3D11RenderTargetView *d3d_render_target_view;
    ID3D11InputLayout *d3d_input_layout;
    ID3D11VertexShader *d3d_vertex_shader;
    ID3D11RasterizerState *d3d_rasterizer_state;
    ID3D11PixelShader *d3d_pixel_shader;
    D3D11_VIEWPORT viewport;
    unsigned int vsync : 1;
};

/************************** D3D11 **************************/

static void d3d_refresh_rate_get(D3d *d3d, UINT *num, UINT *den)
{
    DXGI_MODE_DESC *display_mode_list = NULL; /* 28 bytes */
    IDXGIAdapter *dxgi_adapter;
    IDXGIOutput *dxgi_output;
    UINT nbr_modes;
    UINT i;
    HRESULT res;

    *num = 0U;
    *den = 1U;

    if (!d3d->vsync)
        return;

    /* adapter of primary desktop : pass 0U */
    res = IDXGIFactory_EnumAdapters(d3d->dxgi_factory, 0U, &dxgi_adapter);
    if (FAILED(res))
        return;

    /* output of primary desktop : pass 0U */
    res = IDXGIAdapter_EnumOutputs(dxgi_adapter, 0U, &dxgi_output);
    if (FAILED(res))
        goto release_dxgi_adapter;

    /* number of mode that fit the format */
     res = IDXGIOutput_GetDisplayModeList(dxgi_output,
                                         DXGI_FORMAT_B8G8R8A8_UNORM,
                                         DXGI_ENUM_MODES_INTERLACED,
                                         &nbr_modes, NULL);
    if (FAILED(res))
        goto release_dxgi_output;

    printf("display mode list : %d\n", nbr_modes);
    fflush(stdout);
    display_mode_list = (DXGI_MODE_DESC *)malloc(nbr_modes * sizeof(DXGI_MODE_DESC));
    if (!display_mode_list)
        goto release_dxgi_output;

    /* fill the mode list */
    res = IDXGIOutput_GetDisplayModeList(dxgi_output,
                                         DXGI_FORMAT_B8G8R8A8_UNORM,
                                         DXGI_ENUM_MODES_INTERLACED,
                                         &nbr_modes, display_mode_list);
    if (FAILED(res))
        goto free_mode_list;

    for (i = 0; i < nbr_modes; i++)
    {
        if ((display_mode_list[i].Width == (UINT)GetSystemMetrics(SM_CXSCREEN)) &&
            (display_mode_list[i].Height == (UINT)GetSystemMetrics(SM_CYSCREEN)))
        {
            *num = display_mode_list[i].RefreshRate.Numerator;
            *den = display_mode_list[i].RefreshRate.Denominator;
            break;
        }
    }

#ifdef _DEBUG
    {
        DXGI_ADAPTER_DESC adapter_desc;

        IDXGIAdapter_GetDesc(dxgi_adapter, &adapter_desc);
        printf(" * video mem: %llu B, %llu MB\n",
               adapter_desc.DedicatedVideoMemory,
               adapter_desc.DedicatedVideoMemory / 1024 / 1024);
        fflush(stdout);
        wprintf(L" * description: %ls\n", adapter_desc.Description);
        fflush(stdout);
    }
#endif

  free_mode_list:
    free(display_mode_list);
  release_dxgi_output:
    IDXGIOutput_Release(dxgi_output);
  release_dxgi_adapter:
    IDXGIFactory_Release(dxgi_adapter);
}

D3d *d3d_init(Window *win, int vsync)
{
#ifdef HAVE_WIN10
    DXGI_SWAP_CHAIN_DESC1 desc_sw;
    DXGI_SWAP_CHAIN_FULLSCREEN_DESC desc_fs;
#else
    DXGI_SWAP_CHAIN_DESC desc_sw;
#endif
    D3D11_RASTERIZER_DESC desc_rs;
    D3d *d3d;
    RECT r;
    HRESULT res;
    UINT flags;
    UINT num;
    UINT den;
    D3D_FEATURE_LEVEL feature_level[4];

    d3d = (D3d *)calloc(1, sizeof(D3d));
    if (!d3d)
        return NULL;

    d3d->vsync = vsync;
    win->d3d = d3d;

    /* create the DXGI factory */
    flags = 0;
#ifdef HAVE_WIN10
# ifdef _DEBUG
    flags = DXGI_CREATE_FACTORY_DEBUG;
# endif
    res = CreateDXGIFactory2(flags, &IID_IDXGIFactory2, (void **)&d3d->dxgi_factory);
#else
    res = CreateDXGIFactory(&IID_IDXGIFactory, (void **)&d3d->dxgi_factory);
#endif
    if (FAILED(res))
        goto free_d3d;

    /* software engine functions are called from the main loop */
    flags = D3D11_CREATE_DEVICE_SINGLETHREADED |
            D3D11_CREATE_DEVICE_BGRA_SUPPORT;
#ifdef HAVE_WIN10
# ifdef _DEBUG
    flags |= D3D11_CREATE_DEVICE_DEBUG;
# endif
#endif

    feature_level[0] = D3D_FEATURE_LEVEL_11_1;
    feature_level[1] = D3D_FEATURE_LEVEL_11_0;
    feature_level[2] = D3D_FEATURE_LEVEL_10_1;
    feature_level[3] = D3D_FEATURE_LEVEL_10_0;

    /* create device and device context with hardware support */
    res = D3D11CreateDevice(NULL,
                            D3D_DRIVER_TYPE_HARDWARE,
                            NULL,
                            flags,
                            feature_level,
                            3U,
                            D3D11_SDK_VERSION,
                            &d3d->d3d_device,
                            NULL,
                            &d3d->d3d_device_ctx);
    if (FAILED(res))
        goto release_dxgi_factory2;

    if (!GetClientRect(win->win, &r))
        goto release_d3d_device;

    /*
     * create the swap chain. It needs some settings...
     * the size of the internal buffers
     * the image format
     * the number of back buffers (>= 2 for flip model, see SwapEffect field)
     * vsync enabled: need refresh rate
     *
     * Settings are different in win 7 and win10
     */

    d3d_refresh_rate_get(d3d, &num, &den);

#ifdef HAVE_WIN10
    desc_sw.Width = r.right - r.left;
    desc_sw.Height = r.bottom - r.top;
    desc_sw.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
    desc_sw.Stereo = FALSE;
#else
    desc_sw.BufferDesc.Width= r.right - r.left;
    desc_sw.BufferDesc.Height = r.bottom - r.top;
    desc_sw.BufferDesc.RefreshRate.Numerator = num;
    desc_sw.BufferDesc.RefreshRate.Denominator = den;
    desc_sw.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;;
    desc_sw.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
    desc_sw.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
#endif
    desc_sw.SampleDesc.Count = 1U;
    desc_sw.SampleDesc.Quality = 0U;
    desc_sw.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    desc_sw.BufferCount = 2U;
#ifdef HAVE_WIN10
    desc_sw.Scaling = DXGI_SCALING_NONE;
#else
    desc_sw.OutputWindow = win->win;
    desc_sw.Windowed = TRUE;
#endif
    desc_sw.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
#ifdef HAVE_WIN10
    desc_sw.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
#endif
    desc_sw.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;

#ifdef HAVE_WIN10
    desc_fs.RefreshRate.Numerator = num;
    desc_fs.RefreshRate.Denominator = den;
    desc_fs.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
    desc_fs.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
    desc_fs.Windowed = TRUE;
#endif

#ifdef HAVE_WIN10
    res = IDXGIFactory2_CreateSwapChainForHwnd(d3d->dxgi_factory,
                                               (IUnknown *)d3d->d3d_device,
                                               win->win,
                                               &desc_sw,
                                               &desc_fs,
                                               NULL,
                                               &d3d->dxgi_swapchain);
#else
    res = IDXGIFactory_CreateSwapChain(d3d->dxgi_factory,
                                       (IUnknown *)d3d->d3d_device,
                                       &desc_sw,
                                       &d3d->dxgi_swapchain);
#endif
    if (FAILED(res))
        goto release_d3d_device;

    /* rasterizer */
    desc_rs.FillMode = D3D11_FILL_SOLID;
    desc_rs.CullMode = D3D11_CULL_NONE;
    desc_rs.FrontCounterClockwise = FALSE;
    desc_rs.DepthBias = 0;
    desc_rs.DepthBiasClamp = 0.0f;
    desc_rs.SlopeScaledDepthBias = 0.0f;
    desc_rs.DepthClipEnable = TRUE;
    desc_rs.ScissorEnable = FALSE;
    desc_rs.MultisampleEnable = FALSE;
    desc_rs.AntialiasedLineEnable = FALSE;

    res = ID3D11Device_CreateRasterizerState(d3d->d3d_device,
                                             &desc_rs,
                                             &d3d->d3d_rasterizer_state);
    if (FAILED(res))
        goto release_dxgi_swapchain;

    return d3d;

  release_dxgi_swapchain:
#ifdef HAVE_WIN10
    IDXGISwapChain1_SetFullscreenState(d3d->dxgi_swapchain, FALSE, NULL);
    IDXGISwapChain1_Release(d3d->dxgi_swapchain);
#else
    IDXGISwapChain_SetFullscreenState(d3d->dxgi_swapchain, FALSE, NULL);
    IDXGISwapChain_Release(d3d->dxgi_swapchain);
#endif
  release_d3d_device:
    ID3D11DeviceContext_Release(d3d->d3d_device_ctx);
    ID3D11Device_Release(d3d->d3d_device);
  release_dxgi_factory2:
#ifdef HAVE_WIN10
    IDXGIFactory2_Release(d3d->dxgi_factory);
#else
    IDXGIFactory_Release(d3d->dxgi_factory);
#endif
  free_d3d:
    free(d3d);

    return NULL;
}

void d3d_shutdown(D3d *d3d)
{
#ifdef _DEBUG
    ID3D11Debug *d3d_debug;
    HRESULT res;
#endif

    if (!d3d)
        return;

#ifdef _DEBUG
    res = ID3D11Debug_QueryInterface(d3d->d3d_device, &IID_ID3D11Debug,
                                     (void **)&d3d_debug);
#endif

    ID3D11RasterizerState_Release(d3d->d3d_rasterizer_state);
    ID3D11RenderTargetView_Release(d3d->d3d_render_target_view);
#ifdef HAVE_WIN10
    IDXGISwapChain1_SetFullscreenState(d3d->dxgi_swapchain, FALSE, NULL);
    IDXGISwapChain1_Release(d3d->dxgi_swapchain);
#else
    IDXGISwapChain_SetFullscreenState(d3d->dxgi_swapchain, FALSE, NULL);
    IDXGISwapChain_Release(d3d->dxgi_swapchain);
#endif
    ID3D11DeviceContext_Release(d3d->d3d_device_ctx);
    ID3D11Device_Release(d3d->d3d_device);
#ifdef HAVE_WIN10
    IDXGIFactory2_Release(d3d->dxgi_factory);
#else
    IDXGIFactory_Release(d3d->dxgi_factory);
#endif
    free(d3d);

#ifdef _DEBUG
    if (SUCCEEDED(res))
    {
        ID3D11Debug_ReportLiveDeviceObjects(d3d_debug, D3D11_RLDO_DETAIL);
        ID3D11Debug_Release(d3d_debug);
    }
#endif
}

void d3d_resize(D3d *d3d, UINT width, UINT height)
{
    D3D11_RENDER_TARGET_VIEW_DESC desc_rtv;
    ID3D11Texture2D *back_buffer;
    HRESULT res;

    FCT;

    /* unset the render target view in the output merger */
    ID3D11DeviceContext_OMSetRenderTargets(d3d->d3d_device_ctx,
                                           0U, NULL, NULL);

    /* release the render target view */
    if (d3d->d3d_render_target_view)
        ID3D11RenderTargetView_Release(d3d->d3d_render_target_view);

    /* resize the internal nuffers of the swapt chain to the new size */
#ifdef HAVE_WIN10
    res = IDXGISwapChain1_ResizeBuffers(d3d->dxgi_swapchain,
                                        0U, /* preserve buffer count */
                                        width, height,
                                        DXGI_FORMAT_UNKNOWN, /* preserve format */
                                        0U);
#else
    res = IDXGISwapChain_ResizeBuffers(d3d->dxgi_swapchain,
                                       0U, /* preserve buffer count */
                                       width, height,
                                       DXGI_FORMAT_UNKNOWN, /* preserve format */
                                       0U);
#endif
    if ((res == DXGI_ERROR_DEVICE_REMOVED) ||
        (res == DXGI_ERROR_DEVICE_RESET) ||
        (res == DXGI_ERROR_DRIVER_INTERNAL_ERROR))
    {
        return;
    }

    if (FAILED(res))
    {
        printf("ResizeBuffers() failed\n");
        fflush(stdout);
        return;
    }

    /* get the internal buffer of the swap chain */
#ifdef HAVE_WIN10
    res = IDXGISwapChain1_GetBuffer(d3d->dxgi_swapchain, 0,
                                    &IID_ID3D11Texture2D,
                                    (void **)&back_buffer);
#else
    res = IDXGISwapChain_GetBuffer(d3d->dxgi_swapchain, 0,
                                   &IID_ID3D11Texture2D,
                                   (void **)&back_buffer);
#endif
    if (FAILED(res))
    {
        printf("swapchain GetBuffer() failed\n");
        fflush(stdout);
        return;
    }

    ZeroMemory(&desc_rtv, sizeof(D3D11_RENDER_TARGET_VIEW_DESC));
    desc_rtv.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
    desc_rtv.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;

    /* create the new render target view from this internal buffer */
    res = ID3D11Device_CreateRenderTargetView(d3d->d3d_device,
                                              (ID3D11Resource *)back_buffer,
                                              &desc_rtv,
                                              &d3d->d3d_render_target_view);

    ID3D11Texture2D_Release(back_buffer);

    /* set viewport, depends on size of the window */
    d3d->viewport.TopLeftX = 0.0f;
    d3d->viewport.TopLeftY = 0.0f;
    d3d->viewport.Width = (float)width;
    d3d->viewport.Height = (float)height;
    d3d->viewport.MinDepth = 0.0f;
    d3d->viewport.MaxDepth = 1.0f;
}

void d3d_render(D3d *d3d)
{
#ifdef HAVE_WIN10
    DXGI_PRESENT_PARAMETERS pp;
#endif
    const FLOAT color[4] = { 0.10f, 0.18f, 0.24f, 1.0f };
    HRESULT res;

    FCT;

    /* clear render target */
    ID3D11DeviceContext_ClearRenderTargetView(d3d->d3d_device_ctx,
                                              d3d->d3d_render_target_view,
                                              color);

    /* Rasterizer Stage */
    ID3D11DeviceContext_RSSetState(d3d->d3d_device_ctx,
                                   d3d->d3d_rasterizer_state);
    ID3D11DeviceContext_RSSetViewports(d3d->d3d_device_ctx,
                                       1U, &d3d->viewport);

    /* Output Merger stage */
    ID3D11DeviceContext_OMSetRenderTargets(d3d->d3d_device_ctx,
                                           1U, &d3d->d3d_render_target_view,
                                           NULL);

    /*
     * present frame, that is flip the back buffer and the front buffer
     * if no vsync, we present immediatly
     */
#ifdef HAVE_WIN10
    pp.DirtyRectsCount = 0;
    pp.pDirtyRects = NULL;
    pp.pScrollRect = NULL;
    pp.pScrollOffset = NULL;
    res = IDXGISwapChain1_Present1(d3d->dxgi_swapchain,
                                   d3d->vsync ? 1 : 0, 0, &pp);
#else
    res = IDXGISwapChain_Present(d3d->dxgi_swapchain,
                                 d3d->vsync ? 1 : 0, 0);
#endif
    if (res == DXGI_ERROR_DEVICE_RESET || res == DXGI_ERROR_DEVICE_REMOVED)
    {
        printf("device removed or lost, need to recreate everything\n");
        fflush(stdout);
    }
    else if (res == DXGI_STATUS_OCCLUDED)
    {
        printf("window is not visible, so vsync won't work. Let's sleep a bit to reduce CPU usage\n");
        fflush(stdout);
    }
}

When launched, you should get something like

Display a pink triangle

The first step for en Evas software engine is to draw pixels in a rectangle. In Direct3D, a rectangle is composed of two triangles, so the first step is to draw one triangle. The next section will modify the code to draw a pink rectangle.

For now, we used the Rasterizer and Output Merger stages of the pipeline. The following stages are needed to draw something:

  1. the Input Asembler (IA) stage, which reads the geometric data,
  2. the Vertex Shader (VS) stage that transforms the geometric data built in the IA stage,
  3. the Pixel Shader (PS) stage which modify each pixel that will be seen.

So first, the 3D scene (actually a 2D scene for our purposes) must be created. that is, our pink triangle.

Vertices and coordinate system

A vertex is a point in 3D. is is composed of three coordinates of type float, which are stored in a structure. But as we want 2D, only 2 components are needed:

typedef struct
{
    FLOAT x;
    FLOAT y;
} Vertex;

Later, this structure will be expanded with color components and textures.

The vertices are displayed in a (normalized) coordinate system different from the pixels on the screen (diagram taken from Direct3D 11.3 Functional Specification, section 3.3.1):

So the normalized coordinate system goes from (-1, 1), corresponding to pixel (0,0) in the window of size (w, h) pixels, to (1, -1), corresponding to pixel (w, h) outside the window. So the normalized coordinate (0, 0) corresponds to the center of the window.

So conversion from pixel coordinates on the screen (from (0, 0) to (w, h), that is 1 pixel outside the window) to normalized coordinates (from (-1, 1) to (1, -1)) must be computed. It is basic math. Let us call xf(x) the value of the normalized coordinates, function of the x coordinate of a pixel. The value xf(x) is affine in x, meaning xf(x) = ax + b, a and b being real numbers. So

  • xf(0) = -1 = b, and
  • xf(w) = 1 = aw - 1

So xf(x) = (2x - w)/w. Similarly, yf(y) = (h - 2y) / h. Let us define these two macros:

#define XF(w,x) ((float)(2 * (x) - (w)) / (float)(w))
#define YF(h,y) ((float)((h) - 2 * (y)) / (float)(h))

The scene

We have to pass the vertices of our triangle to the GPU. Several steps are needed:

  • A vertex buffer which contains the list of the vertices (and other values). The vertices must be set in clock-wise order. The vertex buffer will be binding to the pipeline in the IA stage with IASetVertexBuffers().
  • An index buffer which is a lit of indices (integer numbers) that index the vertices in the vertex buffer. For a triangle, it is useless, but for a rectangle, it will be used. Index buffers are used for memory and speed optimizations.. The vertex buffer will be binding to the pipeline in the IA stage with IASetIndexBuffer.
  • an array of D3D11_INPUT_ELEMENT_DESC which describes the format of the coordinates (or color, or texture): float or int, normalized syntax or not etc... For now, only coordinates.
  • a HLSL program to tell the GPU what to do with the vertices. It could be nothing, rotation, translation etc.. This is a small program called a Vertex Shader, which syntax follow the HLSL specification, and which is close to C. The Vertex Shader is bounding to the pipeline at the Vertex Shader stage with VSSetShader().
  • a D3D11 object called input layout created with CreateInputLayout() with the array of D3D11_INPUT_ELEMENT_DESC and the Vertex Shader. It is bounding to the pipeline at the IA stage with IASetInputLayout.
  • a HLSL program to tell the GPU what to do with the pixels. This is a small program called a Pixel Shader, which syntax follow the HLSL specification, and which is close to C. The Pixel Shader is bounding to the pipeline at the Pixel Shader stage with PSSetShader(). -a topology which set how the triangles are linked toggether. See the diagram belo (taken from Direct3D 11.3 Functional Specification, section 3.10). We use D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP for our future rectangle. The topology is set at the IA stage with IASetPrimitiveTopology.

NOTE: the Vertex and Pixel Shaders are compiled at run time for now. But it is better to compile them with a HLSL compiler and include the bytecode in the program.

Vertex and Pixel shaders

The following code is the HLSL code which contains both the function main_vs() for the vertex shader program, and the function main_ps() for the pixel shader program:

shader_1.hlsl
struct vs_input
{
    float2 position : POSITION;
};

struct ps_input
{
    float4 position : SV_POSITION;
};

ps_input main_vs(vs_input input )
{
    ps_input output;
    output.position = float4(input.position, 0.0f, 1.0f);
    return output;
}

float4 main_ps(ps_input input) : SV_TARGET
{
    return float4(1.0f, 0.0f, 1.0f, 1.0f);
}
  • the vertex shader program, named main_vs(), take as input a structure which has the coordinates of a vertex (of our triangle). It must be identical to the Vertex structure above. It can also contains a color or a texture. The function returns a position with the 3 coordinates x, y and z, plus a fourth one. The 4 coordinates are used when rotations or translations ar eneeded. Then, they are transformed with a matrix of size 4x4. The new coordinates are returned and passed to the pixel shader program. This function will be called for each vertex in the vertex buffer.
  • the pixel shader program, named main_ps(), takes as input the output of main_vs(), that is, the final coordinate of the vertex. It will be called for each pixel in the polygon (that is a triangle in our case). it returns the color a the corresponding pixel, that is pink for each pixel in our case.

Rendering

  • We first call all the drawing operations. For our triangle, we call DrawIndexed(). This function, or the function Draw(), must be called for each primitive (point, line, triangles) that must be rendered.
  • When finished, swap front and back buffer with the Present() function as we already did.

The full code

The shader_1.hlsl must be saved in the same directory than win_2.c and d3d_2.c (as well as all the other following sections). Here is thefull D3D code followed with the diff between d3d_1.c and d3d_2.c

d3d_2.c
/*
 * Windows 10:

 gcc -g -O2 -Wall -Wextra -o d3d d3d_2.c win_2.c -ld3d11 -ld3dcompiler -ldxgi -luuid -D_WIN32_WINNT=0x0A00

 * Windows 7:

 gcc -g -O2 -Wall -Wextra -o d3d d3d_2.c win_2.c -ld3d11 -ld3dcompiler -ldxgi -luuid -D_WIN32_WINNT=0x0601

 */

#include <stdio.h>

#if defined _WIN32_WINNT && _WIN32_WINNT >= 0x0A00
# define HAVE_WIN10
#endif

/* C API for d3d11 */
#define COBJMACROS

#ifdef HAVE_WIN10
# include <dxgi1_3.h>
#else
# include <dxgi.h>
#endif
#include <d3d11.h>
#include <d3dcompiler.h>

#include "d3d_1.h"

#ifdef _DEBUG
# define FCT \
do { printf(" * %s\n", __FUNCTION__); fflush(stdout); } while (0)
#else
# define FCT \
do { } while (0)
#endif

#define XF(w,x) ((float)(2 * (x) - (w)) / (float)(w))
#define YF(h,y) ((float)((h) - 2 * (y)) / (float)(h))

struct D3d
{
    /* DXGI */
#ifdef HAVE_WIN10
    IDXGIFactory2 *dxgi_factory;
    IDXGISwapChain1 *dxgi_swapchain;
#else
    IDXGIFactory *dxgi_factory;
    IDXGISwapChain *dxgi_swapchain;
#endif
    /* D3D11 */
    ID3D11Device *d3d_device;
    ID3D11DeviceContext *d3d_device_ctx;
    ID3D11RenderTargetView *d3d_render_target_view;
    ID3D11InputLayout *d3d_input_layout;
    ID3D11VertexShader *d3d_vertex_shader;
    ID3D11RasterizerState *d3d_rasterizer_state;
    ID3D11PixelShader *d3d_pixel_shader;
    D3D11_VIEWPORT viewport;
    unsigned int vsync : 1;
};

typedef struct
{
    FLOAT x;
    FLOAT y;
} Vertex;

/************************** D3D11 **************************/

static void d3d_refresh_rate_get(D3d *d3d, UINT *num, UINT *den)
{
    DXGI_MODE_DESC *display_mode_list = NULL; /* 28 bytes */
    IDXGIAdapter *dxgi_adapter;
    IDXGIOutput *dxgi_output;
    UINT nbr_modes;
    UINT i;
    HRESULT res;

    *num = 0U;
    *den = 1U;

    if (!d3d->vsync)
        return;

    /* adapter of primary desktop : pass 0U */
    res = IDXGIFactory_EnumAdapters(d3d->dxgi_factory, 0U, &dxgi_adapter);
    if (FAILED(res))
        return;

    /* output of primary desktop : pass 0U */
    res = IDXGIAdapter_EnumOutputs(dxgi_adapter, 0U, &dxgi_output);
    if (FAILED(res))
        goto release_dxgi_adapter;

    /* number of mode that fit the format */
     res = IDXGIOutput_GetDisplayModeList(dxgi_output,
                                         DXGI_FORMAT_B8G8R8A8_UNORM,
                                         DXGI_ENUM_MODES_INTERLACED,
                                         &nbr_modes, NULL);
    if (FAILED(res))
        goto release_dxgi_output;

    printf("display mode list : %d\n", nbr_modes);
    fflush(stdout);
    display_mode_list = (DXGI_MODE_DESC *)malloc(nbr_modes * sizeof(DXGI_MODE_DESC));
    if (!display_mode_list)
        goto release_dxgi_output;

    /* fill the mode list */
    res = IDXGIOutput_GetDisplayModeList(dxgi_output,
                                         DXGI_FORMAT_B8G8R8A8_UNORM,
                                         DXGI_ENUM_MODES_INTERLACED,
                                         &nbr_modes, display_mode_list);
    if (FAILED(res))
        goto free_mode_list;

    for (i = 0; i < nbr_modes; i++)
    {
        if ((display_mode_list[i].Width == (UINT)GetSystemMetrics(SM_CXSCREEN)) &&
            (display_mode_list[i].Height == (UINT)GetSystemMetrics(SM_CYSCREEN)))
        {
            *num = display_mode_list[i].RefreshRate.Numerator;
            *den = display_mode_list[i].RefreshRate.Denominator;
            break;
        }
    }

#ifdef _DEBUG
    {
        DXGI_ADAPTER_DESC adapter_desc;

        IDXGIAdapter_GetDesc(dxgi_adapter, &adapter_desc);
        printf(" * video mem: %llu B, %llu MB\n",
               adapter_desc.DedicatedVideoMemory,
               adapter_desc.DedicatedVideoMemory / 1024 / 1024);
        fflush(stdout);
        wprintf(L" * description: %ls\n", adapter_desc.Description);
        fflush(stdout);
    }
#endif

  free_mode_list:
    free(display_mode_list);
  release_dxgi_output:
    IDXGIOutput_Release(dxgi_output);
  release_dxgi_adapter:
    IDXGIFactory_Release(dxgi_adapter);
}

D3d *d3d_init(Window *win, int vsync)
{
    D3D11_INPUT_ELEMENT_DESC desc_ie[] =
    {
        { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }
    };
#ifdef HAVE_WIN10
    DXGI_SWAP_CHAIN_DESC1 desc_sw;
    DXGI_SWAP_CHAIN_FULLSCREEN_DESC desc_fs;
#else
    DXGI_SWAP_CHAIN_DESC desc_sw;
#endif
    D3D11_RASTERIZER_DESC desc_rs;
    D3d *d3d;
    RECT r;
    HRESULT res;
    UINT flags;
    UINT num;
    UINT den;
    D3D_FEATURE_LEVEL feature_level[4];
    ID3DBlob *vs_blob; /* vertex shader blob ptr */
    ID3DBlob *ps_blob; /* pixel shader blob ptr */
    ID3DBlob *err_blob; /* error blob ptr */

    d3d = (D3d *)calloc(1, sizeof(D3d));
    if (!d3d)
        return NULL;

    d3d->vsync = vsync;
    win->d3d = d3d;

    /* create the DXGI factory */
    flags = 0;
#ifdef HAVE_WIN10
# ifdef _DEBUG
    flags = DXGI_CREATE_FACTORY_DEBUG;
# endif
    res = CreateDXGIFactory2(flags, &IID_IDXGIFactory2, (void **)&d3d->dxgi_factory);
#else
    res = CreateDXGIFactory(&IID_IDXGIFactory, (void **)&d3d->dxgi_factory);
#endif
    if (FAILED(res))
        goto free_d3d;

    /* software engine functions are called from the main loop */
    flags = D3D11_CREATE_DEVICE_SINGLETHREADED |
            D3D11_CREATE_DEVICE_BGRA_SUPPORT;
#ifdef HAVE_WIN10
# ifdef _DEBUG
    flags |= D3D11_CREATE_DEVICE_DEBUG;
# endif
#endif

    feature_level[0] = D3D_FEATURE_LEVEL_11_1;
    feature_level[1] = D3D_FEATURE_LEVEL_11_0;
    feature_level[2] = D3D_FEATURE_LEVEL_10_1;
    feature_level[3] = D3D_FEATURE_LEVEL_10_0;

    /* create device and device context with hardware support */
    res = D3D11CreateDevice(NULL,
                            D3D_DRIVER_TYPE_HARDWARE,
                            NULL,
                            flags,
                            feature_level,
                            3U,
                            D3D11_SDK_VERSION,
                            &d3d->d3d_device,
                            NULL,
                            &d3d->d3d_device_ctx);
    if (FAILED(res))
        goto release_dxgi_factory2;

    if (!GetClientRect(win->win, &r))
        goto release_d3d_device;

    /*
     * create the swap chain. It needs some settings...
     * the size of the internal buffers
     * the image format
     * the number of back buffers (>= 2 for flip model, see SwapEffect field)
     * vsync enabled: need refresh rate
     *
     * Settings are different in win 7 and win10
     */

    d3d_refresh_rate_get(d3d, &num, &den);

#ifdef HAVE_WIN10
    desc_sw.Width = r.right - r.left;
    desc_sw.Height = r.bottom - r.top;
    desc_sw.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
    desc_sw.Stereo = FALSE;
#else
    desc_sw.BufferDesc.Width= r.right - r.left;
    desc_sw.BufferDesc.Height = r.bottom - r.top;
    desc_sw.BufferDesc.RefreshRate.Numerator = num;
    desc_sw.BufferDesc.RefreshRate.Denominator = den;
    desc_sw.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;;
    desc_sw.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
    desc_sw.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
#endif
    desc_sw.SampleDesc.Count = 1U;
    desc_sw.SampleDesc.Quality = 0U;
    desc_sw.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    desc_sw.BufferCount = 2U;
#ifdef HAVE_WIN10
    desc_sw.Scaling = DXGI_SCALING_NONE;
#else
    desc_sw.OutputWindow = win->win;
    desc_sw.Windowed = TRUE;
#endif
    desc_sw.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
#ifdef HAVE_WIN10
    desc_sw.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
#endif
    desc_sw.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;

#ifdef HAVE_WIN10
    desc_fs.RefreshRate.Numerator = num;
    desc_fs.RefreshRate.Denominator = den;
    desc_fs.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
    desc_fs.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
    desc_fs.Windowed = TRUE;
#endif

#ifdef HAVE_WIN10
    res = IDXGIFactory2_CreateSwapChainForHwnd(d3d->dxgi_factory,
                                               (IUnknown *)d3d->d3d_device,
                                               win->win,
                                               &desc_sw,
                                               &desc_fs,
                                               NULL,
                                               &d3d->dxgi_swapchain);
#else
    res = IDXGIFactory_CreateSwapChain(d3d->dxgi_factory,
                                       (IUnknown *)d3d->d3d_device,
                                       &desc_sw,
                                       &d3d->dxgi_swapchain);
#endif
    if (FAILED(res))
        goto release_d3d_device;

    /* rasterizer */
    desc_rs.FillMode = D3D11_FILL_SOLID;
    desc_rs.CullMode = D3D11_CULL_NONE;
    desc_rs.FrontCounterClockwise = FALSE;
    desc_rs.DepthBias = 0;
    desc_rs.DepthBiasClamp = 0.0f;
    desc_rs.SlopeScaledDepthBias = 0.0f;
    desc_rs.DepthClipEnable = TRUE;
    desc_rs.ScissorEnable = FALSE;
    desc_rs.MultisampleEnable = FALSE;
    desc_rs.AntialiasedLineEnable = FALSE;

    res = ID3D11Device_CreateRasterizerState(d3d->d3d_device,
                                             &desc_rs,
                                             &d3d->d3d_rasterizer_state);
    if (FAILED(res))
        goto release_dxgi_swapchain;

    /* Vertex shader */
    flags = D3DCOMPILE_ENABLE_STRICTNESS;
#ifdef _DEBUG
    flags |= D3DCOMPILE_DEBUG;
#endif
    vs_blob = NULL;
    res = D3DCompileFromFile(L"shader_1.hlsl",
                             NULL,
                             D3D_COMPILE_STANDARD_FILE_INCLUDE,
                             "main_vs",
                             "vs_5_0",
                             flags,
                             0U,
                             &vs_blob,
                             &err_blob );

    if (FAILED(res))
    {
        printf(" * vs error : %s\n", (char *)ID3D10Blob_GetBufferPointer(err_blob));
        goto release_d3D_rasterizer;
    }

    res = ID3D11Device_CreateVertexShader(d3d->d3d_device,
                                          ID3D10Blob_GetBufferPointer(vs_blob),
                                          ID3D10Blob_GetBufferSize(vs_blob),
                                          NULL,
                                          &d3d->d3d_vertex_shader);

    if (FAILED(res))
    {
        printf(" * vs error : %s\n", (char *)ID3D10Blob_GetBufferPointer(err_blob));
        ID3D10Blob_Release(vs_blob);
        goto release_d3D_rasterizer;
    }

    /* create the input layout */
    res = ID3D11Device_CreateInputLayout(d3d->d3d_device,
                                         desc_ie,
                                         sizeof(desc_ie) / sizeof(D3D11_INPUT_ELEMENT_DESC),
                                         ID3D10Blob_GetBufferPointer(vs_blob),
                                         ID3D10Blob_GetBufferSize(vs_blob),
                                         &d3d->d3d_input_layout);
    ID3D10Blob_Release(vs_blob);
    if (FAILED(res))
    {
        printf(" * CreateInputLayout() failed\n");
        goto release_vertex_shader;
    }

    /* Pixel shader */
    ps_blob = NULL;
    res = D3DCompileFromFile(L"shader_1.hlsl",
                             NULL,
                             D3D_COMPILE_STANDARD_FILE_INCLUDE,
                             "main_ps",
                             "ps_5_0",
                             flags,
                             0U,
                             &ps_blob,
                             &err_blob );

    if (FAILED(res))
    {
        printf(" * ps blob error : %s\n", (char *)ID3D10Blob_GetBufferPointer(err_blob));
        goto release_input_layout;
    }

    res = ID3D11Device_CreatePixelShader(d3d->d3d_device,
                                         ID3D10Blob_GetBufferPointer(ps_blob),
                                         ID3D10Blob_GetBufferSize(ps_blob),
                                         NULL,
                                         &d3d->d3d_pixel_shader);
    ID3D10Blob_Release(ps_blob);
    if (FAILED(res))
    {
        printf(" * CreatePixelShader() failed\n");
        goto release_input_layout;
    }

    return d3d;

  release_input_layout:
    ID3D11InputLayout_Release(d3d->d3d_input_layout);
  release_vertex_shader:
    ID3D11VertexShader_Release(d3d->d3d_vertex_shader);
  release_d3D_rasterizer:
    ID3D11RasterizerState_Release(d3d->d3d_rasterizer_state);
  release_dxgi_swapchain:
#ifdef HAVE_WIN10
    IDXGISwapChain1_SetFullscreenState(d3d->dxgi_swapchain, FALSE, NULL);
    IDXGISwapChain1_Release(d3d->dxgi_swapchain);
#else
    IDXGISwapChain_SetFullscreenState(d3d->dxgi_swapchain, FALSE, NULL);
    IDXGISwapChain_Release(d3d->dxgi_swapchain);
#endif
  release_d3d_device:
    ID3D11DeviceContext_Release(d3d->d3d_device_ctx);
    ID3D11Device_Release(d3d->d3d_device);
  release_dxgi_factory2:
#ifdef HAVE_WIN10
    IDXGIFactory2_Release(d3d->dxgi_factory);
#else
    IDXGIFactory_Release(d3d->dxgi_factory);
#endif
  free_d3d:
    free(d3d);

    return NULL;
}

void d3d_shutdown(D3d *d3d)
{
#ifdef _DEBUG
    ID3D11Debug *d3d_debug;
    HRESULT res;
#endif

    if (!d3d)
        return;

#ifdef _DEBUG
    res = ID3D11Debug_QueryInterface(d3d->d3d_device, &IID_ID3D11Debug,
                                     (void **)&d3d_debug);
#endif

    ID3D11PixelShader_Release(d3d->d3d_pixel_shader);
    ID3D11InputLayout_Release(d3d->d3d_input_layout);
    ID3D11VertexShader_Release(d3d->d3d_vertex_shader);
    ID3D11RasterizerState_Release(d3d->d3d_rasterizer_state);
    ID3D11RenderTargetView_Release(d3d->d3d_render_target_view);
#ifdef HAVE_WIN10
    IDXGISwapChain1_SetFullscreenState(d3d->dxgi_swapchain, FALSE, NULL);
    IDXGISwapChain1_Release(d3d->dxgi_swapchain);
#else
    IDXGISwapChain_SetFullscreenState(d3d->dxgi_swapchain, FALSE, NULL);
    IDXGISwapChain_Release(d3d->dxgi_swapchain);
#endif
    ID3D11DeviceContext_Release(d3d->d3d_device_ctx);
    ID3D11Device_Release(d3d->d3d_device);
#ifdef HAVE_WIN10
    IDXGIFactory2_Release(d3d->dxgi_factory);
#else
    IDXGIFactory_Release(d3d->dxgi_factory);
#endif
    free(d3d);

#ifdef _DEBUG
    if (SUCCEEDED(res))
    {
        ID3D11Debug_ReportLiveDeviceObjects(d3d_debug, D3D11_RLDO_DETAIL);
        ID3D11Debug_Release(d3d_debug);
    }
#endif
}

void d3d_resize(D3d *d3d, UINT width, UINT height)
{
    D3D11_RENDER_TARGET_VIEW_DESC desc_rtv;
    ID3D11Texture2D *back_buffer;
    HRESULT res;

    FCT;

    /* unset the render target view in the output merger */
    ID3D11DeviceContext_OMSetRenderTargets(d3d->d3d_device_ctx,
                                           0U, NULL, NULL);

    /* release the render target view */
    if (d3d->d3d_render_target_view)
        ID3D11RenderTargetView_Release(d3d->d3d_render_target_view);

    /* resize the internal nuffers of the swapt chain to the new size */
#ifdef HAVE_WIN10
    res = IDXGISwapChain1_ResizeBuffers(d3d->dxgi_swapchain,
                                        0U, /* preserve buffer count */
                                        width, height,
                                        DXGI_FORMAT_UNKNOWN, /* preserve format */
                                        0U);
#else
    res = IDXGISwapChain_ResizeBuffers(d3d->dxgi_swapchain,
                                       0U, /* preserve buffer count */
                                       width, height,
                                       DXGI_FORMAT_UNKNOWN, /* preserve format */
                                       0U);
#endif
    if ((res == DXGI_ERROR_DEVICE_REMOVED) ||
        (res == DXGI_ERROR_DEVICE_RESET) ||
        (res == DXGI_ERROR_DRIVER_INTERNAL_ERROR))
    {
        return;
    }

    if (FAILED(res))
    {
        printf("ResizeBuffers() failed\n");
        fflush(stdout);
        return;
    }

    /* get the internal buffer of the swap chain */
#ifdef HAVE_WIN10
    res = IDXGISwapChain1_GetBuffer(d3d->dxgi_swapchain, 0,
                                    &IID_ID3D11Texture2D,
                                    (void **)&back_buffer);
#else
    res = IDXGISwapChain_GetBuffer(d3d->dxgi_swapchain, 0,
                                   &IID_ID3D11Texture2D,
                                   (void **)&back_buffer);
#endif
    if (FAILED(res))
    {
        printf("swapchain GetBuffer() failed\n");
        fflush(stdout);
        return;
    }

    ZeroMemory(&desc_rtv, sizeof(D3D11_RENDER_TARGET_VIEW_DESC));
    desc_rtv.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
    desc_rtv.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;

    /* create the new render target view from this internal buffer */
    res = ID3D11Device_CreateRenderTargetView(d3d->d3d_device,
                                              (ID3D11Resource *)back_buffer,
                                              &desc_rtv,
                                              &d3d->d3d_render_target_view);

    ID3D11Texture2D_Release(back_buffer);

    /* set viewport, depends on size of the window */
    d3d->viewport.TopLeftX = 0.0f;
    d3d->viewport.TopLeftY = 0.0f;
    d3d->viewport.Width = (float)width;
    d3d->viewport.Height = (float)height;
    d3d->viewport.MinDepth = 0.0f;
    d3d->viewport.MaxDepth = 1.0f;
}

/*** triangle ***/

typedef struct
{
    Vertex vertices[3];
    unsigned int indices[3];
    ID3D11Buffer* vertex_buffer;
    ID3D11Buffer* index_buffer; /* not useful for a single triangle */
    UINT stride;
    UINT offset;
    UINT count;
    UINT index_count;
} Triangle;

Triangle *triangle_new(D3d *d3d,
                       int w, int h,
                       int x1, int y1,
                       int x2, int y2,
                       int x3, int y3)
{
    D3D11_BUFFER_DESC desc;
    D3D11_SUBRESOURCE_DATA sr_data;
    Triangle *t;
    HRESULT res;

    t = (Triangle *)malloc(sizeof(Triangle));
    if (!t)
        return NULL;

    t->vertices[0].x = XF(w, x1);
    t->vertices[0].y = YF(h, y1);
    t->vertices[1].x = XF(w, x2);
    t->vertices[1].y = YF(h, y2);
    t->vertices[2].x = XF(w, x3);
    t->vertices[2].y = YF(h, y3);

    t->indices[0] = 0;
    t->indices[1] = 1;
    t->indices[2] = 2;

    t->stride = sizeof(Vertex);
    t->offset = 0U;
    t->index_count = 3U;

    desc.ByteWidth = sizeof(t->vertices);
    desc.Usage = D3D11_USAGE_DYNAMIC;
    desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    desc.MiscFlags = 0U;
    desc.StructureByteStride = 0U;

    sr_data.pSysMem = t->vertices;
    sr_data.SysMemPitch = 0U;
    sr_data.SysMemSlicePitch = 0U;

    res =ID3D11Device_CreateBuffer(d3d->d3d_device,
                                   &desc,
                                   &sr_data,
                                   &t->vertex_buffer);
    if (FAILED(res))
    {
        free(t);
        return NULL;
    }

    desc.ByteWidth = sizeof(t->indices);
    desc.Usage = D3D11_USAGE_DYNAMIC;
    desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
    desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    desc.MiscFlags = 0U;
    desc.StructureByteStride = 0U;

    sr_data.pSysMem = t->indices;
    sr_data.SysMemPitch = 0U;
    sr_data.SysMemSlicePitch = 0U;

    res =ID3D11Device_CreateBuffer(d3d->d3d_device,
                                   &desc,
                                   &sr_data,
                                   &t->index_buffer);
    if (FAILED(res))
    {
      ID3D11Buffer_Release(t->vertex_buffer);
        free(t);
        return NULL;
    }

    return t;
}

void triangle_free(Triangle *t)
{
    if (!t)
        return ;

    ID3D11Buffer_Release(t->index_buffer);
    ID3D11Buffer_Release(t->vertex_buffer);
    free(t);
}

void d3d_render(D3d *d3d)
{
#ifdef HAVE_WIN10
    DXGI_PRESENT_PARAMETERS pp;
    DXGI_SWAP_CHAIN_DESC1 desc;
#else
    DXGI_SWAP_CHAIN_DESC desc;
#endif
    const FLOAT color[4] = { 0.10f, 0.18f, 0.24f, 1.0f };
    HRESULT res;
    int w;
    int h;

    FCT;

#ifdef HAVE_WIN10
    res = IDXGISwapChain1_GetDesc1(d3d->dxgi_swapchain, &desc);
    if (FAILED(res))
        return;

    w = desc.Width;
    h = desc.Height;
#else
    res = IDXGISwapChain_GetDesc(d3d->dxgi_swapchain, &desc);
    if (FAILED(res))
        return;

    w = desc.BufferDesc.Width;
    h = desc.BufferDesc.Height;
#endif

    printf(" * swapchain size : %d %d\n", w, h);
    fflush(stdout);

    /* scene */
    Triangle *t;

    t = triangle_new(d3d,
                     w, h,
                     320, 120,
                     480, 360,
                     160, 360);

    /* clear render target */
    ID3D11DeviceContext_ClearRenderTargetView(d3d->d3d_device_ctx,
                                              d3d->d3d_render_target_view,
                                              color);
    /* Input Assembler (IA) stage */
    ID3D11DeviceContext_IASetPrimitiveTopology(d3d->d3d_device_ctx,
                                               D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
    ID3D11DeviceContext_IASetInputLayout(d3d->d3d_device_ctx,
                                         d3d->d3d_input_layout);
    ID3D11DeviceContext_IASetVertexBuffers(d3d->d3d_device_ctx,
                                           0,
                                           1,
                                           &t->vertex_buffer,
                                           &t->stride,
                                           &t->offset);
    ID3D11DeviceContext_IASetIndexBuffer(d3d->d3d_device_ctx,
                                         t->index_buffer,
                                         DXGI_FORMAT_R32_UINT,
                                         0);
    /* vertex shader stage */
    ID3D11DeviceContext_VSSetShader(d3d->d3d_device_ctx,
                                    d3d->d3d_vertex_shader,
                                    NULL,
                                    0);

    /* Rasterizer stage */
    ID3D11DeviceContext_RSSetState(d3d->d3d_device_ctx,
                                   d3d->d3d_rasterizer_state);
    ID3D11DeviceContext_RSSetViewports(d3d->d3d_device_ctx,
                                       1U, &d3d->viewport);
    /* pixel shader stage */
    ID3D11DeviceContext_PSSetShader(d3d->d3d_device_ctx,
                                    d3d->d3d_pixel_shader,
                                    NULL,
                                    0);

    /* Output Merger */
    ID3D11DeviceContext_OMSetRenderTargets(d3d->d3d_device_ctx,
                                           1U, &d3d->d3d_render_target_view,
                                           NULL);

    /* draw */
    ID3D11DeviceContext_DrawIndexed(d3d->d3d_device_ctx,
                                    t->index_count,
                                    0, 0);

    triangle_free(t);

    /*
     * present frame, that is flip the back buffer and the front buffer
     * if no vsync, we present immediatly
     */
#ifdef HAVE_WIN10
    pp.DirtyRectsCount = 0;
    pp.pDirtyRects = NULL;
    pp.pScrollRect = NULL;
    pp.pScrollOffset = NULL;
    res = IDXGISwapChain1_Present1(d3d->dxgi_swapchain,
                                   d3d->vsync ? 1 : 0, 0, &pp);
#else
    res = IDXGISwapChain_Present(d3d->dxgi_swapchain,
                                 d3d->vsync ? 1 : 0, 0);
#endif
    if (res == DXGI_ERROR_DEVICE_RESET || res == DXGI_ERROR_DEVICE_REMOVED)
    {
        printf("device removed or lost, need to recreate everything\n");
        fflush(stdout);
    }
    else if (res == DXGI_STATUS_OCCLUDED)
    {
        printf("window is not visible, so vsync won't work. Let's sleep a bit to reduce CPU usage\n");
        fflush(stdout);
    }
}

And the diff:

d3d_2.diff
--- d3d_1.c	2022-04-12 15:12:15.544656900 +0200
+++ d3d_2.c	2022-04-12 15:12:40.294910900 +0200
@@ -2,11 +2,11 @@
 /*
  * Windows 10:
 
- gcc -g -O2 -Wall -Wextra -o d3d d3d_1.c win_2.c -ld3d11 -ldxgi -luuid -D_WIN32_WINNT=0x0A00
+ gcc -g -O2 -Wall -Wextra -o d3d d3d_2.c win_2.c -ld3d11 -ld3dcompiler -ldxgi -luuid -D_WIN32_WINNT=0x0A00
 
  * Windows 7:
 
- gcc -g -O2 -Wall -Wextra -o d3d d3d_1.c win_2.c -ld3d11 -ldxgi -luuid -D_WIN32_WINNT=0x0601
+ gcc -g -O2 -Wall -Wextra -o d3d d3d_2.c win_2.c -ld3d11 -ld3dcompiler -ldxgi -luuid -D_WIN32_WINNT=0x0601
 
  */
 
@@ -25,6 +25,7 @@
 # include <dxgi.h>
 #endif
 #include <d3d11.h>
+#include <d3dcompiler.h>
 
 #include "d3d_1.h"
 
@@ -36,6 +37,9 @@
 do { } while (0)
 #endif
 
+#define XF(w,x) ((float)(2 * (x) - (w)) / (float)(w))
+#define YF(h,y) ((float)((h) - 2 * (y)) / (float)(h))
+
 struct D3d
 {
     /* DXGI */
@@ -58,6 +62,12 @@
     unsigned int vsync : 1;
 };
 
+typedef struct
+{
+    FLOAT x;
+    FLOAT y;
+} Vertex;
+
 /************************** D3D11 **************************/
 
 static void d3d_refresh_rate_get(D3d *d3d, UINT *num, UINT *den)
@@ -142,6 +152,10 @@
 
 D3d *d3d_init(Window *win, int vsync)
 {
+    D3D11_INPUT_ELEMENT_DESC desc_ie[] =
+    {
+        { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }
+    };
 #ifdef HAVE_WIN10
     DXGI_SWAP_CHAIN_DESC1 desc_sw;
     DXGI_SWAP_CHAIN_FULLSCREEN_DESC desc_fs;
@@ -156,6 +170,9 @@
     UINT num;
     UINT den;
     D3D_FEATURE_LEVEL feature_level[4];
+    ID3DBlob *vs_blob; /* vertex shader blob ptr */
+    ID3DBlob *ps_blob; /* pixel shader blob ptr */
+    ID3DBlob *err_blob; /* error blob ptr */
 
     d3d = (D3d *)calloc(1, sizeof(D3d));
     if (!d3d)
@@ -293,8 +310,93 @@
     if (FAILED(res))
         goto release_dxgi_swapchain;
 
+    /* Vertex shader */
+    flags = D3DCOMPILE_ENABLE_STRICTNESS;
+#ifdef _DEBUG
+    flags |= D3DCOMPILE_DEBUG;
+#endif
+    vs_blob = NULL;
+    res = D3DCompileFromFile(L"shader_1.hlsl",
+                             NULL,
+                             D3D_COMPILE_STANDARD_FILE_INCLUDE,
+                             "main_vs",
+                             "vs_5_0",
+                             flags,
+                             0U,
+                             &vs_blob,
+                             &err_blob );
+
+    if (FAILED(res))
+    {
+        printf(" * vs error : %s\n", (char *)ID3D10Blob_GetBufferPointer(err_blob));
+        goto release_d3D_rasterizer;
+    }
+
+    res = ID3D11Device_CreateVertexShader(d3d->d3d_device,
+                                          ID3D10Blob_GetBufferPointer(vs_blob),
+                                          ID3D10Blob_GetBufferSize(vs_blob),
+                                          NULL,
+                                          &d3d->d3d_vertex_shader);
+
+    if (FAILED(res))
+    {
+        printf(" * vs error : %s\n", (char *)ID3D10Blob_GetBufferPointer(err_blob));
+        ID3D10Blob_Release(vs_blob);
+        goto release_d3D_rasterizer;
+    }
+
+    /* create the input layout */
+    res = ID3D11Device_CreateInputLayout(d3d->d3d_device,
+                                         desc_ie,
+                                         sizeof(desc_ie) / sizeof(D3D11_INPUT_ELEMENT_DESC),
+                                         ID3D10Blob_GetBufferPointer(vs_blob),
+                                         ID3D10Blob_GetBufferSize(vs_blob),
+                                         &d3d->d3d_input_layout);
+    ID3D10Blob_Release(vs_blob);
+    if (FAILED(res))
+    {
+        printf(" * CreateInputLayout() failed\n");
+        goto release_vertex_shader;
+    }
+
+    /* Pixel shader */
+    ps_blob = NULL;
+    res = D3DCompileFromFile(L"shader_1.hlsl",
+                             NULL,
+                             D3D_COMPILE_STANDARD_FILE_INCLUDE,
+                             "main_ps",
+                             "ps_5_0",
+                             flags,
+                             0U,
+                             &ps_blob,
+                             &err_blob );
+
+    if (FAILED(res))
+    {
+        printf(" * ps blob error : %s\n", (char *)ID3D10Blob_GetBufferPointer(err_blob));
+        goto release_input_layout;
+    }
+
+    res = ID3D11Device_CreatePixelShader(d3d->d3d_device,
+                                         ID3D10Blob_GetBufferPointer(ps_blob),
+                                         ID3D10Blob_GetBufferSize(ps_blob),
+                                         NULL,
+                                         &d3d->d3d_pixel_shader);
+    ID3D10Blob_Release(ps_blob);
+    if (FAILED(res))
+    {
+        printf(" * CreatePixelShader() failed\n");
+        goto release_input_layout;
+    }
+
     return d3d;
 
+  release_input_layout:
+    ID3D11InputLayout_Release(d3d->d3d_input_layout);
+  release_vertex_shader:
+    ID3D11VertexShader_Release(d3d->d3d_vertex_shader);
+  release_d3D_rasterizer:
+    ID3D11RasterizerState_Release(d3d->d3d_rasterizer_state);
   release_dxgi_swapchain:
 #ifdef HAVE_WIN10
     IDXGISwapChain1_SetFullscreenState(d3d->dxgi_swapchain, FALSE, NULL);
@@ -333,6 +435,9 @@
                                      (void **)&d3d_debug);
 #endif
 
+    ID3D11PixelShader_Release(d3d->d3d_pixel_shader);
+    ID3D11InputLayout_Release(d3d->d3d_input_layout);
+    ID3D11VertexShader_Release(d3d->d3d_vertex_shader);
     ID3D11RasterizerState_Release(d3d->d3d_rasterizer_state);
     ID3D11RenderTargetView_Release(d3d->d3d_render_target_view);
 #ifdef HAVE_WIN10
@@ -442,32 +547,197 @@
     d3d->viewport.MaxDepth = 1.0f;
 }
 
+/*** triangle ***/
+
+typedef struct
+{
+    Vertex vertices[3];
+    unsigned int indices[3];
+    ID3D11Buffer* vertex_buffer;
+    ID3D11Buffer* index_buffer; /* not useful for a single triangle */
+    UINT stride;
+    UINT offset;
+    UINT count;
+    UINT index_count;
+} Triangle;
+
+Triangle *triangle_new(D3d *d3d,
+                       int w, int h,
+                       int x1, int y1,
+                       int x2, int y2,
+                       int x3, int y3)
+{
+    D3D11_BUFFER_DESC desc;
+    D3D11_SUBRESOURCE_DATA sr_data;
+    Triangle *t;
+    HRESULT res;
+
+    t = (Triangle *)malloc(sizeof(Triangle));
+    if (!t)
+        return NULL;
+
+    t->vertices[0].x = XF(w, x1);
+    t->vertices[0].y = YF(h, y1);
+    t->vertices[1].x = XF(w, x2);
+    t->vertices[1].y = YF(h, y2);
+    t->vertices[2].x = XF(w, x3);
+    t->vertices[2].y = YF(h, y3);
+
+    t->indices[0] = 0;
+    t->indices[1] = 1;
+    t->indices[2] = 2;
+
+    t->stride = sizeof(Vertex);
+    t->offset = 0U;
+    t->index_count = 3U;
+
+    desc.ByteWidth = sizeof(t->vertices);
+    desc.Usage = D3D11_USAGE_DYNAMIC;
+    desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
+    desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+    desc.MiscFlags = 0U;
+    desc.StructureByteStride = 0U;
+
+    sr_data.pSysMem = t->vertices;
+    sr_data.SysMemPitch = 0U;
+    sr_data.SysMemSlicePitch = 0U;
+
+    res =ID3D11Device_CreateBuffer(d3d->d3d_device,
+                                   &desc,
+                                   &sr_data,
+                                   &t->vertex_buffer);
+    if (FAILED(res))
+    {
+        free(t);
+        return NULL;
+    }
+
+    desc.ByteWidth = sizeof(t->indices);
+    desc.Usage = D3D11_USAGE_DYNAMIC;
+    desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
+    desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+    desc.MiscFlags = 0U;
+    desc.StructureByteStride = 0U;
+
+    sr_data.pSysMem = t->indices;
+    sr_data.SysMemPitch = 0U;
+    sr_data.SysMemSlicePitch = 0U;
+
+    res =ID3D11Device_CreateBuffer(d3d->d3d_device,
+                                   &desc,
+                                   &sr_data,
+                                   &t->index_buffer);
+    if (FAILED(res))
+    {
+      ID3D11Buffer_Release(t->vertex_buffer);
+        free(t);
+        return NULL;
+    }
+
+    return t;
+}
+
+void triangle_free(Triangle *t)
+{
+    if (!t)
+        return ;
+
+    ID3D11Buffer_Release(t->index_buffer);
+    ID3D11Buffer_Release(t->vertex_buffer);
+    free(t);
+}
+
 void d3d_render(D3d *d3d)
 {
 #ifdef HAVE_WIN10
     DXGI_PRESENT_PARAMETERS pp;
+    DXGI_SWAP_CHAIN_DESC1 desc;
+#else
+    DXGI_SWAP_CHAIN_DESC desc;
 #endif
     const FLOAT color[4] = { 0.10f, 0.18f, 0.24f, 1.0f };
     HRESULT res;
+    int w;
+    int h;
 
     FCT;
 
+#ifdef HAVE_WIN10
+    res = IDXGISwapChain1_GetDesc1(d3d->dxgi_swapchain, &desc);
+    if (FAILED(res))
+        return;
+
+    w = desc.Width;
+    h = desc.Height;
+#else
+    res = IDXGISwapChain_GetDesc(d3d->dxgi_swapchain, &desc);
+    if (FAILED(res))
+        return;
+
+    w = desc.BufferDesc.Width;
+    h = desc.BufferDesc.Height;
+#endif
+
+    printf(" * swapchain size : %d %d\n", w, h);
+    fflush(stdout);
+
+    /* scene */
+    Triangle *t;
+
+    t = triangle_new(d3d,
+                     w, h,
+                     320, 120,
+                     480, 360,
+                     160, 360);
+
     /* clear render target */
     ID3D11DeviceContext_ClearRenderTargetView(d3d->d3d_device_ctx,
                                               d3d->d3d_render_target_view,
                                               color);
+    /* Input Assembler (IA) stage */
+    ID3D11DeviceContext_IASetPrimitiveTopology(d3d->d3d_device_ctx,
+                                               D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
+    ID3D11DeviceContext_IASetInputLayout(d3d->d3d_device_ctx,
+                                         d3d->d3d_input_layout);
+    ID3D11DeviceContext_IASetVertexBuffers(d3d->d3d_device_ctx,
+                                           0,
+                                           1,
+                                           &t->vertex_buffer,
+                                           &t->stride,
+                                           &t->offset);
+    ID3D11DeviceContext_IASetIndexBuffer(d3d->d3d_device_ctx,
+                                         t->index_buffer,
+                                         DXGI_FORMAT_R32_UINT,
+                                         0);
+    /* vertex shader stage */
+    ID3D11DeviceContext_VSSetShader(d3d->d3d_device_ctx,
+                                    d3d->d3d_vertex_shader,
+                                    NULL,
+                                    0);
 
     /* Rasterizer Stage */
     ID3D11DeviceContext_RSSetState(d3d->d3d_device_ctx,
                                    d3d->d3d_rasterizer_state);
     ID3D11DeviceContext_RSSetViewports(d3d->d3d_device_ctx,
                                        1U, &d3d->viewport);
+    /* pixel shader stage */
+    ID3D11DeviceContext_PSSetShader(d3d->d3d_device_ctx,
+                                    d3d->d3d_pixel_shader,
+                                    NULL,
+                                    0);
 
     /* Output Merger stage */
     ID3D11DeviceContext_OMSetRenderTargets(d3d->d3d_device_ctx,
                                            1U, &d3d->d3d_render_target_view,
                                            NULL);
 
+    /* draw */
+    ID3D11DeviceContext_DrawIndexed(d3d->d3d_device_ctx,
+                                    t->index_count,
+                                    0, 0);
+
+    triangle_free(t);
+
     /*
      * present frame, that is flip the back buffer and the front buffer
      * if no vsync, we present immediatly

When the program is launched, you should obtain a window with a pink triangle, like this:

Scale the coordinates in the vertex shader

Two macros were added to scale from pixels coordinates to the normalized coordinates:

#define XF(w,x) ((float)(2 * (x) - (w)) / (float)(w))
#define YF(h,y) ((float)((h) - 2 * (y)) / (float)(h))

Note that the width and height are obviously needed for the computations. These computations can be done in the shader. But the size is still needed. The classic way to provide some values in a shader is a global variable. This global varial is stored in a constant buffer. Mathematically, only 1/w and 1/h are needed in the shader, so 2 floats. But to create a constant buffer, one need to pass a multiple of 16 bytes (according to the documentation of MSDN). So we use this 16 bits array to copy to the GPU into the constant buffer:

FLOAT ivps[4]; /* inverse viewport size */

and the 2 first elements are the inverse of the width and height. The 2 other elements are unused and set to 0. Once this array is filled, we copy it to the GPU with the Map() function. Here are the shader and C files, with their diff:

shader_2.hlsl
cbuffer cv_viewport : register(b0)
{
    float2 viewport_inv_size;
    float2 dummy;
}

struct vs_input
{
    uint2 position : POSITION;
};

struct ps_input
{
    float4 position : SV_POSITION;
};

ps_input main_vs(vs_input input )
{
    ps_input output;
    float2 p = input.position;
    p *= viewport_inv_size;
    p *= 2.0f;
    p -= 1.0f;
    p.y *= -1.0f;
    output.position = float4(p, 0.0f, 1.0f);
    return output;
}

float4 main_ps(ps_input input) : SV_TARGET
{
    return float4(1.0f, 0.0f, 1.0f, 1.0f);
}
shader_2.hlsl
--- shader_1.hlsl	2022-04-09 16:11:30.929767900 +0200
+++ shader_2.hlsl	2022-04-09 23:15:11.121662600 +0200
@@ -1,6 +1,12 @@
+cbuffer cv_viewport : register(b0)
+{
+    float2 viewport_inv_size;
+    float2 dummy;
+}
+
 struct vs_input
 {
-    float2 position : POSITION;
+    uint2 position : POSITION;
 };
 
 struct ps_input
@@ -11,7 +17,12 @@
 ps_input main_vs(vs_input input )
 {
     ps_input output;
-    output.position = float4(input.position, 0.0f, 1.0f);
+    float2 p = input.position;
+    p *= viewport_inv_size;
+    p *= 2.0f;
+    p -= 1.0f;
+    p.y *= -1.0f;
+    output.position = float4(p, 0.0f, 1.0f);
     return output;
 }
d3d_3.c

/*
 * Windows 10:

 gcc -g -O2 -Wall -Wextra -o d3d d3d_3.c win_2.c -ld3d11 -ld3dcompiler -ldxgi -luuid -D_WIN32_WINNT=0x0A00

 * Windows 7:

 gcc -g -O2 -Wall -Wextra -o d3d d3d_3.c win_2.c -ld3d11 -ld3dcompiler -ldxgi -luuid -D_WIN32_WINNT=0x0601

 */

#include <stdio.h>

#if defined _WIN32_WINNT && _WIN32_WINNT >= 0x0A00
# define HAVE_WIN10
#endif

/* C API for d3d11 */
#define COBJMACROS

#ifdef HAVE_WIN10
# include <dxgi1_3.h>
#else
# include <dxgi.h>
#endif
#include <d3d11.h>
#include <d3dcompiler.h>

#include "win.h"

#ifdef _DEBUG
# define FCT \
do { printf(" * %s\n", __FUNCTION__); fflush(stdout); } while (0)
#else
# define FCT \
do { } while (0)
#endif

#define XF(w,x) ((float)(2 * (x) - (w)) / (float)(w))
#define YF(h,y) ((float)((h) - 2 * (y)) / (float)(h))

struct D3d
{
    /* DXGI */
#ifdef HAVE_WIN10
    IDXGIFactory2 *dxgi_factory;
    IDXGISwapChain1 *dxgi_swapchain;
#else
    IDXGIFactory *dxgi_factory;
    IDXGISwapChain *dxgi_swapchain;
#endif
    /* D3D11 */
    ID3D11Device *d3d_device;
    ID3D11DeviceContext *d3d_device_ctx;
    ID3D11RenderTargetView *d3d_render_target_view;
    ID3D11InputLayout *d3d_input_layout;
    ID3D11VertexShader *d3d_vertex_shader;
    ID3D11Buffer *d3d_const_buffer;
    ID3D11RasterizerState *d3d_rasterizer_state;
    ID3D11PixelShader *d3d_pixel_shader;
    D3D11_VIEWPORT viewport;
    unsigned int vsync : 1;
};

typedef struct
{
    UINT x;
    UINT y;
} Vertex;

/************************** D3D11 **************************/

static void d3d_refresh_rate_get(D3d *d3d, UINT *num, UINT *den)
{
    DXGI_MODE_DESC *display_mode_list = NULL; /* 28 bytes */
    IDXGIAdapter *dxgi_adapter;
    IDXGIOutput *dxgi_output;
    UINT nbr_modes;
    UINT i;
    HRESULT res;

    *num = 0U;
    *den = 1U;

    if (!d3d->vsync)
        return;

    /* adapter of primary desktop : pass 0U */
    res = IDXGIFactory_EnumAdapters(d3d->dxgi_factory, 0U, &dxgi_adapter);
    if (FAILED(res))
        return;

    /* output of primary desktop : pass 0U */
    res = IDXGIAdapter_EnumOutputs(dxgi_adapter, 0U, &dxgi_output);
    if (FAILED(res))
        goto release_dxgi_adapter;

    /* number of mode that fit the format */
     res = IDXGIOutput_GetDisplayModeList(dxgi_output,
                                         DXGI_FORMAT_B8G8R8A8_UNORM,
                                         DXGI_ENUM_MODES_INTERLACED,
                                         &nbr_modes, NULL);
    if (FAILED(res))
        goto release_dxgi_output;

    printf("display mode list : %d\n", nbr_modes);
    fflush(stdout);
    display_mode_list = (DXGI_MODE_DESC *)malloc(nbr_modes * sizeof(DXGI_MODE_DESC));
    if (!display_mode_list)
        goto release_dxgi_output;

    /* fill the mode list */
    res = IDXGIOutput_GetDisplayModeList(dxgi_output,
                                         DXGI_FORMAT_B8G8R8A8_UNORM,
                                         DXGI_ENUM_MODES_INTERLACED,
                                         &nbr_modes, display_mode_list);
    if (FAILED(res))
        goto free_mode_list;

    for (i = 0; i < nbr_modes; i++)
    {
        if ((display_mode_list[i].Width == (UINT)GetSystemMetrics(SM_CXSCREEN)) &&
            (display_mode_list[i].Height == (UINT)GetSystemMetrics(SM_CYSCREEN)))
        {
            *num = display_mode_list[i].RefreshRate.Numerator;
            *den = display_mode_list[i].RefreshRate.Denominator;
            break;
        }
    }

#ifdef _DEBUG
    {
        DXGI_ADAPTER_DESC adapter_desc;

        IDXGIAdapter_GetDesc(dxgi_adapter, &adapter_desc);
        printf(" * video mem: %llu B, %llu MB\n",
               adapter_desc.DedicatedVideoMemory,
               adapter_desc.DedicatedVideoMemory / 1024 / 1024);
        fflush(stdout);
        wprintf(L" * description: %ls\n", adapter_desc.Description);
        fflush(stdout);
    }
#endif

  free_mode_list:
    free(display_mode_list);
  release_dxgi_output:
    IDXGIOutput_Release(dxgi_output);
  release_dxgi_adapter:
    IDXGIFactory_Release(dxgi_adapter);
}

D3d *d3d_init(Window *win, int vsync)
{
    D3D11_INPUT_ELEMENT_DESC desc_ie[] =
    {
        { "POSITION", 0, DXGI_FORMAT_R32G32_UINT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }
    };
#ifdef HAVE_WIN10
    DXGI_SWAP_CHAIN_DESC1 desc_sw;
    DXGI_SWAP_CHAIN_FULLSCREEN_DESC desc_fs;
#else
    DXGI_SWAP_CHAIN_DESC desc_sw;
#endif
    D3D11_BUFFER_DESC desc_buf;
    D3D11_SUBRESOURCE_DATA desc_data;
    D3D11_RASTERIZER_DESC desc_rs;
    D3d *d3d;
    RECT r;
    HRESULT res;
    UINT flags;
    UINT num;
    UINT den;
    FLOAT ivps[4]; /* inverse viewport size */
    D3D_FEATURE_LEVEL feature_level[4];
    ID3DBlob *vs_blob; /* vertex shader blob ptr */
    ID3DBlob *ps_blob; /* pixel shader blob ptr */
    ID3DBlob *err_blob; /* error blob ptr */

    d3d = (D3d *)calloc(1, sizeof(D3d));
    if (!d3d)
        return NULL;

    d3d->vsync = vsync;
    win->d3d = d3d;

    /* create the DXGI factory */
    flags = 0;
#ifdef HAVE_WIN10
# ifdef _DEBUG
    flags = DXGI_CREATE_FACTORY_DEBUG;
# endif
    res = CreateDXGIFactory2(flags, &IID_IDXGIFactory2, (void **)&d3d->dxgi_factory);
#else
    res = CreateDXGIFactory(&IID_IDXGIFactory, (void **)&d3d->dxgi_factory);
#endif
    if (FAILED(res))
        goto free_d3d;

    /* software engine functions are called from the main loop */
    flags = D3D11_CREATE_DEVICE_SINGLETHREADED |
            D3D11_CREATE_DEVICE_BGRA_SUPPORT;
#ifdef HAVE_WIN10
# ifdef _DEBUG
    flags |= D3D11_CREATE_DEVICE_DEBUG;
# endif
#endif

    feature_level[0] = D3D_FEATURE_LEVEL_11_1;
    feature_level[1] = D3D_FEATURE_LEVEL_11_0;
    feature_level[2] = D3D_FEATURE_LEVEL_10_1;
    feature_level[3] = D3D_FEATURE_LEVEL_10_0;

    /* create device and device context with hardware support */
    res = D3D11CreateDevice(NULL,
                            D3D_DRIVER_TYPE_HARDWARE,
                            NULL,
                            flags,
                            feature_level,
                            3U,
                            D3D11_SDK_VERSION,
                            &d3d->d3d_device,
                            NULL,
                            &d3d->d3d_device_ctx);
    if (FAILED(res))
        goto release_dxgi_factory2;

    if (!GetClientRect(win->win, &r))
        goto release_d3d_device;

    /*
     * create the swap chain. It needs some settings...
     * the size of the internal buffers
     * the image format
     * the number of back buffers (>= 2 for flip model, see SwapEffect field)
     * vsync enabled: need refresh rate
     *
     * Settings are different in win 7 and win10
     */

    d3d_refresh_rate_get(d3d, &num, &den);

#ifdef HAVE_WIN10
    desc_sw.Width = r.right - r.left;
    desc_sw.Height = r.bottom - r.top;
    desc_sw.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
    desc_sw.Stereo = FALSE;
#else
    desc_sw.BufferDesc.Width= r.right - r.left;
    desc_sw.BufferDesc.Height = r.bottom - r.top;
    desc_sw.BufferDesc.RefreshRate.Numerator = num;
    desc_sw.BufferDesc.RefreshRate.Denominator = den;
    desc_sw.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;;
    desc_sw.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
    desc_sw.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
#endif
    desc_sw.SampleDesc.Count = 1U;
    desc_sw.SampleDesc.Quality = 0U;
    desc_sw.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    desc_sw.BufferCount = 2U;
#ifdef HAVE_WIN10
    desc_sw.Scaling = DXGI_SCALING_NONE;
#else
    desc_sw.OutputWindow = win->win;
    desc_sw.Windowed = TRUE;
#endif
    desc_sw.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
#ifdef HAVE_WIN10
    desc_sw.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
#endif
    desc_sw.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;

#ifdef HAVE_WIN10
    desc_fs.RefreshRate.Numerator = num;
    desc_fs.RefreshRate.Denominator = den;
    desc_fs.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
    desc_fs.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
    desc_fs.Windowed = TRUE;
#endif

#ifdef HAVE_WIN10
    res = IDXGIFactory2_CreateSwapChainForHwnd(d3d->dxgi_factory,
                                               (IUnknown *)d3d->d3d_device,
                                               win->win,
                                               &desc_sw,
                                               &desc_fs,
                                               NULL,
                                               &d3d->dxgi_swapchain);
#else
    res = IDXGIFactory_CreateSwapChain(d3d->dxgi_factory,
                                       (IUnknown *)d3d->d3d_device,
                                       &desc_sw,
                                       &d3d->dxgi_swapchain);
#endif
    if (FAILED(res))
        goto release_d3d_device;

    /* rasterizer */
    desc_rs.FillMode = D3D11_FILL_SOLID;
    desc_rs.CullMode = D3D11_CULL_NONE;
    desc_rs.FrontCounterClockwise = FALSE;
    desc_rs.DepthBias = 0;
    desc_rs.DepthBiasClamp = 0.0f;
    desc_rs.SlopeScaledDepthBias = 0.0f;
    desc_rs.DepthClipEnable = TRUE;
    desc_rs.ScissorEnable = FALSE;
    desc_rs.MultisampleEnable = FALSE;
    desc_rs.AntialiasedLineEnable = FALSE;

    res = ID3D11Device_CreateRasterizerState(d3d->d3d_device,
                                             &desc_rs,
                                             &d3d->d3d_rasterizer_state);
    if (FAILED(res))
        goto release_dxgi_swapchain;

    /* Vertex shader */
    flags = D3DCOMPILE_ENABLE_STRICTNESS;
#ifdef _DEBUG
    flags |= D3DCOMPILE_DEBUG;
#endif
    vs_blob = NULL;
    res = D3DCompileFromFile(L"shader_2.hlsl",
                             NULL,
                             D3D_COMPILE_STANDARD_FILE_INCLUDE,
                             "main_vs",
                             "vs_5_0",
                             flags,
                             0U,
                             &vs_blob,
                             &err_blob );

    if (FAILED(res))
    {
        printf(" * vs error : %s\n", (char *)ID3D10Blob_GetBufferPointer(err_blob));
        goto release_d3D_rasterizer;
    }

    res = ID3D11Device_CreateVertexShader(d3d->d3d_device,
                                          ID3D10Blob_GetBufferPointer(vs_blob),
                                          ID3D10Blob_GetBufferSize(vs_blob),
                                          NULL,
                                          &d3d->d3d_vertex_shader);

    if (FAILED(res))
    {
        printf(" * vs error : %s\n", (char *)ID3D10Blob_GetBufferPointer(err_blob));
        ID3D10Blob_Release(vs_blob);
        goto release_d3D_rasterizer;
    }

    /* create the input layout */
    res = ID3D11Device_CreateInputLayout(d3d->d3d_device,
                                         desc_ie,
                                         sizeof(desc_ie) / sizeof(D3D11_INPUT_ELEMENT_DESC),
                                         ID3D10Blob_GetBufferPointer(vs_blob),
                                         ID3D10Blob_GetBufferSize(vs_blob),
                                         &d3d->d3d_input_layout);
    ID3D10Blob_Release(vs_blob);
    if (FAILED(res))
    {
        printf(" * CreateInputLayout() failed\n");
        goto release_vertex_shader;
    }

    /* Pixel shader */
    ps_blob = NULL;
    res = D3DCompileFromFile(L"shader_2.hlsl",
                             NULL,
                             D3D_COMPILE_STANDARD_FILE_INCLUDE,
                             "main_ps",
                             "ps_5_0",
                             flags,
                             0U,
                             &ps_blob,
                             &err_blob );

    if (FAILED(res))
    {
        printf(" * ps blob error : %s\n", (char *)ID3D10Blob_GetBufferPointer(err_blob));
        goto release_input_layout;
    }

    res = ID3D11Device_CreatePixelShader(d3d->d3d_device,
                                         ID3D10Blob_GetBufferPointer(ps_blob),
                                         ID3D10Blob_GetBufferSize(ps_blob),
                                         NULL,
                                         &d3d->d3d_pixel_shader);
    ID3D10Blob_Release(ps_blob);
    if (FAILED(res))
    {
        printf(" * CreatePixelShader() failed\n");
        goto release_input_layout;
    }

#ifdef HAVE_WIN10
    ivps[0] = 1.0f / desc_sw.Width;
    ivps[1] = 1.0f / desc_sw.Height;
#else
    ivps[0] = 1.0f / desc_sw.BufferDesc.Width;
    ivps[1] = 1.0f / desc_sw.BufferDesc.Height;
#endif
    ivps[2] = 0.0f;
    ivps[3] = 0.0f;

    desc_buf.ByteWidth = sizeof(ivps);
    desc_buf.Usage = D3D11_USAGE_DYNAMIC; /* because buffer is updated when the window has resized */
    desc_buf.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
    desc_buf.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    desc_buf.MiscFlags = 0;
    desc_buf.StructureByteStride = 0;

    desc_data.pSysMem = ivps;
    desc_data.SysMemPitch = 0;
    desc_data.SysMemSlicePitch = 0;

    res = ID3D11Device_CreateBuffer(d3d->d3d_device,
                                    &desc_buf,
                                    &desc_data,
                                    &d3d->d3d_const_buffer);
    if (FAILED(res))
    {
        printf(" * CreateBuffer() failed 0x%lx\n", res);
        goto release_pixel_shader;
    }

    return d3d;

  release_pixel_shader:
    ID3D11PixelShader_Release(d3d->d3d_pixel_shader);
  release_input_layout:
    ID3D11InputLayout_Release(d3d->d3d_input_layout);
  release_vertex_shader:
    ID3D11VertexShader_Release(d3d->d3d_vertex_shader);
  release_d3D_rasterizer:
    ID3D11RasterizerState_Release(d3d->d3d_rasterizer_state);
  release_dxgi_swapchain:
#ifdef HAVE_WIN10
    IDXGISwapChain1_SetFullscreenState(d3d->dxgi_swapchain, FALSE, NULL);
    IDXGISwapChain1_Release(d3d->dxgi_swapchain);
#else
    IDXGISwapChain_SetFullscreenState(d3d->dxgi_swapchain, FALSE, NULL);
    IDXGISwapChain_Release(d3d->dxgi_swapchain);
#endif
  release_d3d_device:
    ID3D11DeviceContext_Release(d3d->d3d_device_ctx);
    ID3D11Device_Release(d3d->d3d_device);
  release_dxgi_factory2:
#ifdef HAVE_WIN10
    IDXGIFactory2_Release(d3d->dxgi_factory);
#else
    IDXGIFactory_Release(d3d->dxgi_factory);
#endif
  free_d3d:
    free(d3d);

    return NULL;
}

void d3d_shutdown(D3d *d3d)
{
#ifdef _DEBUG
    ID3D11Debug *d3d_debug;
    HRESULT res;
#endif

    if (!d3d)
        return;

#ifdef _DEBUG
    res = ID3D11Debug_QueryInterface(d3d->d3d_device, &IID_ID3D11Debug,
                                     (void **)&d3d_debug);
#endif

    ID3D11Buffer_Release(d3d->d3d_const_buffer);
    ID3D11PixelShader_Release(d3d->d3d_pixel_shader);
    ID3D11InputLayout_Release(d3d->d3d_input_layout);
    ID3D11VertexShader_Release(d3d->d3d_vertex_shader);
    ID3D11RasterizerState_Release(d3d->d3d_rasterizer_state);
    ID3D11RenderTargetView_Release(d3d->d3d_render_target_view);
#ifdef HAVE_WIN10
    IDXGISwapChain1_SetFullscreenState(d3d->dxgi_swapchain, FALSE, NULL);
    IDXGISwapChain1_Release(d3d->dxgi_swapchain);
#else
    IDXGISwapChain_SetFullscreenState(d3d->dxgi_swapchain, FALSE, NULL);
    IDXGISwapChain_Release(d3d->dxgi_swapchain);
#endif
    ID3D11DeviceContext_Release(d3d->d3d_device_ctx);
    ID3D11Device_Release(d3d->d3d_device);
#ifdef HAVE_WIN10
    IDXGIFactory2_Release(d3d->dxgi_factory);
#else
    IDXGIFactory_Release(d3d->dxgi_factory);
#endif
    free(d3d);

#ifdef _DEBUG
    if (SUCCEEDED(res))
    {
        ID3D11Debug_ReportLiveDeviceObjects(d3d_debug, D3D11_RLDO_DETAIL);
        ID3D11Debug_Release(d3d_debug);
    }
#endif
}

void d3d_resize(D3d *d3d, UINT width, UINT height)
{
    D3D11_RENDER_TARGET_VIEW_DESC desc_rtv;
    D3D11_MAPPED_SUBRESOURCE mapped;
    ID3D11Texture2D *back_buffer;
    HRESULT res;

    FCT;

    res = ID3D11DeviceContext_Map(d3d->d3d_device_ctx,
                                  (ID3D11Resource *)d3d->d3d_const_buffer,
                                  0U, D3D11_MAP_WRITE_DISCARD, 0, &mapped);

    if (FAILED(res))
    {
        printf("Map() failed\n");
        fflush(stdout);
        return;
    }

    ((float *)mapped.pData)[0] = 1.0f / (float)width;
    ((float *)mapped.pData)[1] = 1.0f / (float)height;

    ID3D11DeviceContext_Unmap(d3d->d3d_device_ctx,
                              (ID3D11Resource *)d3d->d3d_const_buffer,
                              0U);

    /* unset the render target view in the output merger */
    ID3D11DeviceContext_OMSetRenderTargets(d3d->d3d_device_ctx,
                                           0U, NULL, NULL);

    /* release the render target view */
    if (d3d->d3d_render_target_view)
        ID3D11RenderTargetView_Release(d3d->d3d_render_target_view);

    /* resize the internal nuffers of the swapt chain to the new size */
#ifdef HAVE_WIN10
    res = IDXGISwapChain1_ResizeBuffers(d3d->dxgi_swapchain,
                                        0U, /* preserve buffer count */
                                        width, height,
                                        DXGI_FORMAT_UNKNOWN, /* preserve format */
                                        0U);
#else
    res = IDXGISwapChain_ResizeBuffers(d3d->dxgi_swapchain,
                                       0U, /* preserve buffer count */
                                       width, height,
                                       DXGI_FORMAT_UNKNOWN, /* preserve format */
                                       0U);
#endif
    if ((res == DXGI_ERROR_DEVICE_REMOVED) ||
        (res == DXGI_ERROR_DEVICE_RESET) ||
        (res == DXGI_ERROR_DRIVER_INTERNAL_ERROR))
    {
        return;
    }

    if (FAILED(res))
    {
        printf("ResizeBuffers() failed\n");
        fflush(stdout);
        return;
    }

    /* get the internal buffer of the swap chain */
#ifdef HAVE_WIN10
    res = IDXGISwapChain1_GetBuffer(d3d->dxgi_swapchain, 0,
                                    &IID_ID3D11Texture2D,
                                    (void **)&back_buffer);
#else
    res = IDXGISwapChain_GetBuffer(d3d->dxgi_swapchain, 0,
                                   &IID_ID3D11Texture2D,
                                   (void **)&back_buffer);
#endif
    if (FAILED(res))
    {
        printf("swapchain GetBuffer() failed\n");
        fflush(stdout);
        return;
    }

    ZeroMemory(&desc_rtv, sizeof(D3D11_RENDER_TARGET_VIEW_DESC));
    desc_rtv.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
    desc_rtv.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;

    /* create the new render target view from this internal buffer */
    res = ID3D11Device_CreateRenderTargetView(d3d->d3d_device,
                                              (ID3D11Resource *)back_buffer,
                                              &desc_rtv,
                                              &d3d->d3d_render_target_view);

    ID3D11Texture2D_Release(back_buffer);

    /* set viewport, depends on size of the window */
    d3d->viewport.TopLeftX = 0.0f;
    d3d->viewport.TopLeftY = 0.0f;
    d3d->viewport.Width = (float)width;
    d3d->viewport.Height = (float)height;
    d3d->viewport.MinDepth = 0.0f;
    d3d->viewport.MaxDepth = 1.0f;
}

/*** triangle ***/

typedef struct
{
    Vertex vertices[3];
    unsigned int indices[3];
    ID3D11Buffer* vertex_buffer;
    ID3D11Buffer* index_buffer; /* not useful for a single triangle */
    UINT stride;
    UINT offset;
    UINT count;
    UINT index_count;
} Triangle;

Triangle *triangle_new(D3d *d3d,
                       int x1, int y1,
                       int x2, int y2,
                       int x3, int y3)
{
    D3D11_BUFFER_DESC desc;
    D3D11_SUBRESOURCE_DATA sr_data;
    Triangle *t;
    HRESULT res;

    t = (Triangle *)malloc(sizeof(Triangle));
    if (!t)
        return NULL;

    t->vertices[0].x = x1;
    t->vertices[0].y = y1;
    t->vertices[1].x = x2;
    t->vertices[1].y = y2;
    t->vertices[2].x = x3;
    t->vertices[2].y = y3;

    t->indices[0] = 0;
    t->indices[1] = 1;
    t->indices[2] = 2;

    t->stride = sizeof(Vertex);
    t->offset = 0U;
    t->index_count = 3U;

    desc.ByteWidth = sizeof(t->vertices);
    desc.Usage = D3D11_USAGE_DYNAMIC;
    desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    desc.MiscFlags = 0U;
    desc.StructureByteStride = 0U;

    sr_data.pSysMem = t->vertices;
    sr_data.SysMemPitch = 0U;
    sr_data.SysMemSlicePitch = 0U;

    res =ID3D11Device_CreateBuffer(d3d->d3d_device,
                                   &desc,
                                   &sr_data,
                                   &t->vertex_buffer);
    if (FAILED(res))
    {
        free(t);
        return NULL;
    }

    desc.ByteWidth = sizeof(t->indices);
    desc.Usage = D3D11_USAGE_DYNAMIC;
    desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
    desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    desc.MiscFlags = 0U;
    desc.StructureByteStride = 0U;

    sr_data.pSysMem = t->indices;
    sr_data.SysMemPitch = 0U;
    sr_data.SysMemSlicePitch = 0U;

    res =ID3D11Device_CreateBuffer(d3d->d3d_device,
                                   &desc,
                                   &sr_data,
                                   &t->index_buffer);
    if (FAILED(res))
    {
        ID3D11Buffer_Release(t->vertex_buffer);
        free(t);
        return NULL;
    }

    return t;
}

void triangle_free(Triangle *t)
{
    if (!t)
        return ;

    ID3D11Buffer_Release(t->index_buffer);
    ID3D11Buffer_Release(t->vertex_buffer);
    free(t);
}

void d3d_render(D3d *d3d)
{
#ifdef HAVE_WIN10
    DXGI_PRESENT_PARAMETERS pp;
#endif
    const FLOAT color[4] = { 0.10f, 0.18f, 0.24f, 1.0f };
    HRESULT res;

    FCT;

    /* scene */
    Triangle *t;

    t = triangle_new(d3d,
                     320, 120,
                     480, 360,
                     160, 360);

    /* clear render target */
    ID3D11DeviceContext_ClearRenderTargetView(d3d->d3d_device_ctx,
                                              d3d->d3d_render_target_view,
                                              color);
    /* Input Assembler (IA) stage */
    ID3D11DeviceContext_IASetPrimitiveTopology(d3d->d3d_device_ctx,
                                               D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
    ID3D11DeviceContext_IASetInputLayout(d3d->d3d_device_ctx,
                                         d3d->d3d_input_layout);
    ID3D11DeviceContext_IASetVertexBuffers(d3d->d3d_device_ctx,
                                           0,
                                           1,
                                           &t->vertex_buffer,
                                           &t->stride,
                                           &t->offset);
    ID3D11DeviceContext_IASetIndexBuffer(d3d->d3d_device_ctx,
                                         t->index_buffer,
                                         DXGI_FORMAT_R32_UINT,
                                         0);
    /* vertex shader stage */
    ID3D11DeviceContext_VSSetShader(d3d->d3d_device_ctx,
                                    d3d->d3d_vertex_shader,
                                    NULL,
                                    0);
    ID3D11DeviceContext_VSSetConstantBuffers(d3d->d3d_device_ctx,
                                             0,
                                             1,
                                             &d3d->d3d_const_buffer);

    /* Rasterizer Stage */
    ID3D11DeviceContext_RSSetState(d3d->d3d_device_ctx,
                                   d3d->d3d_rasterizer_state);
    ID3D11DeviceContext_RSSetViewports(d3d->d3d_device_ctx,
                                       1U, &d3d->viewport);
    /* pixel shader stage */
    ID3D11DeviceContext_PSSetShader(d3d->d3d_device_ctx,
                                    d3d->d3d_pixel_shader,
                                    NULL,
                                    0);

    /* Output Merger stage */
    ID3D11DeviceContext_OMSetRenderTargets(d3d->d3d_device_ctx,
                                           1U, &d3d->d3d_render_target_view,
                                           NULL);

    /* draw */
    ID3D11DeviceContext_DrawIndexed(d3d->d3d_device_ctx,
                                    t->index_count,
                                    0, 0);

    triangle_free(t);

    /*
     * present frame, that is flip the back buffer and the front buffer
     * if no vsync, we present immediatly
     */
#ifdef HAVE_WIN10
    pp.DirtyRectsCount = 0;
    pp.pDirtyRects = NULL;
    pp.pScrollRect = NULL;
    pp.pScrollOffset = NULL;
    res = IDXGISwapChain1_Present1(d3d->dxgi_swapchain,
                                   d3d->vsync ? 1 : 0, 0, &pp);
#else
    res = IDXGISwapChain_Present(d3d->dxgi_swapchain,
                                 d3d->vsync ? 1 : 0, 0);
#endif
    if (res == DXGI_ERROR_DEVICE_RESET || res == DXGI_ERROR_DEVICE_REMOVED)
    {
        printf("device removed or lost, need to recreate everything\n");
        fflush(stdout);
    }
    else if (res == DXGI_STATUS_OCCLUDED)
    {
        printf("window is not visible, so vsync won't work. Let's sleep a bit to reduce CPU usage\n");
        fflush(stdout);
    }
}
d3d_3.diff
--- d3d_2.c	2022-04-13 14:14:35.216286300 +0200
+++ d3d_3.c	2022-04-13 14:52:21.128389200 +0200
@@ -2,11 +2,11 @@
 /*
  * Windows 10:
 
- gcc -g -O2 -Wall -Wextra -o d3d d3d_2.c win_2.c -ld3d11 -ld3dcompiler -ldxgi -luuid -D_WIN32_WINNT=0x0A00
+ gcc -g -O2 -Wall -Wextra -o d3d d3d_3.c win_2.c -ld3d11 -ld3dcompiler -ldxgi -luuid -D_WIN32_WINNT=0x0A00
 
  * Windows 7:
 
- gcc -g -O2 -Wall -Wextra -o d3d d3d_2.c win_2.c -ld3d11 -ld3dcompiler -ldxgi -luuid -D_WIN32_WINNT=0x0601
+ gcc -g -O2 -Wall -Wextra -o d3d d3d_3.c win_2.c -ld3d11 -ld3dcompiler -ldxgi -luuid -D_WIN32_WINNT=0x0601
 
  */
 
@@ -56,6 +56,7 @@
     ID3D11RenderTargetView *d3d_render_target_view;
     ID3D11InputLayout *d3d_input_layout;
     ID3D11VertexShader *d3d_vertex_shader;
+    ID3D11Buffer *d3d_const_buffer;
     ID3D11RasterizerState *d3d_rasterizer_state;
     ID3D11PixelShader *d3d_pixel_shader;
     D3D11_VIEWPORT viewport;
@@ -64,8 +65,8 @@
 
 typedef struct
 {
-    FLOAT x;
-    FLOAT y;
+    UINT x;
+    UINT y;
 } Vertex;
 
 /************************** D3D11 **************************/
@@ -154,7 +155,7 @@
 {
     D3D11_INPUT_ELEMENT_DESC desc_ie[] =
     {
-        { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }
+        { "POSITION", 0, DXGI_FORMAT_R32G32_UINT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }
     };
 #ifdef HAVE_WIN10
     DXGI_SWAP_CHAIN_DESC1 desc_sw;
@@ -162,6 +163,8 @@
 #else
     DXGI_SWAP_CHAIN_DESC desc_sw;
 #endif
+    D3D11_BUFFER_DESC desc_buf;
+    D3D11_SUBRESOURCE_DATA desc_data;
     D3D11_RASTERIZER_DESC desc_rs;
     D3d *d3d;
     RECT r;
@@ -169,6 +172,7 @@
     UINT flags;
     UINT num;
     UINT den;
+    FLOAT ivps[4]; /* inverse viewport size */
     D3D_FEATURE_LEVEL feature_level[4];
     ID3DBlob *vs_blob; /* vertex shader blob ptr */
     ID3DBlob *ps_blob; /* pixel shader blob ptr */
@@ -316,7 +320,7 @@
     flags |= D3DCOMPILE_DEBUG;
 #endif
     vs_blob = NULL;
-    res = D3DCompileFromFile(L"shader_1.hlsl",
+    res = D3DCompileFromFile(L"shader_2.hlsl",
                              NULL,
                              D3D_COMPILE_STANDARD_FILE_INCLUDE,
                              "main_vs",
@@ -361,7 +365,7 @@
 
     /* Pixel shader */
     ps_blob = NULL;
-    res = D3DCompileFromFile(L"shader_1.hlsl",
+    res = D3DCompileFromFile(L"shader_2.hlsl",
                              NULL,
                              D3D_COMPILE_STANDARD_FILE_INCLUDE,
                              "main_ps",
@@ -389,8 +393,41 @@
         goto release_input_layout;
     }
 
+#ifdef HAVE_WIN10
+    ivps[0] = 1.0f / desc_sw.Width;
+    ivps[1] = 1.0f / desc_sw.Height;
+#else
+    ivps[0] = 1.0f / desc_sw.BufferDesc.Width;
+    ivps[1] = 1.0f / desc_sw.BufferDesc.Height;
+#endif
+    ivps[2] = 0.0f;
+    ivps[3] = 0.0f;
+
+    desc_buf.ByteWidth = sizeof(ivps);
+    desc_buf.Usage = D3D11_USAGE_DYNAMIC; /* because buffer is updated when the window has resized */
+    desc_buf.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
+    desc_buf.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+    desc_buf.MiscFlags = 0;
+    desc_buf.StructureByteStride = 0;
+
+    desc_data.pSysMem = ivps;
+    desc_data.SysMemPitch = 0;
+    desc_data.SysMemSlicePitch = 0;
+
+    res = ID3D11Device_CreateBuffer(d3d->d3d_device,
+                                    &desc_buf,
+                                    &desc_data,
+                                    &d3d->d3d_const_buffer);
+    if (FAILED(res))
+    {
+        printf(" * CreateBuffer() failed 0x%lx\n", res);
+        goto release_pixel_shader;
+    }
+
     return d3d;
 
+  release_pixel_shader:
+    ID3D11PixelShader_Release(d3d->d3d_pixel_shader);
   release_input_layout:
     ID3D11InputLayout_Release(d3d->d3d_input_layout);
   release_vertex_shader:
@@ -435,6 +472,7 @@
                                      (void **)&d3d_debug);
 #endif
 
+    ID3D11Buffer_Release(d3d->d3d_const_buffer);
     ID3D11PixelShader_Release(d3d->d3d_pixel_shader);
     ID3D11InputLayout_Release(d3d->d3d_input_layout);
     ID3D11VertexShader_Release(d3d->d3d_vertex_shader);
@@ -468,11 +506,30 @@
 void d3d_resize(D3d *d3d, UINT width, UINT height)
 {
     D3D11_RENDER_TARGET_VIEW_DESC desc_rtv;
+    D3D11_MAPPED_SUBRESOURCE mapped;
     ID3D11Texture2D *back_buffer;
     HRESULT res;
 
     FCT;
 
+    res = ID3D11DeviceContext_Map(d3d->d3d_device_ctx,
+                                  (ID3D11Resource *)d3d->d3d_const_buffer,
+                                  0U, D3D11_MAP_WRITE_DISCARD, 0, &mapped);
+
+    if (FAILED(res))
+    {
+        printf("Map() failed\n");
+        fflush(stdout);
+        return;
+    }
+
+    ((float *)mapped.pData)[0] = 1.0f / (float)width;
+    ((float *)mapped.pData)[1] = 1.0f / (float)height;
+
+    ID3D11DeviceContext_Unmap(d3d->d3d_device_ctx,
+                              (ID3D11Resource *)d3d->d3d_const_buffer,
+                              0U);
+
     /* unset the render target view in the output merger */
     ID3D11DeviceContext_OMSetRenderTargets(d3d->d3d_device_ctx,
                                            0U, NULL, NULL);
@@ -562,7 +619,6 @@
 } Triangle;
 
 Triangle *triangle_new(D3d *d3d,
-                       int w, int h,
                        int x1, int y1,
                        int x2, int y2,
                        int x3, int y3)
@@ -576,12 +632,12 @@
     if (!t)
         return NULL;
 
-    t->vertices[0].x = XF(w, x1);
-    t->vertices[0].y = YF(h, y1);
-    t->vertices[1].x = XF(w, x2);
-    t->vertices[1].y = YF(h, y2);
-    t->vertices[2].x = XF(w, x3);
-    t->vertices[2].y = YF(h, y3);
+    t->vertices[0].x = x1;
+    t->vertices[0].y = y1;
+    t->vertices[1].x = x2;
+    t->vertices[1].y = y2;
+    t->vertices[2].x = x3;
+    t->vertices[2].y = y3;
 
     t->indices[0] = 0;
     t->indices[1] = 1;
@@ -651,41 +707,16 @@
 {
 #ifdef HAVE_WIN10
     DXGI_PRESENT_PARAMETERS pp;
-    DXGI_SWAP_CHAIN_DESC1 desc;
-#else
-    DXGI_SWAP_CHAIN_DESC desc;
 #endif
     const FLOAT color[4] = { 0.10f, 0.18f, 0.24f, 1.0f };
     HRESULT res;
-    int w;
-    int h;
 
     FCT;
 
-#ifdef HAVE_WIN10
-    res = IDXGISwapChain1_GetDesc1(d3d->dxgi_swapchain, &desc);
-    if (FAILED(res))
-        return;
-
-    w = desc.Width;
-    h = desc.Height;
-#else
-    res = IDXGISwapChain_GetDesc(d3d->dxgi_swapchain, &desc);
-    if (FAILED(res))
-        return;
-
-    w = desc.BufferDesc.Width;
-    h = desc.BufferDesc.Height;
-#endif
-
-    printf(" * swapchain size : %d %d\n", w, h);
-    fflush(stdout);
-
     /* scene */
     Triangle *t;
 
     t = triangle_new(d3d,
-                     w, h,
                      320, 120,
                      480, 360,
                      160, 360);
@@ -714,6 +745,10 @@
                                     d3d->d3d_vertex_shader,
                                     NULL,
                                     0);
+    ID3D11DeviceContext_VSSetConstantBuffers(d3d->d3d_device_ctx,
+                                             0,
+                                             1,
+                                             &d3d->d3d_const_buffer);
 
     /* Rasterizer Stage */
     ID3D11DeviceContext_RSSetState(d3d->d3d_device_ctx,

Changing the color of the triangle

As we have seen, the color of the triangle is hard coded in the pixel shader. It is possible to pass the color we want to the pixel shader. We need to modify the C code and the shader code. Here are the steps for the C code:

  • Add the four components of the BGRA color in the Vertex structure:
typedef struct
{
    UINT x;
    UINT y;
    BYTE r;
    BYTE g;
    BYTE b;
    BYTE a;
} Vertex;
  • Add an element in the D3D11_INPUT_ELEMENT_DESC array, namely desc_ie. This element describes how the Vertex structure is laying in memory.
D3D11_INPUT_ELEMENT_DESC desc_ie[] =
{
    { "POSITION", 0, DXGI_FORMAT_R32G32_UINT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
    { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 2 * sizeof(UINT), D3D11_INPUT_PER_VERTEX_DATA, 0 }
};

which means that the first 8 bytes (R32G32) are 2 variables of type UINT (which is coherent) and the following bytes (see the 2 * sizeof(UINT)) are 4 bytes (R8G8B8A8), which represent the color components.

  • Add the color components to triangle_new() and fill the vertex accordingly.
  • Call triangle_new with the chosen color.

On the shader side:

  • Add 4 floats in the vs_input and ps_input structures
  • Fill output color member with the input color member.
  • Return the color member of the input of the pixel shader main() function.

Here is the new shader code and the diff:

shader_3.hlsl
cbuffer cv_viewport : register(b0)
{
    float2 viewport_inv_size;
    float2 dummy;
}

struct vs_input
{
    uint2 position : POSITION;
    float4 color : COLOR;
};

struct ps_input
{
    float4 position : SV_POSITION;
    float4 color : COLOR;
};

ps_input main_vs(vs_input input )
{
    ps_input output;
    float2 p = input.position;
    p *= viewport_inv_size;
    p *= 2.0f;
    p -= 1.0f;
    p.y *= -1.0f;
    output.position = float4(p, 0.0f, 1.0f);
    output.color = input.color;
    return output;
}

float4 main_ps(ps_input input) : SV_TARGET
{
    return input.color;
}
shader_3.diff
--- shader_2.hlsl	2022-04-09 23:15:11.121662600 +0200
+++ shader_3.hlsl	2022-04-13 17:08:20.802513200 +0200
@@ -7,11 +7,13 @@
 struct vs_input
 {
     uint2 position : POSITION;
+    float4 color : COLOR;
 };
 
 struct ps_input
 {
     float4 position : SV_POSITION;
+    float4 color : COLOR;
 };
 
 ps_input main_vs(vs_input input )
@@ -23,10 +25,11 @@
     p -= 1.0f;
     p.y *= -1.0f;
     output.position = float4(p, 0.0f, 1.0f);
+    output.color = input.color;
     return output;
 }
 
 float4 main_ps(ps_input input) : SV_TARGET
 {
-    return float4(1.0f, 0.0f, 1.0f, 1.0f);
+    return input.color;
 }

And the C source which displays a yellow triangle, followed bythe diff:

d3d_4.c

/*
 * Windows 10:

 gcc -g -O2 -Wall -Wextra -o d3d d3d_4.c win_2.c -ld3d11 -ld3dcompiler -ldxgi -luuid -D_WIN32_WINNT=0x0A00

 * Windows 7:

 gcc -g -O2 -Wall -Wextra -o d3d d3d_4.c win_2.c -ld3d11 -ld3dcompiler -ldxgi -luuid -D_WIN32_WINNT=0x0601

 */

#include <stdio.h>

#if defined _WIN32_WINNT && _WIN32_WINNT >= 0x0A00
# define HAVE_WIN10
#endif

/* C API for d3d11 */
#define COBJMACROS

#ifdef HAVE_WIN10
# include <dxgi1_3.h>
#else
# include <dxgi.h>
#endif
#include <d3d11.h>
#include <d3dcompiler.h>

#include "win.h"

#ifdef _DEBUG
# define FCT \
do { printf(" * %s\n", __FUNCTION__); fflush(stdout); } while (0)
#else
# define FCT \
do { } while (0)
#endif

#define XF(w,x) ((float)(2 * (x) - (w)) / (float)(w))
#define YF(h,y) ((float)((h) - 2 * (y)) / (float)(h))

struct D3d
{
    /* DXGI */
#ifdef HAVE_WIN10
    IDXGIFactory2 *dxgi_factory;
    IDXGISwapChain1 *dxgi_swapchain;
#else
    IDXGIFactory *dxgi_factory;
    IDXGISwapChain *dxgi_swapchain;
#endif
    /* D3D11 */
    ID3D11Device *d3d_device;
    ID3D11DeviceContext *d3d_device_ctx;
    ID3D11RenderTargetView *d3d_render_target_view;
    ID3D11InputLayout *d3d_input_layout;
    ID3D11VertexShader *d3d_vertex_shader;
    ID3D11Buffer *d3d_const_buffer;
    ID3D11RasterizerState *d3d_rasterizer_state;
    ID3D11PixelShader *d3d_pixel_shader;
    D3D11_VIEWPORT viewport;
    unsigned int vsync : 1;
};

typedef struct
{
    UINT x;
    UINT y;
    BYTE r;
    BYTE g;
    BYTE b;
    BYTE a;
} Vertex;

/************************** D3D11 **************************/

static void d3d_refresh_rate_get(D3d *d3d, UINT *num, UINT *den)
{
    DXGI_MODE_DESC *display_mode_list = NULL; /* 28 bytes */
    IDXGIAdapter *dxgi_adapter;
    IDXGIOutput *dxgi_output;
    UINT nbr_modes;
    UINT i;
    HRESULT res;

    *num = 0U;
    *den = 1U;

    if (!d3d->vsync)
        return;

    /* adapter of primary desktop : pass 0U */
    res = IDXGIFactory_EnumAdapters(d3d->dxgi_factory, 0U, &dxgi_adapter);
    if (FAILED(res))
        return;

    /* output of primary desktop : pass 0U */
    res = IDXGIAdapter_EnumOutputs(dxgi_adapter, 0U, &dxgi_output);
    if (FAILED(res))
        goto release_dxgi_adapter;

    /* number of mode that fit the format */
     res = IDXGIOutput_GetDisplayModeList(dxgi_output,
                                         DXGI_FORMAT_B8G8R8A8_UNORM,
                                         DXGI_ENUM_MODES_INTERLACED,
                                         &nbr_modes, NULL);
    if (FAILED(res))
        goto release_dxgi_output;

    printf("display mode list : %d\n", nbr_modes);
    fflush(stdout);
    display_mode_list = (DXGI_MODE_DESC *)malloc(nbr_modes * sizeof(DXGI_MODE_DESC));
    if (!display_mode_list)
        goto release_dxgi_output;

    /* fill the mode list */
    res = IDXGIOutput_GetDisplayModeList(dxgi_output,
                                         DXGI_FORMAT_B8G8R8A8_UNORM,
                                         DXGI_ENUM_MODES_INTERLACED,
                                         &nbr_modes, display_mode_list);
    if (FAILED(res))
        goto free_mode_list;

    for (i = 0; i < nbr_modes; i++)
    {
        if ((display_mode_list[i].Width == (UINT)GetSystemMetrics(SM_CXSCREEN)) &&
            (display_mode_list[i].Height == (UINT)GetSystemMetrics(SM_CYSCREEN)))
        {
            *num = display_mode_list[i].RefreshRate.Numerator;
            *den = display_mode_list[i].RefreshRate.Denominator;
            break;
        }
    }

#ifdef _DEBUG
    {
        DXGI_ADAPTER_DESC adapter_desc;

        IDXGIAdapter_GetDesc(dxgi_adapter, &adapter_desc);
        printf(" * video mem: %llu B, %llu MB\n",
               adapter_desc.DedicatedVideoMemory,
               adapter_desc.DedicatedVideoMemory / 1024 / 1024);
        fflush(stdout);
        wprintf(L" * description: %ls\n", adapter_desc.Description);
        fflush(stdout);
    }
#endif

  free_mode_list:
    free(display_mode_list);
  release_dxgi_output:
    IDXGIOutput_Release(dxgi_output);
  release_dxgi_adapter:
    IDXGIFactory_Release(dxgi_adapter);
}

D3d *d3d_init(Window *win, int vsync)
{
    D3D11_INPUT_ELEMENT_DESC desc_ie[] =
    {
        { "POSITION", 0, DXGI_FORMAT_R32G32_UINT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
        { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 2 * sizeof(UINT), D3D11_INPUT_PER_VERTEX_DATA, 0 }
    };
#ifdef HAVE_WIN10
    DXGI_SWAP_CHAIN_DESC1 desc_sw;
    DXGI_SWAP_CHAIN_FULLSCREEN_DESC desc_fs;
#else
    DXGI_SWAP_CHAIN_DESC desc_sw;
#endif
    D3D11_BUFFER_DESC desc_buf;
    D3D11_SUBRESOURCE_DATA desc_data;
    D3D11_RASTERIZER_DESC desc_rs;
    D3d *d3d;
    RECT r;
    HRESULT res;
    UINT flags;
    UINT num;
    UINT den;
    FLOAT ivps[4]; /* inverse viewport size */
    D3D_FEATURE_LEVEL feature_level[4];
    ID3DBlob *vs_blob; /* vertex shader blob ptr */
    ID3DBlob *ps_blob; /* pixel shader blob ptr */
    ID3DBlob *err_blob; /* error blob ptr */

    d3d = (D3d *)calloc(1, sizeof(D3d));
    if (!d3d)
        return NULL;

    d3d->vsync = vsync;
    win->d3d = d3d;

    /* create the DXGI factory */
    flags = 0;
#ifdef HAVE_WIN10
# ifdef _DEBUG
    flags = DXGI_CREATE_FACTORY_DEBUG;
# endif
    res = CreateDXGIFactory2(flags, &IID_IDXGIFactory2, (void **)&d3d->dxgi_factory);
#else
    res = CreateDXGIFactory(&IID_IDXGIFactory, (void **)&d3d->dxgi_factory);
#endif
    if (FAILED(res))
        goto free_d3d;

    /* software engine functions are called from the main loop */
    flags = D3D11_CREATE_DEVICE_SINGLETHREADED |
            D3D11_CREATE_DEVICE_BGRA_SUPPORT;
#ifdef HAVE_WIN10
# ifdef _DEBUG
    flags |= D3D11_CREATE_DEVICE_DEBUG;
# endif
#endif

    feature_level[0] = D3D_FEATURE_LEVEL_11_1;
    feature_level[1] = D3D_FEATURE_LEVEL_11_0;
    feature_level[2] = D3D_FEATURE_LEVEL_10_1;
    feature_level[3] = D3D_FEATURE_LEVEL_10_0;

    /* create device and device context with hardware support */
    res = D3D11CreateDevice(NULL,
                            D3D_DRIVER_TYPE_HARDWARE,
                            NULL,
                            flags,
                            feature_level,
                            3U,
                            D3D11_SDK_VERSION,
                            &d3d->d3d_device,
                            NULL,
                            &d3d->d3d_device_ctx);
    if (FAILED(res))
        goto release_dxgi_factory2;

    if (!GetClientRect(win->win, &r))
        goto release_d3d_device;

    /*
     * create the swap chain. It needs some settings...
     * the size of the internal buffers
     * the image format
     * the number of back buffers (>= 2 for flip model, see SwapEffect field)
     * vsync enabled: need refresh rate
     *
     * Settings are different in win 7 and win10
     */

    d3d_refresh_rate_get(d3d, &num, &den);

#ifdef HAVE_WIN10
    desc_sw.Width = r.right - r.left;
    desc_sw.Height = r.bottom - r.top;
    desc_sw.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
    desc_sw.Stereo = FALSE;
#else
    desc_sw.BufferDesc.Width= r.right - r.left;
    desc_sw.BufferDesc.Height = r.bottom - r.top;
    desc_sw.BufferDesc.RefreshRate.Numerator = num;
    desc_sw.BufferDesc.RefreshRate.Denominator = den;
    desc_sw.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;;
    desc_sw.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
    desc_sw.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
#endif
    desc_sw.SampleDesc.Count = 1U;
    desc_sw.SampleDesc.Quality = 0U;
    desc_sw.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    desc_sw.BufferCount = 2U;
#ifdef HAVE_WIN10
    desc_sw.Scaling = DXGI_SCALING_NONE;
#else
    desc_sw.OutputWindow = win->win;
    desc_sw.Windowed = TRUE;
#endif
    desc_sw.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
#ifdef HAVE_WIN10
    desc_sw.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
#endif
    desc_sw.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;

#ifdef HAVE_WIN10
    desc_fs.RefreshRate.Numerator = num;
    desc_fs.RefreshRate.Denominator = den;
    desc_fs.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
    desc_fs.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
    desc_fs.Windowed = TRUE;
#endif

#ifdef HAVE_WIN10
    res = IDXGIFactory2_CreateSwapChainForHwnd(d3d->dxgi_factory,
                                               (IUnknown *)d3d->d3d_device,
                                               win->win,
                                               &desc_sw,
                                               &desc_fs,
                                               NULL,
                                               &d3d->dxgi_swapchain);
#else
    res = IDXGIFactory_CreateSwapChain(d3d->dxgi_factory,
                                       (IUnknown *)d3d->d3d_device,
                                       &desc_sw,
                                       &d3d->dxgi_swapchain);
#endif
    if (FAILED(res))
        goto release_d3d_device;

    /* rasterizer */
    desc_rs.FillMode = D3D11_FILL_SOLID;
    desc_rs.CullMode = D3D11_CULL_NONE;
    desc_rs.FrontCounterClockwise = FALSE;
    desc_rs.DepthBias = 0;
    desc_rs.DepthBiasClamp = 0.0f;
    desc_rs.SlopeScaledDepthBias = 0.0f;
    desc_rs.DepthClipEnable = TRUE;
    desc_rs.ScissorEnable = FALSE;
    desc_rs.MultisampleEnable = FALSE;
    desc_rs.AntialiasedLineEnable = FALSE;

    res = ID3D11Device_CreateRasterizerState(d3d->d3d_device,
                                             &desc_rs,
                                             &d3d->d3d_rasterizer_state);
    if (FAILED(res))
        goto release_dxgi_swapchain;

    /* Vertex shader */
    flags = D3DCOMPILE_ENABLE_STRICTNESS;
#ifdef _DEBUG
    flags |= D3DCOMPILE_DEBUG;
#endif
    vs_blob = NULL;
    res = D3DCompileFromFile(L"shader_3.hlsl",
                             NULL,
                             D3D_COMPILE_STANDARD_FILE_INCLUDE,
                             "main_vs",
                             "vs_5_0",
                             flags,
                             0U,
                             &vs_blob,
                             &err_blob );

    if (FAILED(res))
    {
        printf(" * vs error : %s\n", (char *)ID3D10Blob_GetBufferPointer(err_blob));
        goto release_d3D_rasterizer;
    }

    res = ID3D11Device_CreateVertexShader(d3d->d3d_device,
                                          ID3D10Blob_GetBufferPointer(vs_blob),
                                          ID3D10Blob_GetBufferSize(vs_blob),
                                          NULL,
                                          &d3d->d3d_vertex_shader);

    if (FAILED(res))
    {
        printf(" * vs error : %s\n", (char *)ID3D10Blob_GetBufferPointer(err_blob));
        ID3D10Blob_Release(vs_blob);
        goto release_d3D_rasterizer;
    }

    /* create the input layout */
    res = ID3D11Device_CreateInputLayout(d3d->d3d_device,
                                         desc_ie,
                                         sizeof(desc_ie) / sizeof(D3D11_INPUT_ELEMENT_DESC),
                                         ID3D10Blob_GetBufferPointer(vs_blob),
                                         ID3D10Blob_GetBufferSize(vs_blob),
                                         &d3d->d3d_input_layout);
    ID3D10Blob_Release(vs_blob);
    if (FAILED(res))
    {
        printf(" * CreateInputLayout() failed\n");
        goto release_vertex_shader;
    }

    /* Pixel shader */
    ps_blob = NULL;
    res = D3DCompileFromFile(L"shader_3.hlsl",
                             NULL,
                             D3D_COMPILE_STANDARD_FILE_INCLUDE,
                             "main_ps",
                             "ps_5_0",
                             flags,
                             0U,
                             &ps_blob,
                             &err_blob );

    if (FAILED(res))
    {
        printf(" * ps blob error : %s\n", (char *)ID3D10Blob_GetBufferPointer(err_blob));
        goto release_input_layout;
    }

    res = ID3D11Device_CreatePixelShader(d3d->d3d_device,
                                         ID3D10Blob_GetBufferPointer(ps_blob),
                                         ID3D10Blob_GetBufferSize(ps_blob),
                                         NULL,
                                         &d3d->d3d_pixel_shader);
    ID3D10Blob_Release(ps_blob);
    if (FAILED(res))
    {
        printf(" * CreatePixelShader() failed\n");
        goto release_input_layout;
    }

#ifdef HAVE_WIN10
    ivps[0] = 1.0f / desc_sw.Width;
    ivps[1] = 1.0f / desc_sw.Height;
#else
    ivps[0] = 1.0f / desc_sw.BufferDesc.Width;
    ivps[1] = 1.0f / desc_sw.BufferDesc.Height;
#endif
    ivps[2] = 0.0f;
    ivps[3] = 0.0f;

    desc_buf.ByteWidth = sizeof(ivps);
    desc_buf.Usage = D3D11_USAGE_DYNAMIC; /* because buffer is updated when the window has resized */
    desc_buf.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
    desc_buf.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    desc_buf.MiscFlags = 0;
    desc_buf.StructureByteStride = 0;

    desc_data.pSysMem = ivps;
    desc_data.SysMemPitch = 0;
    desc_data.SysMemSlicePitch = 0;

    res = ID3D11Device_CreateBuffer(d3d->d3d_device,
                                    &desc_buf,
                                    &desc_data,
                                    &d3d->d3d_const_buffer);
    if (FAILED(res))
    {
        printf(" * CreateBuffer() failed 0x%lx\n", res);
        goto release_pixel_shader;
    }

    return d3d;

  release_pixel_shader:
    ID3D11PixelShader_Release(d3d->d3d_pixel_shader);
  release_input_layout:
    ID3D11InputLayout_Release(d3d->d3d_input_layout);
  release_vertex_shader:
    ID3D11VertexShader_Release(d3d->d3d_vertex_shader);
  release_d3D_rasterizer:
    ID3D11RasterizerState_Release(d3d->d3d_rasterizer_state);
  release_dxgi_swapchain:
#ifdef HAVE_WIN10
    IDXGISwapChain1_SetFullscreenState(d3d->dxgi_swapchain, FALSE, NULL);
    IDXGISwapChain1_Release(d3d->dxgi_swapchain);
#else
    IDXGISwapChain_SetFullscreenState(d3d->dxgi_swapchain, FALSE, NULL);
    IDXGISwapChain_Release(d3d->dxgi_swapchain);
#endif
  release_d3d_device:
    ID3D11DeviceContext_Release(d3d->d3d_device_ctx);
    ID3D11Device_Release(d3d->d3d_device);
  release_dxgi_factory2:
#ifdef HAVE_WIN10
    IDXGIFactory2_Release(d3d->dxgi_factory);
#else
    IDXGIFactory_Release(d3d->dxgi_factory);
#endif
  free_d3d:
    free(d3d);

    return NULL;
}

void d3d_shutdown(D3d *d3d)
{
#ifdef _DEBUG
    ID3D11Debug *d3d_debug;
    HRESULT res;
#endif

    if (!d3d)
        return;

#ifdef _DEBUG
    res = ID3D11Debug_QueryInterface(d3d->d3d_device, &IID_ID3D11Debug,
                                     (void **)&d3d_debug);
#endif

    ID3D11Buffer_Release(d3d->d3d_const_buffer);
    ID3D11PixelShader_Release(d3d->d3d_pixel_shader);
    ID3D11InputLayout_Release(d3d->d3d_input_layout);
    ID3D11VertexShader_Release(d3d->d3d_vertex_shader);
    ID3D11RasterizerState_Release(d3d->d3d_rasterizer_state);
    ID3D11RenderTargetView_Release(d3d->d3d_render_target_view);
#ifdef HAVE_WIN10
    IDXGISwapChain1_SetFullscreenState(d3d->dxgi_swapchain, FALSE, NULL);
    IDXGISwapChain1_Release(d3d->dxgi_swapchain);
#else
    IDXGISwapChain_SetFullscreenState(d3d->dxgi_swapchain, FALSE, NULL);
    IDXGISwapChain_Release(d3d->dxgi_swapchain);
#endif
    ID3D11DeviceContext_Release(d3d->d3d_device_ctx);
    ID3D11Device_Release(d3d->d3d_device);
#ifdef HAVE_WIN10
    IDXGIFactory2_Release(d3d->dxgi_factory);
#else
    IDXGIFactory_Release(d3d->dxgi_factory);
#endif
    free(d3d);

#ifdef _DEBUG
    if (SUCCEEDED(res))
    {
        ID3D11Debug_ReportLiveDeviceObjects(d3d_debug, D3D11_RLDO_DETAIL);
        ID3D11Debug_Release(d3d_debug);
    }
#endif
}

void d3d_resize(D3d *d3d, UINT width, UINT height)
{
    D3D11_RENDER_TARGET_VIEW_DESC desc_rtv;
    D3D11_MAPPED_SUBRESOURCE mapped;
    ID3D11Texture2D *back_buffer;
    HRESULT res;

    FCT;

    res = ID3D11DeviceContext_Map(d3d->d3d_device_ctx,
                                  (ID3D11Resource *)d3d->d3d_const_buffer,
                                  0U, D3D11_MAP_WRITE_DISCARD, 0, &mapped);

    if (FAILED(res))
    {
        printf("Map() failed\n");
        fflush(stdout);
        return;
    }

    ((float *)mapped.pData)[0] = 1.0f / (float)width;
    ((float *)mapped.pData)[1] = 1.0f / (float)height;

    ID3D11DeviceContext_Unmap(d3d->d3d_device_ctx,
                              (ID3D11Resource *)d3d->d3d_const_buffer,
                              0U);

    /* unset the render target view in the output merger */
    ID3D11DeviceContext_OMSetRenderTargets(d3d->d3d_device_ctx,
                                           0U, NULL, NULL);

    /* release the render target view */
    if (d3d->d3d_render_target_view)
        ID3D11RenderTargetView_Release(d3d->d3d_render_target_view);

    /* resize the internal nuffers of the swapt chain to the new size */
#ifdef HAVE_WIN10
    res = IDXGISwapChain1_ResizeBuffers(d3d->dxgi_swapchain,
                                        0U, /* preserve buffer count */
                                        width, height,
                                        DXGI_FORMAT_UNKNOWN, /* preserve format */
                                        0U);
#else
    res = IDXGISwapChain_ResizeBuffers(d3d->dxgi_swapchain,
                                       0U, /* preserve buffer count */
                                       width, height,
                                       DXGI_FORMAT_UNKNOWN, /* preserve format */
                                       0U);
#endif
    if ((res == DXGI_ERROR_DEVICE_REMOVED) ||
        (res == DXGI_ERROR_DEVICE_RESET) ||
        (res == DXGI_ERROR_DRIVER_INTERNAL_ERROR))
    {
        return;
    }

    if (FAILED(res))
    {
        printf("ResizeBuffers() failed\n");
        fflush(stdout);
        return;
    }

    /* get the internal buffer of the swap chain */
#ifdef HAVE_WIN10
    res = IDXGISwapChain1_GetBuffer(d3d->dxgi_swapchain, 0,
                                    &IID_ID3D11Texture2D,
                                    (void **)&back_buffer);
#else
    res = IDXGISwapChain_GetBuffer(d3d->dxgi_swapchain, 0,
                                   &IID_ID3D11Texture2D,
                                   (void **)&back_buffer);
#endif
    if (FAILED(res))
    {
        printf("swapchain GetBuffer() failed\n");
        fflush(stdout);
        return;
    }

    ZeroMemory(&desc_rtv, sizeof(D3D11_RENDER_TARGET_VIEW_DESC));
    desc_rtv.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
    desc_rtv.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;

    /* create the new render target view from this internal buffer */
    res = ID3D11Device_CreateRenderTargetView(d3d->d3d_device,
                                              (ID3D11Resource *)back_buffer,
                                              &desc_rtv,
                                              &d3d->d3d_render_target_view);

    ID3D11Texture2D_Release(back_buffer);

    /* set viewport, depends on size of the window */
    d3d->viewport.TopLeftX = 0.0f;
    d3d->viewport.TopLeftY = 0.0f;
    d3d->viewport.Width = (float)width;
    d3d->viewport.Height = (float)height;
    d3d->viewport.MinDepth = 0.0f;
    d3d->viewport.MaxDepth = 1.0f;
}

/*** triangle ***/

typedef struct
{
    ID3D11Buffer* vertex_buffer;
    ID3D11Buffer* index_buffer; /* not useful for a single triangle */
    UINT stride;
    UINT offset;
    UINT count;
    UINT index_count;
} Triangle;

Triangle *triangle_new(D3d *d3d,
                       int x1, int y1,
                       int x2, int y2,
                       int x3, int y3,
                       unsigned char r,
                       unsigned char g,
                       unsigned char b,
                       unsigned char a)
{
    Vertex vertices[3];
    unsigned int indices[3];
    D3D11_BUFFER_DESC desc;
    D3D11_SUBRESOURCE_DATA sr_data;
    Triangle *t;
    HRESULT res;

    t = (Triangle *)malloc(sizeof(Triangle));
    if (!t)
        return NULL;

    vertices[0].x = x1;
    vertices[0].y = y1;
    vertices[0].r = r;
    vertices[0].g = g;
    vertices[0].b = b;
    vertices[0].a = a;
    vertices[1].x = x2;
    vertices[1].y = y2;
    vertices[1].r = r;
    vertices[1].g = g;
    vertices[1].b = b;
    vertices[1].a = a;
    vertices[2].x = x3;
    vertices[2].y = y3;
    vertices[2].r = r;
    vertices[2].g = g;
    vertices[2].b = b;
    vertices[2].a = a;

    indices[0] = 0;
    indices[1] = 1;
    indices[2] = 2;

    t->stride = sizeof(Vertex);
    t->offset = 0U;
    t->index_count = 3U;

    desc.ByteWidth = sizeof(vertices);
    desc.Usage = D3D11_USAGE_DYNAMIC;
    desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    desc.MiscFlags = 0U;
    desc.StructureByteStride = 0U;

    sr_data.pSysMem = vertices;
    sr_data.SysMemPitch = 0U;
    sr_data.SysMemSlicePitch = 0U;

    res =ID3D11Device_CreateBuffer(d3d->d3d_device,
                                   &desc,
                                   &sr_data,
                                   &t->vertex_buffer);
    if (FAILED(res))
    {
        free(t);
        return NULL;
    }

    desc.ByteWidth = sizeof(indices);
    desc.Usage = D3D11_USAGE_DYNAMIC;
    desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
    desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    desc.MiscFlags = 0U;
    desc.StructureByteStride = 0U;

    sr_data.pSysMem = indices;
    sr_data.SysMemPitch = 0U;
    sr_data.SysMemSlicePitch = 0U;

    res =ID3D11Device_CreateBuffer(d3d->d3d_device,
                                   &desc,
                                   &sr_data,
                                   &t->index_buffer);
    if (FAILED(res))
    {
        ID3D11Buffer_Release(t->vertex_buffer);
        free(t);
        return NULL;
    }

    return t;
}

void triangle_free(Triangle *t)
{
    if (!t)
        return ;

    ID3D11Buffer_Release(t->index_buffer);
    ID3D11Buffer_Release(t->vertex_buffer);
    free(t);
}

void d3d_render(D3d *d3d)
{
#ifdef HAVE_WIN10
    DXGI_PRESENT_PARAMETERS pp;
#endif
    const FLOAT color[4] = { 0.10f, 0.18f, 0.24f, 1.0f };
    HRESULT res;

    FCT;

    /* scene */
    Triangle *t;

    t = triangle_new(d3d,
                     320, 120,
                     480, 360,
                     160, 360,
                     255, 255, 0, 255); /* r, g, b, a */

    /* clear render target */
    ID3D11DeviceContext_ClearRenderTargetView(d3d->d3d_device_ctx,
                                              d3d->d3d_render_target_view,
                                              color);
    /* Input Assembler (IA) stage */
    ID3D11DeviceContext_IASetPrimitiveTopology(d3d->d3d_device_ctx,
                                               D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
    ID3D11DeviceContext_IASetInputLayout(d3d->d3d_device_ctx,
                                         d3d->d3d_input_layout);
    ID3D11DeviceContext_IASetVertexBuffers(d3d->d3d_device_ctx,
                                           0,
                                           1,
                                           &t->vertex_buffer,
                                           &t->stride,
                                           &t->offset);
    ID3D11DeviceContext_IASetIndexBuffer(d3d->d3d_device_ctx,
                                         t->index_buffer,
                                         DXGI_FORMAT_R32_UINT,
                                         0);
    /* vertex shader stage */
    ID3D11DeviceContext_VSSetShader(d3d->d3d_device_ctx,
                                    d3d->d3d_vertex_shader,
                                    NULL,
                                    0);
    ID3D11DeviceContext_VSSetConstantBuffers(d3d->d3d_device_ctx,
                                             0,
                                             1,
                                             &d3d->d3d_const_buffer);

    /* Rasterizer Stage */
    ID3D11DeviceContext_RSSetState(d3d->d3d_device_ctx,
                                   d3d->d3d_rasterizer_state);
    ID3D11DeviceContext_RSSetViewports(d3d->d3d_device_ctx,
                                       1U, &d3d->viewport);
    /* pixel shader stage */
    ID3D11DeviceContext_PSSetShader(d3d->d3d_device_ctx,
                                    d3d->d3d_pixel_shader,
                                    NULL,
                                    0);

    /* Output Merger stage */
    ID3D11DeviceContext_OMSetRenderTargets(d3d->d3d_device_ctx,
                                           1U, &d3d->d3d_render_target_view,
                                           NULL);

    /* draw */
    ID3D11DeviceContext_DrawIndexed(d3d->d3d_device_ctx,
                                    t->index_count,
                                    0, 0);

    triangle_free(t);

    /*
     * present frame, that is flip the back buffer and the front buffer
     * if no vsync, we present immediatly
     */
#ifdef HAVE_WIN10
    pp.DirtyRectsCount = 0;
    pp.pDirtyRects = NULL;
    pp.pScrollRect = NULL;
    pp.pScrollOffset = NULL;
    res = IDXGISwapChain1_Present1(d3d->dxgi_swapchain,
                                   d3d->vsync ? 1 : 0, 0, &pp);
#else
    res = IDXGISwapChain_Present(d3d->dxgi_swapchain,
                                 d3d->vsync ? 1 : 0, 0);
#endif
    if (res == DXGI_ERROR_DEVICE_RESET || res == DXGI_ERROR_DEVICE_REMOVED)
    {
        printf("device removed or lost, need to recreate everything\n");
        fflush(stdout);
    }
    else if (res == DXGI_STATUS_OCCLUDED)
    {
        printf("window is not visible, so vsync won't work. Let's sleep a bit to reduce CPU usage\n");
        fflush(stdout);
    }
}
d3d_4.diff
--- d3d_3.c	2022-04-13 16:49:51.368538000 +0200
+++ d3d_4.c	2022-04-13 17:19:56.378507800 +0200
@@ -2,11 +2,11 @@
 /*
  * Windows 10:
 
- gcc -g -O2 -Wall -Wextra -o d3d d3d_3.c win_2.c -ld3d11 -ld3dcompiler -ldxgi -luuid -D_WIN32_WINNT=0x0A00
+ gcc -g -O2 -Wall -Wextra -o d3d d3d_4.c win_2.c -ld3d11 -ld3dcompiler -ldxgi -luuid -D_WIN32_WINNT=0x0A00
 
  * Windows 7:
 
- gcc -g -O2 -Wall -Wextra -o d3d d3d_3.c win_2.c -ld3d11 -ld3dcompiler -ldxgi -luuid -D_WIN32_WINNT=0x0601
+ gcc -g -O2 -Wall -Wextra -o d3d d3d_4.c win_2.c -ld3d11 -ld3dcompiler -ldxgi -luuid -D_WIN32_WINNT=0x0601
 
  */
 
@@ -67,6 +67,10 @@
 {
     UINT x;
     UINT y;
+    BYTE r;
+    BYTE g;
+    BYTE b;
+    BYTE a;
 } Vertex;
 
 /************************** D3D11 **************************/
@@ -155,7 +159,8 @@
 {
     D3D11_INPUT_ELEMENT_DESC desc_ie[] =
     {
-        { "POSITION", 0, DXGI_FORMAT_R32G32_UINT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }
+        { "POSITION", 0, DXGI_FORMAT_R32G32_UINT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
+        { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 2 * sizeof(UINT), D3D11_INPUT_PER_VERTEX_DATA, 0 }
     };
 #ifdef HAVE_WIN10
     DXGI_SWAP_CHAIN_DESC1 desc_sw;
@@ -320,7 +325,7 @@
     flags |= D3DCOMPILE_DEBUG;
 #endif
     vs_blob = NULL;
-    res = D3DCompileFromFile(L"shader_2.hlsl",
+    res = D3DCompileFromFile(L"shader_3.hlsl",
                              NULL,
                              D3D_COMPILE_STANDARD_FILE_INCLUDE,
                              "main_vs",
@@ -365,7 +370,7 @@
 
     /* Pixel shader */
     ps_blob = NULL;
-    res = D3DCompileFromFile(L"shader_2.hlsl",
+    res = D3DCompileFromFile(L"shader_3.hlsl",
                              NULL,
                              D3D_COMPILE_STANDARD_FILE_INCLUDE,
                              "main_ps",
@@ -619,7 +624,11 @@
 Triangle *triangle_new(D3d *d3d,
                        int x1, int y1,
                        int x2, int y2,
-                       int x3, int y3)
+                       int x3, int y3,
+                       unsigned char r,
+                       unsigned char g,
+                       unsigned char b,
+                       unsigned char a)
 {
     Vertex vertices[3];
     unsigned int indices[3];
@@ -634,10 +643,22 @@
 
     vertices[0].x = x1;
     vertices[0].y = y1;
+    vertices[0].r = r;
+    vertices[0].g = g;
+    vertices[0].b = b;
+    vertices[0].a = a;
     vertices[1].x = x2;
     vertices[1].y = y2;
+    vertices[1].r = r;
+    vertices[1].g = g;
+    vertices[1].b = b;
+    vertices[1].a = a;
     vertices[2].x = x3;
     vertices[2].y = y3;
+    vertices[2].r = r;
+    vertices[2].g = g;
+    vertices[2].b = b;
+    vertices[2].a = a;
 
     indices[0] = 0;
     indices[1] = 1;
@@ -719,7 +740,8 @@
     t = triangle_new(d3d,
                      320, 120,
                      480, 360,
-                     160, 360);
+                     160, 360,
+                     255, 255, 0, 255); /* r, g, b, a */
 
     /* clear render target */
     ID3D11DeviceContext_ClearRenderTargetView(d3d->d3d_device_ctx,

When the program is launched, you should obtain a window with a yellow triangle, like this:

Display a colored rectangle

Now we have everything to display a rectangle. A rectangle is just 2 triangles one next to another one. So one can consider 6 vertices to define the rectangle (3 for both triangles). But there is a smarter way which saves memory: we define the 4 vertices of a rectangle, say vertices v0, v1, v2 and v3 in the clock-wise orientation, and an array of 6 indices which define the numero of the vertex for the 2 triangles. For example, this array can be 0, 1, 3 for the first triangle, and 1, 2, 3 for the second triangle. See the topology above for the difference between a triangle list and a triangle strip. That is why we use D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP in the IA stage. Otherwise, it's the same than a triangle: we set the index and vertex buffer and we draw the figure.

Here is the code source that draw a yellow triangle and a blue rectangle, followedd with the diff:

d3d_5.c

/*
 * Windows 10:

 gcc -g -O2 -Wall -Wextra -o d3d d3d_5.c win_2.c -ld3d11 -ld3dcompiler -ldxgi -luuid -D_WIN32_WINNT=0x0A00

 * Windows 7:

 gcc -g -O2 -Wall -Wextra -o d3d d3d_5.c win_2.c -ld3d11 -ld3dcompiler -ldxgi -luuid -D_WIN32_WINNT=0x0601

 */

#include <stdio.h>

#if defined _WIN32_WINNT && _WIN32_WINNT >= 0x0A00
# define HAVE_WIN10
#endif

/* C API for d3d11 */
#define COBJMACROS

#ifdef HAVE_WIN10
# include <dxgi1_3.h>
#else
# include <dxgi.h>
#endif
#include <d3d11.h>
#include <d3dcompiler.h>

#include "win.h"

#ifdef _DEBUG
# define FCT \
do { printf(" * %s\n", __FUNCTION__); fflush(stdout); } while (0)
#else
# define FCT \
do { } while (0)
#endif

#define XF(w,x) ((float)(2 * (x) - (w)) / (float)(w))
#define YF(h,y) ((float)((h) - 2 * (y)) / (float)(h))

struct D3d
{
    /* DXGI */
#ifdef HAVE_WIN10
    IDXGIFactory2 *dxgi_factory;
    IDXGISwapChain1 *dxgi_swapchain;
#else
    IDXGIFactory *dxgi_factory;
    IDXGISwapChain *dxgi_swapchain;
#endif
    /* D3D11 */
    ID3D11Device *d3d_device;
    ID3D11DeviceContext *d3d_device_ctx;
    ID3D11RenderTargetView *d3d_render_target_view;
    ID3D11InputLayout *d3d_input_layout;
    ID3D11VertexShader *d3d_vertex_shader;
    ID3D11Buffer *d3d_const_buffer;
    ID3D11RasterizerState *d3d_rasterizer_state;
    ID3D11PixelShader *d3d_pixel_shader;
    D3D11_VIEWPORT viewport;
    unsigned int vsync : 1;
};

typedef struct
{
    UINT x;
    UINT y;
    BYTE r;
    BYTE g;
    BYTE b;
    BYTE a;
} Vertex;

/************************** D3D11 **************************/

static void d3d_refresh_rate_get(D3d *d3d, UINT *num, UINT *den)
{
    DXGI_MODE_DESC *display_mode_list = NULL; /* 28 bytes */
    IDXGIAdapter *dxgi_adapter;
    IDXGIOutput *dxgi_output;
    UINT nbr_modes;
    UINT i;
    HRESULT res;

    *num = 0U;
    *den = 1U;

    if (!d3d->vsync)
        return;

    /* adapter of primary desktop : pass 0U */
    res = IDXGIFactory_EnumAdapters(d3d->dxgi_factory, 0U, &dxgi_adapter);
    if (FAILED(res))
        return;

    /* output of primary desktop : pass 0U */
    res = IDXGIAdapter_EnumOutputs(dxgi_adapter, 0U, &dxgi_output);
    if (FAILED(res))
        goto release_dxgi_adapter;

    /* number of mode that fit the format */
     res = IDXGIOutput_GetDisplayModeList(dxgi_output,
                                         DXGI_FORMAT_B8G8R8A8_UNORM,
                                         DXGI_ENUM_MODES_INTERLACED,
                                         &nbr_modes, NULL);
    if (FAILED(res))
        goto release_dxgi_output;

    printf("display mode list : %d\n", nbr_modes);
    fflush(stdout);
    display_mode_list = (DXGI_MODE_DESC *)malloc(nbr_modes * sizeof(DXGI_MODE_DESC));
    if (!display_mode_list)
        goto release_dxgi_output;

    /* fill the mode list */
    res = IDXGIOutput_GetDisplayModeList(dxgi_output,
                                         DXGI_FORMAT_B8G8R8A8_UNORM,
                                         DXGI_ENUM_MODES_INTERLACED,
                                         &nbr_modes, display_mode_list);
    if (FAILED(res))
        goto free_mode_list;

    for (i = 0; i < nbr_modes; i++)
    {
        if ((display_mode_list[i].Width == (UINT)GetSystemMetrics(SM_CXSCREEN)) &&
            (display_mode_list[i].Height == (UINT)GetSystemMetrics(SM_CYSCREEN)))
        {
            *num = display_mode_list[i].RefreshRate.Numerator;
            *den = display_mode_list[i].RefreshRate.Denominator;
            break;
        }
    }

#ifdef _DEBUG
    {
        DXGI_ADAPTER_DESC adapter_desc;

        IDXGIAdapter_GetDesc(dxgi_adapter, &adapter_desc);
        printf(" * video mem: %llu B, %llu MB\n",
               adapter_desc.DedicatedVideoMemory,
               adapter_desc.DedicatedVideoMemory / 1024 / 1024);
        fflush(stdout);
        wprintf(L" * description: %ls\n", adapter_desc.Description);
        fflush(stdout);
    }
#endif

  free_mode_list:
    free(display_mode_list);
  release_dxgi_output:
    IDXGIOutput_Release(dxgi_output);
  release_dxgi_adapter:
    IDXGIFactory_Release(dxgi_adapter);
}

D3d *d3d_init(Window *win, int vsync)
{
    D3D11_INPUT_ELEMENT_DESC desc_ie[] =
    {
        { "POSITION", 0, DXGI_FORMAT_R32G32_UINT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
        { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 2 * sizeof(UINT), D3D11_INPUT_PER_VERTEX_DATA, 0 }
    };
#ifdef HAVE_WIN10
    DXGI_SWAP_CHAIN_DESC1 desc_sw;
    DXGI_SWAP_CHAIN_FULLSCREEN_DESC desc_fs;
#else
    DXGI_SWAP_CHAIN_DESC desc_sw;
#endif
    D3D11_BUFFER_DESC desc_buf;
    D3D11_SUBRESOURCE_DATA desc_data;
    D3D11_RASTERIZER_DESC desc_rs;
    D3d *d3d;
    RECT r;
    HRESULT res;
    UINT flags;
    UINT num;
    UINT den;
    FLOAT ivps[4]; /* inverse viewport size */
    D3D_FEATURE_LEVEL feature_level[4];
    ID3DBlob *vs_blob; /* vertex shader blob ptr */
    ID3DBlob *ps_blob; /* pixel shader blob ptr */
    ID3DBlob *err_blob; /* error blob ptr */

    d3d = (D3d *)calloc(1, sizeof(D3d));
    if (!d3d)
        return NULL;

    d3d->vsync = vsync;
    win->d3d = d3d;

    /* create the DXGI factory */
    flags = 0;
#ifdef HAVE_WIN10
# ifdef _DEBUG
    flags = DXGI_CREATE_FACTORY_DEBUG;
# endif
    res = CreateDXGIFactory2(flags, &IID_IDXGIFactory2, (void **)&d3d->dxgi_factory);
#else
    res = CreateDXGIFactory(&IID_IDXGIFactory, (void **)&d3d->dxgi_factory);
#endif
    if (FAILED(res))
        goto free_d3d;

    /* software engine functions are called from the main loop */
    flags = D3D11_CREATE_DEVICE_SINGLETHREADED |
            D3D11_CREATE_DEVICE_BGRA_SUPPORT;
#ifdef HAVE_WIN10
# ifdef _DEBUG
    flags |= D3D11_CREATE_DEVICE_DEBUG;
# endif
#endif

    feature_level[0] = D3D_FEATURE_LEVEL_11_1;
    feature_level[1] = D3D_FEATURE_LEVEL_11_0;
    feature_level[2] = D3D_FEATURE_LEVEL_10_1;
    feature_level[3] = D3D_FEATURE_LEVEL_10_0;

    /* create device and device context with hardware support */
    res = D3D11CreateDevice(NULL,
                            D3D_DRIVER_TYPE_HARDWARE,
                            NULL,
                            flags,
                            feature_level,
                            3U,
                            D3D11_SDK_VERSION,
                            &d3d->d3d_device,
                            NULL,
                            &d3d->d3d_device_ctx);
    if (FAILED(res))
        goto release_dxgi_factory2;

    if (!GetClientRect(win->win, &r))
        goto release_d3d_device;

    /*
     * create the swap chain. It needs some settings...
     * the size of the internal buffers
     * the image format
     * the number of back buffers (>= 2 for flip model, see SwapEffect field)
     * vsync enabled: need refresh rate
     *
     * Settings are different in win 7 and win10
     */

    d3d_refresh_rate_get(d3d, &num, &den);

#ifdef HAVE_WIN10
    desc_sw.Width = r.right - r.left;
    desc_sw.Height = r.bottom - r.top;
    desc_sw.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
    desc_sw.Stereo = FALSE;
#else
    desc_sw.BufferDesc.Width= r.right - r.left;
    desc_sw.BufferDesc.Height = r.bottom - r.top;
    desc_sw.BufferDesc.RefreshRate.Numerator = num;
    desc_sw.BufferDesc.RefreshRate.Denominator = den;
    desc_sw.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;;
    desc_sw.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
    desc_sw.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
#endif
    desc_sw.SampleDesc.Count = 1U;
    desc_sw.SampleDesc.Quality = 0U;
    desc_sw.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    desc_sw.BufferCount = 2U;
#ifdef HAVE_WIN10
    desc_sw.Scaling = DXGI_SCALING_NONE;
#else
    desc_sw.OutputWindow = win->win;
    desc_sw.Windowed = TRUE;
#endif
    desc_sw.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
#ifdef HAVE_WIN10
    desc_sw.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
#endif
    desc_sw.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;

#ifdef HAVE_WIN10
    desc_fs.RefreshRate.Numerator = num;
    desc_fs.RefreshRate.Denominator = den;
    desc_fs.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
    desc_fs.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
    desc_fs.Windowed = TRUE;
#endif

#ifdef HAVE_WIN10
    res = IDXGIFactory2_CreateSwapChainForHwnd(d3d->dxgi_factory,
                                               (IUnknown *)d3d->d3d_device,
                                               win->win,
                                               &desc_sw,
                                               &desc_fs,
                                               NULL,
                                               &d3d->dxgi_swapchain);
#else
    res = IDXGIFactory_CreateSwapChain(d3d->dxgi_factory,
                                       (IUnknown *)d3d->d3d_device,
                                       &desc_sw,
                                       &d3d->dxgi_swapchain);
#endif
    if (FAILED(res))
        goto release_d3d_device;

    /* rasterizer */
    desc_rs.FillMode = D3D11_FILL_SOLID;
    desc_rs.CullMode = D3D11_CULL_NONE;
    desc_rs.FrontCounterClockwise = FALSE;
    desc_rs.DepthBias = 0;
    desc_rs.DepthBiasClamp = 0.0f;
    desc_rs.SlopeScaledDepthBias = 0.0f;
    desc_rs.DepthClipEnable = TRUE;
    desc_rs.ScissorEnable = FALSE;
    desc_rs.MultisampleEnable = FALSE;
    desc_rs.AntialiasedLineEnable = FALSE;

    res = ID3D11Device_CreateRasterizerState(d3d->d3d_device,
                                             &desc_rs,
                                             &d3d->d3d_rasterizer_state);
    if (FAILED(res))
        goto release_dxgi_swapchain;

    /* Vertex shader */
    flags = D3DCOMPILE_ENABLE_STRICTNESS;
#ifdef _DEBUG
    flags |= D3DCOMPILE_DEBUG;
#endif
    vs_blob = NULL;
    res = D3DCompileFromFile(L"shader_3.hlsl",
                             NULL,
                             D3D_COMPILE_STANDARD_FILE_INCLUDE,
                             "main_vs",
                             "vs_5_0",
                             flags,
                             0U,
                             &vs_blob,
                             &err_blob );

    if (FAILED(res))
    {
        printf(" * vs error : %s\n", (char *)ID3D10Blob_GetBufferPointer(err_blob));
        goto release_d3D_rasterizer;
    }

    res = ID3D11Device_CreateVertexShader(d3d->d3d_device,
                                          ID3D10Blob_GetBufferPointer(vs_blob),
                                          ID3D10Blob_GetBufferSize(vs_blob),
                                          NULL,
                                          &d3d->d3d_vertex_shader);

    if (FAILED(res))
    {
        printf(" * vs error : %s\n", (char *)ID3D10Blob_GetBufferPointer(err_blob));
        ID3D10Blob_Release(vs_blob);
        goto release_d3D_rasterizer;
    }

    /* create the input layout */
    res = ID3D11Device_CreateInputLayout(d3d->d3d_device,
                                         desc_ie,
                                         sizeof(desc_ie) / sizeof(D3D11_INPUT_ELEMENT_DESC),
                                         ID3D10Blob_GetBufferPointer(vs_blob),
                                         ID3D10Blob_GetBufferSize(vs_blob),
                                         &d3d->d3d_input_layout);
    ID3D10Blob_Release(vs_blob);
    if (FAILED(res))
    {
        printf(" * CreateInputLayout() failed\n");
        goto release_vertex_shader;
    }

    /* Pixel shader */
    ps_blob = NULL;
    res = D3DCompileFromFile(L"shader_3.hlsl",
                             NULL,
                             D3D_COMPILE_STANDARD_FILE_INCLUDE,
                             "main_ps",
                             "ps_5_0",
                             flags,
                             0U,
                             &ps_blob,
                             &err_blob );

    if (FAILED(res))
    {
        printf(" * ps blob error : %s\n", (char *)ID3D10Blob_GetBufferPointer(err_blob));
        goto release_input_layout;
    }

    res = ID3D11Device_CreatePixelShader(d3d->d3d_device,
                                         ID3D10Blob_GetBufferPointer(ps_blob),
                                         ID3D10Blob_GetBufferSize(ps_blob),
                                         NULL,
                                         &d3d->d3d_pixel_shader);
    ID3D10Blob_Release(ps_blob);
    if (FAILED(res))
    {
        printf(" * CreatePixelShader() failed\n");
        goto release_input_layout;
    }

#ifdef HAVE_WIN10
    ivps[0] = 1.0f / desc_sw.Width;
    ivps[1] = 1.0f / desc_sw.Height;
#else
    ivps[0] = 1.0f / desc_sw.BufferDesc.Width;
    ivps[1] = 1.0f / desc_sw.BufferDesc.Height;
#endif
    ivps[2] = 0.0f;
    ivps[3] = 0.0f;

    desc_buf.ByteWidth = sizeof(ivps);
    desc_buf.Usage = D3D11_USAGE_DYNAMIC; /* because buffer is updated when the window has resized */
    desc_buf.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
    desc_buf.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    desc_buf.MiscFlags = 0;
    desc_buf.StructureByteStride = 0;

    desc_data.pSysMem = ivps;
    desc_data.SysMemPitch = 0;
    desc_data.SysMemSlicePitch = 0;

    res = ID3D11Device_CreateBuffer(d3d->d3d_device,
                                    &desc_buf,
                                    &desc_data,
                                    &d3d->d3d_const_buffer);
    if (FAILED(res))
    {
        printf(" * CreateBuffer() failed 0x%lx\n", res);
        goto release_pixel_shader;
    }

    return d3d;

  release_pixel_shader:
    ID3D11PixelShader_Release(d3d->d3d_pixel_shader);
  release_input_layout:
    ID3D11InputLayout_Release(d3d->d3d_input_layout);
  release_vertex_shader:
    ID3D11VertexShader_Release(d3d->d3d_vertex_shader);
  release_d3D_rasterizer:
    ID3D11RasterizerState_Release(d3d->d3d_rasterizer_state);
  release_dxgi_swapchain:
#ifdef HAVE_WIN10
    IDXGISwapChain1_SetFullscreenState(d3d->dxgi_swapchain, FALSE, NULL);
    IDXGISwapChain1_Release(d3d->dxgi_swapchain);
#else
    IDXGISwapChain_SetFullscreenState(d3d->dxgi_swapchain, FALSE, NULL);
    IDXGISwapChain_Release(d3d->dxgi_swapchain);
#endif
  release_d3d_device:
    ID3D11DeviceContext_Release(d3d->d3d_device_ctx);
    ID3D11Device_Release(d3d->d3d_device);
  release_dxgi_factory2:
#ifdef HAVE_WIN10
    IDXGIFactory2_Release(d3d->dxgi_factory);
#else
    IDXGIFactory_Release(d3d->dxgi_factory);
#endif
  free_d3d:
    free(d3d);

    return NULL;
}

void d3d_shutdown(D3d *d3d)
{
#ifdef _DEBUG
    ID3D11Debug *d3d_debug;
    HRESULT res;
#endif

    if (!d3d)
        return;

#ifdef _DEBUG
    res = ID3D11Debug_QueryInterface(d3d->d3d_device, &IID_ID3D11Debug,
                                     (void **)&d3d_debug);
#endif

    ID3D11Buffer_Release(d3d->d3d_const_buffer);
    ID3D11PixelShader_Release(d3d->d3d_pixel_shader);
    ID3D11InputLayout_Release(d3d->d3d_input_layout);
    ID3D11VertexShader_Release(d3d->d3d_vertex_shader);
    ID3D11RasterizerState_Release(d3d->d3d_rasterizer_state);
    ID3D11RenderTargetView_Release(d3d->d3d_render_target_view);
#ifdef HAVE_WIN10
    IDXGISwapChain1_SetFullscreenState(d3d->dxgi_swapchain, FALSE, NULL);
    IDXGISwapChain1_Release(d3d->dxgi_swapchain);
#else
    IDXGISwapChain_SetFullscreenState(d3d->dxgi_swapchain, FALSE, NULL);
    IDXGISwapChain_Release(d3d->dxgi_swapchain);
#endif
    ID3D11DeviceContext_Release(d3d->d3d_device_ctx);
    ID3D11Device_Release(d3d->d3d_device);
#ifdef HAVE_WIN10
    IDXGIFactory2_Release(d3d->dxgi_factory);
#else
    IDXGIFactory_Release(d3d->dxgi_factory);
#endif
    free(d3d);

#ifdef _DEBUG
    if (SUCCEEDED(res))
    {
        ID3D11Debug_ReportLiveDeviceObjects(d3d_debug, D3D11_RLDO_DETAIL);
        ID3D11Debug_Release(d3d_debug);
    }
#endif
}

void d3d_resize(D3d *d3d, UINT width, UINT height)
{
    D3D11_RENDER_TARGET_VIEW_DESC desc_rtv;
    D3D11_MAPPED_SUBRESOURCE mapped;
    ID3D11Texture2D *back_buffer;
    HRESULT res;

    FCT;

    res = ID3D11DeviceContext_Map(d3d->d3d_device_ctx,
                                  (ID3D11Resource *)d3d->d3d_const_buffer,
                                  0U, D3D11_MAP_WRITE_DISCARD, 0, &mapped);

    if (FAILED(res))
    {
        printf("Map() failed\n");
        fflush(stdout);
        return;
    }

    ((float *)mapped.pData)[0] = 1.0f / (float)width;
    ((float *)mapped.pData)[1] = 1.0f / (float)height;

    ID3D11DeviceContext_Unmap(d3d->d3d_device_ctx,
                              (ID3D11Resource *)d3d->d3d_const_buffer,
                              0U);

    /* unset the render target view in the output merger */
    ID3D11DeviceContext_OMSetRenderTargets(d3d->d3d_device_ctx,
                                           0U, NULL, NULL);

    /* release the render target view */
    if (d3d->d3d_render_target_view)
        ID3D11RenderTargetView_Release(d3d->d3d_render_target_view);

    /* resize the internal nuffers of the swapt chain to the new size */
#ifdef HAVE_WIN10
    res = IDXGISwapChain1_ResizeBuffers(d3d->dxgi_swapchain,
                                        0U, /* preserve buffer count */
                                        width, height,
                                        DXGI_FORMAT_UNKNOWN, /* preserve format */
                                        0U);
#else
    res = IDXGISwapChain_ResizeBuffers(d3d->dxgi_swapchain,
                                       0U, /* preserve buffer count */
                                       width, height,
                                       DXGI_FORMAT_UNKNOWN, /* preserve format */
                                       0U);
#endif
    if ((res == DXGI_ERROR_DEVICE_REMOVED) ||
        (res == DXGI_ERROR_DEVICE_RESET) ||
        (res == DXGI_ERROR_DRIVER_INTERNAL_ERROR))
    {
        return;
    }

    if (FAILED(res))
    {
        printf("ResizeBuffers() failed\n");
        fflush(stdout);
        return;
    }

    /* get the internal buffer of the swap chain */
#ifdef HAVE_WIN10
    res = IDXGISwapChain1_GetBuffer(d3d->dxgi_swapchain, 0,
                                    &IID_ID3D11Texture2D,
                                    (void **)&back_buffer);
#else
    res = IDXGISwapChain_GetBuffer(d3d->dxgi_swapchain, 0,
                                   &IID_ID3D11Texture2D,
                                   (void **)&back_buffer);
#endif
    if (FAILED(res))
    {
        printf("swapchain GetBuffer() failed\n");
        fflush(stdout);
        return;
    }

    ZeroMemory(&desc_rtv, sizeof(D3D11_RENDER_TARGET_VIEW_DESC));
    desc_rtv.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
    desc_rtv.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;

    /* create the new render target view from this internal buffer */
    res = ID3D11Device_CreateRenderTargetView(d3d->d3d_device,
                                              (ID3D11Resource *)back_buffer,
                                              &desc_rtv,
                                              &d3d->d3d_render_target_view);

    ID3D11Texture2D_Release(back_buffer);

    /* set viewport, depends on size of the window */
    d3d->viewport.TopLeftX = 0.0f;
    d3d->viewport.TopLeftY = 0.0f;
    d3d->viewport.Width = (float)width;
    d3d->viewport.Height = (float)height;
    d3d->viewport.MinDepth = 0.0f;
    d3d->viewport.MaxDepth = 1.0f;
}

/*** triangle ***/

typedef struct
{
    ID3D11Buffer* vertex_buffer;
    ID3D11Buffer* index_buffer; /* not useful for a single triangle */
    UINT stride;
    UINT offset;
    UINT count;
    UINT index_count;
} Triangle;

Triangle *triangle_new(D3d *d3d,
                       int x1, int y1,
                       int x2, int y2,
                       int x3, int y3,
                       unsigned char r,
                       unsigned char g,
                       unsigned char b,
                       unsigned char a)
{
    Vertex vertices[3];
    unsigned int indices[3];
    D3D11_BUFFER_DESC desc;
    D3D11_SUBRESOURCE_DATA sr_data;
    Triangle *t;
    HRESULT res;

    t = (Triangle *)malloc(sizeof(Triangle));
    if (!t)
        return NULL;

    vertices[0].x = x1;
    vertices[0].y = y1;
    vertices[0].r = r;
    vertices[0].g = g;
    vertices[0].b = b;
    vertices[0].a = a;
    vertices[1].x = x2;
    vertices[1].y = y2;
    vertices[1].r = r;
    vertices[1].g = g;
    vertices[1].b = b;
    vertices[1].a = a;
    vertices[2].x = x3;
    vertices[2].y = y3;
    vertices[2].r = r;
    vertices[2].g = g;
    vertices[2].b = b;
    vertices[2].a = a;

    indices[0] = 0;
    indices[1] = 1;
    indices[2] = 2;

    t->stride = sizeof(Vertex);
    t->offset = 0U;
    t->index_count = 3U;

    desc.ByteWidth = sizeof(vertices);
    desc.Usage = D3D11_USAGE_DYNAMIC;
    desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    desc.MiscFlags = 0U;
    desc.StructureByteStride = 0U;

    sr_data.pSysMem = vertices;
    sr_data.SysMemPitch = 0U;
    sr_data.SysMemSlicePitch = 0U;

    res =ID3D11Device_CreateBuffer(d3d->d3d_device,
                                   &desc,
                                   &sr_data,
                                   &t->vertex_buffer);
    if (FAILED(res))
    {
        free(t);
        return NULL;
    }

    desc.ByteWidth = sizeof(indices);
    desc.Usage = D3D11_USAGE_DYNAMIC;
    desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
    desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    desc.MiscFlags = 0U;
    desc.StructureByteStride = 0U;

    sr_data.pSysMem = indices;
    sr_data.SysMemPitch = 0U;
    sr_data.SysMemSlicePitch = 0U;

    res =ID3D11Device_CreateBuffer(d3d->d3d_device,
                                   &desc,
                                   &sr_data,
                                   &t->index_buffer);
    if (FAILED(res))
    {
        ID3D11Buffer_Release(t->vertex_buffer);
        free(t);
        return NULL;
    }

    return t;
}

void triangle_free(Triangle *t)
{
    if (!t)
        return ;

    ID3D11Buffer_Release(t->index_buffer);
    ID3D11Buffer_Release(t->vertex_buffer);
    free(t);
}

/*** rectangle ***/

typedef struct
{
    ID3D11Buffer* vertex_buffer;
    ID3D11Buffer* index_buffer;
    UINT stride;
    UINT offset;
    UINT count;
    UINT index_count;
} Rect;

Rect *rectangle_new(D3d *d3d,
                    int x, int y,
                    int w, int h,
                    unsigned char r,
                    unsigned char g,
                    unsigned char b,
                    unsigned char a)
{
    Vertex vertices[4];
    unsigned int indices[6];
    D3D11_BUFFER_DESC desc;
    D3D11_SUBRESOURCE_DATA sr_data;
    Rect *rc;
    HRESULT res;

    rc = (Rect *)malloc(sizeof(Rect));
    if (!rc)
        return NULL;

    /* vertex upper left */
    vertices[0].x = x;
    vertices[0].y = y;
    vertices[0].r = r;
    vertices[0].g = g;
    vertices[0].b = b;
    vertices[0].a = a;
    /* vertex upper right*/
    vertices[1].x = x + w;
    vertices[1].y = y;
    vertices[1].r = r;
    vertices[1].g = g;
    vertices[1].b = b;
    vertices[1].a = a;
    /* vertex bottom right*/
    vertices[2].x = x + w;
    vertices[2].y = y + h;
    vertices[2].r = r;
    vertices[2].g = g;
    vertices[2].b = b;
    vertices[2].a = a;
    /* vertex bottom left*/
    vertices[3].x = x;
    vertices[3].y = y + h;
    vertices[3].r = r;
    vertices[3].g = g;
    vertices[3].b = b;
    vertices[3].a = a;

    /* triangle upper left */
    indices[0] = 0;
    indices[1] = 1;
    indices[2] = 3;
    /* triangle bottom right */
    indices[3] = 1;
    indices[4] = 2;
    indices[5] = 3;

    rc->stride = sizeof(Vertex);
    rc->offset = 0U;
    rc->index_count = 6U;

    desc.ByteWidth = sizeof(vertices);
    desc.Usage = D3D11_USAGE_DYNAMIC;
    desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    desc.MiscFlags = 0U;
    desc.StructureByteStride = 0U;

    sr_data.pSysMem = vertices;
    sr_data.SysMemPitch = 0U;
    sr_data.SysMemSlicePitch = 0U;

    res =ID3D11Device_CreateBuffer(d3d->d3d_device,
                                   &desc,
                                   &sr_data,
                                   &rc->vertex_buffer);
    if (FAILED(res))
    {
        free(rc);
        return NULL;
    }

    desc.ByteWidth = sizeof(indices);
    desc.Usage = D3D11_USAGE_DYNAMIC;
    desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
    desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    desc.MiscFlags = 0U;
    desc.StructureByteStride = 0U;

    sr_data.pSysMem = indices;
    sr_data.SysMemPitch = 0U;
    sr_data.SysMemSlicePitch = 0U;

    res =ID3D11Device_CreateBuffer(d3d->d3d_device,
                                   &desc,
                                   &sr_data,
                                   &rc->index_buffer);
    if (FAILED(res))
    {
        ID3D11Buffer_Release(rc->vertex_buffer);
        free(rc);
        return NULL;
    }

    return rc;
}

void rectangle_free(Rect *r)
{
    if (!r)
        return ;

    ID3D11Buffer_Release(r->index_buffer);
    ID3D11Buffer_Release(r->vertex_buffer);
    free(r);
}

void d3d_render(D3d *d3d)
{
#ifdef HAVE_WIN10
    DXGI_PRESENT_PARAMETERS pp;
#endif
    const FLOAT color[4] = { 0.10f, 0.18f, 0.24f, 1.0f };
    HRESULT res;

    FCT;

    /* scene */
    Triangle *t;

    t = triangle_new(d3d,
                     320, 120,
                     480, 360,
                     160, 360,
                     255, 255, 0, 255); /* r, g, b, a */

    /* clear render target */
    ID3D11DeviceContext_ClearRenderTargetView(d3d->d3d_device_ctx,
                                              d3d->d3d_render_target_view,
                                              color);
    /* Input Assembler (IA) stage */
    ID3D11DeviceContext_IASetPrimitiveTopology(d3d->d3d_device_ctx,
                                               D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
    ID3D11DeviceContext_IASetInputLayout(d3d->d3d_device_ctx,
                                         d3d->d3d_input_layout);
    ID3D11DeviceContext_IASetVertexBuffers(d3d->d3d_device_ctx,
                                           0,
                                           1,
                                           &t->vertex_buffer,
                                           &t->stride,
                                           &t->offset);
    ID3D11DeviceContext_IASetIndexBuffer(d3d->d3d_device_ctx,
                                         t->index_buffer,
                                         DXGI_FORMAT_R32_UINT,
                                         0);
    /* vertex shader stage */
    ID3D11DeviceContext_VSSetShader(d3d->d3d_device_ctx,
                                    d3d->d3d_vertex_shader,
                                    NULL,
                                    0);
    ID3D11DeviceContext_VSSetConstantBuffers(d3d->d3d_device_ctx,
                                             0,
                                             1,
                                             &d3d->d3d_const_buffer);

    /* Rasterizer Stage */
    ID3D11DeviceContext_RSSetState(d3d->d3d_device_ctx,
                                   d3d->d3d_rasterizer_state);
    ID3D11DeviceContext_RSSetViewports(d3d->d3d_device_ctx,
                                       1U, &d3d->viewport);
    /* pixel shader stage */
    ID3D11DeviceContext_PSSetShader(d3d->d3d_device_ctx,
                                    d3d->d3d_pixel_shader,
                                    NULL,
                                    0);

    /* Output Merger stage */
    ID3D11DeviceContext_OMSetRenderTargets(d3d->d3d_device_ctx,
                                           1U, &d3d->d3d_render_target_view,
                                           NULL);

    /* draw */
    ID3D11DeviceContext_DrawIndexed(d3d->d3d_device_ctx,
                                    t->index_count,
                                    0, 0);

    triangle_free(t);

    Rect *r;

    r = rectangle_new(d3d,
                      520, 120,
                      200, 100,
                      0, 0, 255, 255);
    ID3D11DeviceContext_IASetVertexBuffers(d3d->d3d_device_ctx,
                                           0,
                                           1,
                                           &r->vertex_buffer,
                                           &r->stride,
                                           &r->offset);
    ID3D11DeviceContext_IASetIndexBuffer(d3d->d3d_device_ctx,
                                         r->index_buffer,
                                         DXGI_FORMAT_R32_UINT,
                                         0);
    ID3D11DeviceContext_DrawIndexed(d3d->d3d_device_ctx,
                                    r->index_count,
                                    0, 0);

    rectangle_free(r);

    /*
     * present frame, that is flip the back buffer and the front buffer
     * if no vsync, we present immediatly
     */
#ifdef HAVE_WIN10
    pp.DirtyRectsCount = 0;
    pp.pDirtyRects = NULL;
    pp.pScrollRect = NULL;
    pp.pScrollOffset = NULL;
    res = IDXGISwapChain1_Present1(d3d->dxgi_swapchain,
                                   d3d->vsync ? 1 : 0, 0, &pp);
#else
    res = IDXGISwapChain_Present(d3d->dxgi_swapchain,
                                 d3d->vsync ? 1 : 0, 0);
#endif
    if (res == DXGI_ERROR_DEVICE_RESET || res == DXGI_ERROR_DEVICE_REMOVED)
    {
        printf("device removed or lost, need to recreate everything\n");
        fflush(stdout);
    }
    else if (res == DXGI_STATUS_OCCLUDED)
    {
        printf("window is not visible, so vsync won't work. Let's sleep a bit to reduce CPU usage\n");
        fflush(stdout);
    }
}
d3d_5.diff
--- d3d_4.c	2022-04-13 17:19:56.378507800 +0200
+++ d3d_5.c	2022-04-13 19:06:10.073483500 +0200
@@ -2,11 +2,11 @@
 /*
  * Windows 10:
 
- gcc -g -O2 -Wall -Wextra -o d3d d3d_4.c win_2.c -ld3d11 -ld3dcompiler -ldxgi -luuid -D_WIN32_WINNT=0x0A00
+ gcc -g -O2 -Wall -Wextra -o d3d d3d_5.c win_2.c -ld3d11 -ld3dcompiler -ldxgi -luuid -D_WIN32_WINNT=0x0A00
 
  * Windows 7:
 
- gcc -g -O2 -Wall -Wextra -o d3d d3d_4.c win_2.c -ld3d11 -ld3dcompiler -ldxgi -luuid -D_WIN32_WINNT=0x0601
+ gcc -g -O2 -Wall -Wextra -o d3d d3d_5.c win_2.c -ld3d11 -ld3dcompiler -ldxgi -luuid -D_WIN32_WINNT=0x0601
 
  */
 
@@ -724,6 +724,135 @@
     free(t);
 }
 
+/*** rectangle ***/
+
+typedef struct
+{
+    ID3D11Buffer* vertex_buffer;
+    ID3D11Buffer* index_buffer;
+    UINT stride;
+    UINT offset;
+    UINT count;
+    UINT index_count;
+} Rect;
+
+Rect *rectangle_new(D3d *d3d,
+                    int x, int y,
+                    int w, int h,
+                    unsigned char r,
+                    unsigned char g,
+                    unsigned char b,
+                    unsigned char a)
+{
+    Vertex vertices[4];
+    unsigned int indices[6];
+    D3D11_BUFFER_DESC desc;
+    D3D11_SUBRESOURCE_DATA sr_data;
+    Rect *rc;
+    HRESULT res;
+
+    rc = (Rect *)malloc(sizeof(Rect));
+    if (!rc)
+        return NULL;
+
+    /* vertex upper left */
+    vertices[0].x = x;
+    vertices[0].y = y;
+    vertices[0].r = r;
+    vertices[0].g = g;
+    vertices[0].b = b;
+    vertices[0].a = a;
+    /* vertex upper right*/
+    vertices[1].x = x + w;
+    vertices[1].y = y;
+    vertices[1].r = r;
+    vertices[1].g = g;
+    vertices[1].b = b;
+    vertices[1].a = a;
+    /* vertex bottom right*/
+    vertices[2].x = x + w;
+    vertices[2].y = y + h;
+    vertices[2].r = r;
+    vertices[2].g = g;
+    vertices[2].b = b;
+    vertices[2].a = a;
+    /* vertex bottom left*/
+    vertices[3].x = x;
+    vertices[3].y = y + h;
+    vertices[3].r = r;
+    vertices[3].g = g;
+    vertices[3].b = b;
+    vertices[3].a = a;
+
+    /* triangle upper left */
+    indices[0] = 0;
+    indices[1] = 1;
+    indices[2] = 3;
+    /* triangle bottom right */
+    indices[3] = 1;
+    indices[4] = 2;
+    indices[5] = 3;
+
+    rc->stride = sizeof(Vertex);
+    rc->offset = 0U;
+    rc->index_count = 6U;
+
+    desc.ByteWidth = sizeof(vertices);
+    desc.Usage = D3D11_USAGE_DYNAMIC;
+    desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
+    desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+    desc.MiscFlags = 0U;
+    desc.StructureByteStride = 0U;
+
+    sr_data.pSysMem = vertices;
+    sr_data.SysMemPitch = 0U;
+    sr_data.SysMemSlicePitch = 0U;
+
+    res =ID3D11Device_CreateBuffer(d3d->d3d_device,
+                                   &desc,
+                                   &sr_data,
+                                   &rc->vertex_buffer);
+    if (FAILED(res))
+    {
+        free(rc);
+        return NULL;
+    }
+
+    desc.ByteWidth = sizeof(indices);
+    desc.Usage = D3D11_USAGE_DYNAMIC;
+    desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
+    desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+    desc.MiscFlags = 0U;
+    desc.StructureByteStride = 0U;
+
+    sr_data.pSysMem = indices;
+    sr_data.SysMemPitch = 0U;
+    sr_data.SysMemSlicePitch = 0U;
+
+    res =ID3D11Device_CreateBuffer(d3d->d3d_device,
+                                   &desc,
+                                   &sr_data,
+                                   &rc->index_buffer);
+    if (FAILED(res))
+    {
+        ID3D11Buffer_Release(rc->vertex_buffer);
+        free(rc);
+        return NULL;
+    }
+
+    return rc;
+}
+
+void rectangle_free(Rect *r)
+{
+    if (!r)
+        return ;
+
+    ID3D11Buffer_Release(r->index_buffer);
+    ID3D11Buffer_Release(r->vertex_buffer);
+    free(r);
+}
+
 void d3d_render(D3d *d3d)
 {
 #ifdef HAVE_WIN10
@@ -795,6 +924,28 @@
 
     triangle_free(t);
 
+    Rect *r;
+
+    r = rectangle_new(d3d,
+                      520, 120,
+                      200, 100,
+                      0, 0, 255, 255);
+    ID3D11DeviceContext_IASetVertexBuffers(d3d->d3d_device_ctx,
+                                           0,
+                                           1,
+                                           &r->vertex_buffer,
+                                           &r->stride,
+                                           &r->offset);
+    ID3D11DeviceContext_IASetIndexBuffer(d3d->d3d_device_ctx,
+                                         r->index_buffer,
+                                         DXGI_FORMAT_R32_UINT,
+                                         0);
+    ID3D11DeviceContext_DrawIndexed(d3d->d3d_device_ctx,
+                                    r->index_count,
+                                    0, 0);
+
+    rectangle_free(r);
+
     /*
      * present frame, that is flip the back buffer and the front buffer
      * if no vsync, we present immediatly

When the program is launched, you should obtain a window with a yellow triangle and a blue rectangle, like this:

Last Author
vtorri
Last Edited
Apr 13 2022, 11:31 AM
Projects
Subscribers
raster