winapidirectx-11coordinate-systemsvertexdirect3d11

direct3d 11 and 2D: pass coordinates of a vertex as int and not float


My purpose is to write a backend of a toolkit using only Direct3D 11 for 2D (no additional library like Direct2D, or SpriteBatch or something else).

Note that it is the first time I use Direct3D, and I'm currently learning d3D 11.

So for now, I can display a triangle or rectangle of the color I want.

The vertex structure of my C code contains 2 float for the position and 4 unsigned char for the color. In my vertex shader, the vertex structure has 2 floats for the position of the vertex, and 4 floats for the color.

I have remarked that if I use DXGI_FORMAT_R8G8B8A8_UNORM for the color in my D3D11_INPUT_ELEMENT_DESC array, then the color is interpolated automatically from the values 0 to 255 to the values 0.0f to 1.0f. It seems resonnable when I read the documentation (DXGI Format anumeration, the description of _UNORM):

"Unsigned normalized integer; which is interpreted in a resource as an unsigned integer, and is interpreted in a shader as an unsigned normalized floating-point value in the range [0, 1]. All 0's maps to 0.0f, and all 1's maps to 1.0f. A sequence of evenly spaced floating-point values from 0.0f to 1.0f are represented. For instance, a 2-bit UNORM represents 0.0f, 1/3, 2/3, and 1.0f."

Or at least that is how I interpret this doc (I may be wrong). And the color of the triangle is correct.

What I would like to do is the same for pixels: if I pass an integer for the coordinates (x between 0 and the width of the window -1, and y between 0 and the height of the window - 1), then is it interpreted as the correct signed normalized floating-point value bythe vertex shader (-1.0f to 1.0f for x, and 1.0f to -1.0f for y). I tried several values in my Vertex C struct and D3D11_INPUT_ELEMENT_DESC array, without luck. So I have 2 questions:

  1. Is it possible ?
  2. If it is not possible, is it faster to convert the coordinates in the C code, or in the shader code (with the viewport as a constant buffer) ? See the macros XF and YF in the code below for the conversion from int to float.

Below is my complete code that displays a simple triangle, followed with the HLSL code for vertex and pixel shader. I use the C api of Direct3D. I support Win 7 and Win 10.

Source code:

/* Windows 10 */
#define _WIN32_WINNT 0x0A00

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

#include <stdio.h>

#ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
#endif

#include <windows.h>

/* C API for d3d11 */
#define COBJMACROS

#include <guiddef.h>

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

#include "d3d11_vs.h"
#include "d3d11_ps.h"

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

#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))

typedef struct Window Window;
typedef struct D3d D3d;

struct Window
{
    HINSTANCE instance;
    RECT rect;
    HWND win;
    D3d *d3d;
};

struct D3d
{
#ifdef HAVE_WIN10
    IDXGIFactory2 *dxgi_factory;
    IDXGISwapChain1 *dxgi_swapchain;
#else
    IDXGIFactory *dxgi_factory;
    IDXGISwapChain *dxgi_swapchain;
#endif
    ID3D11Device *d3d_device;
    ID3D11DeviceContext *d3d_device_ctx;
    ID3D11RenderTargetView *d3d_render_target_view;
    ID3D11InputLayout *d3d_input_layout;

    ID3D11VertexShader *d3d_vertex_shader;
    ID3D11PixelShader *d3d_pixel_shader;
    D3D11_VIEWPORT viewport;
    Window *win;
    unsigned int vsync : 1;
};

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

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

void d3d_render(D3d *d3d);

/************************* 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);
        }
        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);
}

/************************** 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 },
        { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 2 * sizeof(float), D3D11_INPUT_PER_VERTEX_DATA, 0 }
    };
#ifdef HAVE_WIN10
    DXGI_SWAP_CHAIN_DESC1 desc;
    DXGI_SWAP_CHAIN_FULLSCREEN_DESC desc_fs;
#else
    DXGI_SWAP_CHAIN_DESC desc;
#endif
    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;
    d3d->win = win;

    /* 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;

    /* single threaded for now */
    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)
     *
     * Settings are different in win 7 and win10
     */

    d3d_refresh_rate_get(d3d, &num, &den);

#ifdef HAVE_WIN10
    desc.Width = r.right - r.left;
    desc.Height = r.bottom - r.top;
    desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
    desc.Stereo = FALSE;
#else
    desc.BufferDesc.Width = r.right - r.left;
    desc.BufferDesc.Height = r.bottom - r.top;
    desc.BufferDesc.RefreshRate.Numerator = num;
    desc.BufferDesc.RefreshRate.Denominator = den;
    desc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;;
    desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
    desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
#endif
    desc.SampleDesc.Count = 1U;
    desc.SampleDesc.Quality = 0U;
    desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    desc.BufferCount = 2U;
#ifdef HAVE_WIN10
    desc.Scaling = DXGI_SCALING_NONE;
#else
    desc.OutputWindow = win->win;
    desc.Windowed = TRUE;
#endif
    desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
#ifdef HAVE_WIN10
    desc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
#endif
    desc.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,
                                               &desc_fs,
                                               NULL,
                                               &d3d->dxgi_swapchain);
#else
    res = IDXGIFactory_CreateSwapChain(d3d->dxgi_factory,
                                       (IUnknown *)d3d->d3d_device,
                                       &desc,
                                       &d3d->dxgi_swapchain);
#endif
    if (FAILED(res))
        goto release_d3d_device;

    /* Vertex shader */
    res = ID3D11Device_CreateVertexShader(d3d->d3d_device,
                                          d3d_vertex_shader,
                                          sizeof(d3d_vertex_shader),
                                          NULL,
                                          &d3d->d3d_vertex_shader);
    if (FAILED(res))
    {
        printf(" * CreateVertexShader() failed\n");
        goto release_dxgi_swapchain;
    }

    /* Pixel shader */
    res = ID3D11Device_CreatePixelShader(d3d->d3d_device,
                                         d3d_pixel_shader,
                                         sizeof(d3d_pixel_shader),
                                         NULL,
                                         &d3d->d3d_pixel_shader);
    if (FAILED(res))
    {
        printf(" * CreatePixelShader() failed\n");
        goto release_vertex_shader;
    }

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

    return d3d;

  release_pixel_shader:
    ID3D11PixelShader_Release(d3d->d3d_pixel_shader);
  release_vertex_shader:
    ID3D11VertexShader_Release(d3d->d3d_vertex_shader);
  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);
    ID3D11VertexShader_Release(d3d->d3d_vertex_shader);
    ID3D11InputLayout_Release(d3d->d3d_input_layout);
    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;

    /* 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;

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

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

    /* 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);
}

/*** 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,
                       unsigned char r,
                       unsigned char g,
                       unsigned char b,
                       unsigned char a)
{
    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[0].r = r;
    t->vertices[0].g = g;
    t->vertices[0].b = b;
    t->vertices[0].a = a;
    t->vertices[1].x = XF(w, x2);
    t->vertices[1].y = YF(h, y2);
    t->vertices[1].r = r;
    t->vertices[1].g = g;
    t->vertices[1].b = b;
    t->vertices[1].a = a;
    t->vertices[2].x = XF(w, x3);
    t->vertices[2].y = YF(h, y3);
    t->vertices[2].r = r;
    t->vertices[2].g = g;
    t->vertices[2].b = b;
    t->vertices[2].a = a;

    /* useful only for the rectangle later */
    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))
    {
        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 };
    RECT rect;
    HRESULT res;

    FCT;

    if (!GetClientRect(d3d->win->win, &rect))
    {
        return;
    }

    /* scene */
    Triangle *t;

    t = triangle_new(d3d,
                     rect.right - rect.left,
                     rect.bottom - rect.top,
                     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) */
    /* TRIANGLESTRIP only useful for the rectangle later */
    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 */
    ID3D11DeviceContext_VSSetShader(d3d->d3d_device_ctx,
                                    d3d->d3d_vertex_shader,
                                    NULL,
                                    0);
    /* pixel shader */
    ID3D11DeviceContext_PSSetShader(d3d->d3d_device_ctx,
                                    d3d->d3d_pixel_shader,
                                    NULL,
                                    0);

    /* set viewport in the Rasterizer Stage */
    ID3D11DeviceContext_RSSetViewports(d3d->d3d_device_ctx, 1U, &d3d->viewport);

    /* 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);
    }
}

int main()
{
    Window *win;
    D3d *d3d;

    /* remove scaling on HiDPI */
#ifdef  HAVE_WIN10
    SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_SYSTEM_AWARE);
#endif

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

    d3d = d3d_init(win, 0);
    if (!d3d)
        goto del_window;

    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);
    window_del(win);

    return 0;

  del_window:
    window_del(win);
    printf(" error\n");
    fflush(stdout);

    return 1;
}

Vertex shader:

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

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

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

Pixel shader:

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

float4 main(ps_input input) : SV_TARGET
{
    return input.color;
}

thank you


Solution

  • If you want to use pixel coordinates for your vertex, you can use one of those 2 formats :

    DXGI_FORMAT_R32G32_FLOAT (same as you use right now, pixel in floating point)
    DXGI_FORMAT_R32G32_UINT  (pixel coordinates as int, vertex shader position input becomes uint2 position : POSITION)
    

    if you use float, the float conversion is done in C side, if you use UINT, the conversion is done on the vertex shader side. Speed difference would need profiling, if number of vertices is low I'd expect it to be negligible.

    you can then easily remap those values into the -1 to 1 range in vertex shader (which is quite efficient), you only need to pass the inverse viewport size in a constant buffer.

    so your vertex shader becomes :

    struct vs_input
    {
        float2 position : POSITION;
        //uint2 position : POSITION; If you use UINT
        float4 color : COLOR;
    };
    
    struct ps_input
    {
        float4 position : SV_POSITION;
        float4 color : COLOR;
    };
    
    cbuffer cbViewport : register(b0)
    {
        float2 inverseViewportSize;
    }
    
    ps_input main(vs_input input )
    {
        ps_input output;
        float2 p = input.position; //if you use UINT, conversion is done here
        p *= inverseViewportSize;
        p *= 2.0f;
        p -= 1.0f;
        p.y *= -1.0f; (clip space is bottom to top, pixel is top to bottom)
        output.position = float4(p, 0.0f, 1.0f);
        output.color = input.color;
        return output;
    }