「技術」カテゴリーアーカイブ

DIRECT2Dを試す、その3

今回はFPSを固定化するコードを書きます。

大抵のゲームエンジンはエンジン側でFPSを制御していると思うのですが、

DirectXを使用する場合は実際に時間を計測して画面更新のタイミングを計らなくちゃいけないんですね。

サンプルソースは以下のサイトのコードを使用しています。

http://dioltista.blogspot.com/2019/04/c-directx11-fps.html

main.h
#pragma once

#include <windows.h>
#pragma comment(lib,"winmm.lib")

class Window
{
public:
    HRESULT InitWindow(HINSTANCE hInstance, int nCmdShow);
    void InitFps();
    void CalculationFps();
    void CalculationSleep();
    static HWND GethWnd();
    static double GetFps();
private:
    static HWND g_hWnd;
    static double g_dFps;
    LARGE_INTEGER Freq = { 0 };
    LARGE_INTEGER StartTime = { 0 };
    LARGE_INTEGER NowTime = { 0 };
    int iCount = 0;
    DWORD SleepTime = 0;
};

main.cpp

#include "Main.h"
#include "DirectX.h"

HWND Window::g_hWnd = nullptr;
double Window::g_dFps = 0;

//--------------------------------------------------------------------------------------
// 前方宣言
//--------------------------------------------------------------------------------------
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
{
    Window win;

    if (FAILED(win.InitWindow(hInstance, nCmdShow)))
        return 0;

    DirectX11 dx;

    if (FAILED(dx.InitDevice()))
        return 0;

    win.InitFps();

    // メインメッセージループ
    MSG msg = { 0 };
    while (WM_QUIT != msg.message)
    {
        if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        else
        {
            win.CalculationFps();

            dx.Render();

            win.CalculationSleep();
        }
    }

    return (int)msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    PAINTSTRUCT ps;
    HDC hdc;

    switch (message)
    {
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        EndPaint(hWnd, &ps);
        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        break;

    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }

    return 0;
}

HRESULT Window::InitWindow(HINSTANCE hInstance, int nCmdShow)
{
    WNDCLASSEX wcex;
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = nullptr;
    wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszMenuName = nullptr;
    wcex.lpszClassName = L"WindowClass";
    wcex.hIconSm = nullptr;
    if (!RegisterClassEx(&wcex))
        return E_FAIL;

    RECT rc = { 0, 0, 800, 600 };
    AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE);
    g_hWnd = CreateWindow(L"WindowClass", L"FPSの固定",
        WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
        CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left, rc.bottom - rc.top, nullptr, nullptr, hInstance,
        nullptr);
    if (!g_hWnd)
        return E_FAIL;

    ShowWindow(g_hWnd, nCmdShow);

    return S_OK;
}

HWND Window::GethWnd()
{
    return g_hWnd;
}

void Window::InitFps()
{

    QueryPerformanceFrequency(&Freq);
    QueryPerformanceCounter(&StartTime);//現在の時間を取得(1フレーム目)
}

void Window::CalculationFps()
{
    //FPSの計算
    if (iCount == 60)//カウントが60の時の処理
    {
        QueryPerformanceCounter(&NowTime);//現在の時間を取得(60フレーム目)
        //FPS = 1秒 / 1フレームの描画にかかる時間
        //    = 1000ms / ((現在の時間ms - 1フレーム目の時間ms) / 60フレーム)
        g_dFps = 1000.0 / (static_cast<double>((NowTime.QuadPart - StartTime.QuadPart) * 1000 / Freq.QuadPart) / 60.0);
        iCount = 0;//カウントを初期値に戻す
        StartTime = NowTime;//1フレーム目の時間を現在の時間にする
    }
    iCount++;//カウント+1
}

void Window::CalculationSleep()
{
    //Sleepさせる時間の計算
    QueryPerformanceCounter(&NowTime);//現在の時間を取得
    //Sleepさせる時間ms = 1フレーム目から現在のフレームまでの描画にかかるべき時間ms - 1フレーム目から現在のフレームまで実際にかかった時間ms
    //                  = (1000ms / 60)*フレーム数 - (現在の時間ms - 1フレーム目の時間ms)
    SleepTime = static_cast<DWORD>((1000.0 / 60.0) * iCount - (NowTime.QuadPart - StartTime.QuadPart) * 1000 / Freq.QuadPart);
    if (SleepTime > 0 && SleepTime < 18)//大きく変動がなければSleepTimeは1~17の間に納まる
    {
        timeBeginPeriod(1);
        Sleep(SleepTime);
        timeEndPeriod(1);
    }
    else//大きく変動があった場合
    {
        timeBeginPeriod(1);
        Sleep(1);
        timeEndPeriod(1);
    }
}

double Window::GetFps()
{
    return g_dFps;
}

DirectX.h

#pragma once

#pragma comment(lib,"d3d11.lib")
#pragma comment(lib,"d2d1.lib")
#pragma comment(lib,"dwrite.lib")
#include <d3d11_1.h>
#include <directxcolors.h>
#include <d2d1.h>
#include <dwrite.h>
#include <wchar.h>

class DirectX11
{
public:
    DirectX11();
    ~DirectX11();
    HRESULT InitDevice();
    void Render();
private:
    ID3D11Device* pd3dDevice;
    ID3D11Device1* pd3dDevice1;
    ID3D11DeviceContext* pImmediateContext;
    ID3D11DeviceContext1* pImmediateContext1;
    IDXGISwapChain* pSwapChain;
    IDXGISwapChain1* pSwapChain1;
    ID3D11RenderTargetView* pRenderTargetView;

    ID2D1Factory* pD2DFactory;
    IDWriteFactory* pDWriteFactory;
    IDWriteTextFormat* pTextFormat;
    ID2D1RenderTarget* pRT;
    ID2D1SolidColorBrush* pSolidBrush;
    IDXGISurface* pDXGISurface;
};

DirectX.cpp

#include "Main.h"
#include "DirectX.h"

DirectX11::DirectX11()
{
    pd3dDevice = nullptr;
    pd3dDevice1 = nullptr;
    pImmediateContext = nullptr;
    pImmediateContext1 = nullptr;
    pSwapChain = nullptr;
    pSwapChain1 = nullptr;
    pRenderTargetView = nullptr;

    pD2DFactory = nullptr;
    pDWriteFactory = nullptr;
    pTextFormat = nullptr;
    pRT = nullptr;
    pSolidBrush = nullptr;
    pDXGISurface = nullptr;
}

DirectX11::~DirectX11()
{
    if (pDXGISurface) pDXGISurface->Release();
    if (pSolidBrush) pSolidBrush->Release();
    if (pRT) pRT->Release();
    if (pTextFormat) pTextFormat->Release();
    if (pDWriteFactory) pDWriteFactory->Release();
    if (pD2DFactory) pD2DFactory->Release();

    if (pImmediateContext) pImmediateContext->ClearState();

    if (pRenderTargetView) pRenderTargetView->Release();
    if (pSwapChain1) pSwapChain1->Release();
    if (pSwapChain) pSwapChain->Release();
    if (pImmediateContext1) pImmediateContext1->Release();
    if (pImmediateContext) pImmediateContext->Release();
    if (pd3dDevice1) pd3dDevice1->Release();
    if (pd3dDevice) pd3dDevice->Release();
}

HRESULT DirectX11::InitDevice()
{
    HRESULT hr = S_OK;

    RECT rc;
    GetClientRect(Window::GethWnd(), &rc);
    UINT width = rc.right - rc.left;
    UINT height = rc.bottom - rc.top;

    UINT createDeviceFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;

#ifdef _DEBUG
    createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

    D3D_DRIVER_TYPE driverTypes[] =
    {
        D3D_DRIVER_TYPE_HARDWARE,
        D3D_DRIVER_TYPE_WARP,
        D3D_DRIVER_TYPE_REFERENCE,
    };
    UINT numDriverTypes = ARRAYSIZE(driverTypes);

    D3D_FEATURE_LEVEL featureLevels[] =
    {
        D3D_FEATURE_LEVEL_11_1,
        D3D_FEATURE_LEVEL_11_0,
        D3D_FEATURE_LEVEL_10_1,
        D3D_FEATURE_LEVEL_10_0,
    };
    UINT numFeatureLevels = ARRAYSIZE(featureLevels);

    D3D_DRIVER_TYPE g_driverType = D3D_DRIVER_TYPE_NULL;
    D3D_FEATURE_LEVEL g_featureLevel = D3D_FEATURE_LEVEL_11_0;
    for (UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++)
    {
        g_driverType = driverTypes[driverTypeIndex];
        hr = D3D11CreateDevice(nullptr, g_driverType, nullptr, createDeviceFlags, featureLevels, numFeatureLevels,
            D3D11_SDK_VERSION, &pd3dDevice, &g_featureLevel, &pImmediateContext);

        if (hr == E_INVALIDARG)
        {
            hr = D3D11CreateDevice(nullptr, g_driverType, nullptr, createDeviceFlags, &featureLevels[1], numFeatureLevels - 1,
                D3D11_SDK_VERSION, &pd3dDevice, &g_featureLevel, &pImmediateContext);
        }

        if (SUCCEEDED(hr))
            break;
    }
    if (FAILED(hr))
        return hr;

    IDXGIFactory1* dxgiFactory = nullptr;
    {
        IDXGIDevice* dxgiDevice = nullptr;
        hr = pd3dDevice->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&dxgiDevice));
        if (SUCCEEDED(hr))
        {
            IDXGIAdapter* adapter = nullptr;
            hr = dxgiDevice->GetAdapter(&adapter);
            if (SUCCEEDED(hr))
            {
                hr = adapter->GetParent(__uuidof(IDXGIFactory1), reinterpret_cast<void**>(&dxgiFactory));
                adapter->Release();
            }
            dxgiDevice->Release();
        }
    }
    if (FAILED(hr))
        return hr;

    IDXGIFactory2* dxgiFactory2 = nullptr;
    hr = dxgiFactory->QueryInterface(__uuidof(IDXGIFactory2), reinterpret_cast<void**>(&dxgiFactory2));
    if (dxgiFactory2)
    {
        hr = pd3dDevice->QueryInterface(__uuidof(ID3D11Device1), reinterpret_cast<void**>(&pd3dDevice1));
        if (SUCCEEDED(hr))
        {
            (void)pImmediateContext->QueryInterface(__uuidof(ID3D11DeviceContext1), reinterpret_cast<void**>(&pImmediateContext1));
        }

        DXGI_SWAP_CHAIN_DESC1 sd = {};
        sd.Width = width;
        sd.Height = height;
        sd.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
        sd.SampleDesc.Count = 1;
        sd.SampleDesc.Quality = 0;
        sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
        sd.BufferCount = 1;

        hr = dxgiFactory2->CreateSwapChainForHwnd(pd3dDevice, Window::GethWnd(), &sd, nullptr, nullptr, &pSwapChain1);
        if (SUCCEEDED(hr))
        {
            hr = pSwapChain1->QueryInterface(__uuidof(IDXGISwapChain), reinterpret_cast<void**>(&pSwapChain));
        }

        dxgiFactory2->Release();
    }
    else
    {
        DXGI_SWAP_CHAIN_DESC sd = {};
        sd.BufferCount = 1;
        sd.BufferDesc.Width = width;
        sd.BufferDesc.Height = height;
        sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
        sd.BufferDesc.RefreshRate.Numerator = 60;
        sd.BufferDesc.RefreshRate.Denominator = 1;
        sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
        sd.OutputWindow = Window::GethWnd();
        sd.SampleDesc.Count = 1;
        sd.SampleDesc.Quality = 0;
        sd.Windowed = TRUE;

        hr = dxgiFactory->CreateSwapChain(pd3dDevice, &sd, &pSwapChain);
    }

    dxgiFactory->MakeWindowAssociation(Window::GethWnd(), DXGI_MWA_NO_ALT_ENTER);

    dxgiFactory->Release();

    if (FAILED(hr))
        return hr;

    ID3D11Texture2D* pBackBuffer = nullptr;
    hr = pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&pBackBuffer));
    if (FAILED(hr))
        return hr;

    hr = pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &pRenderTargetView);
    pBackBuffer->Release();
    if (FAILED(hr))
        return hr;

    pImmediateContext->OMSetRenderTargets(1, &pRenderTargetView, nullptr);

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

    // Direct2D,DirectWriteの初期化
    hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &pD2DFactory);
    if (FAILED(hr))
        return hr;

    hr = pSwapChain->GetBuffer(0, IID_PPV_ARGS(&pDXGISurface));
    if (FAILED(hr))
        return hr;

    FLOAT dpiX;
    FLOAT dpiY;
    //pD2DFactory->GetDesktopDpi(&dpiX, &dpiY);
    dpiX = (FLOAT)GetDpiForWindow(GetDesktopWindow());
    dpiY = dpiX;

    D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED), dpiX, dpiY);

    hr = pD2DFactory->CreateDxgiSurfaceRenderTarget(pDXGISurface, &props, &pRT);
    if (FAILED(hr))
        return hr;

    hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown**>(&pDWriteFactory));
    if (FAILED(hr))
        return hr;

    //関数CreateTextFormat()
    //第1引数:フォント名(L"メイリオ", L"Arial", L"Meiryo UI"等)
    //第2引数:フォントコレクション(nullptr)
    //第3引数:フォントの太さ(DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_WEIGHT_BOLD等)
    //第4引数:フォントスタイル(DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STYLE_OBLIQUE, DWRITE_FONT_STYLE_ITALIC)
    //第5引数:フォントの幅(DWRITE_FONT_STRETCH_NORMAL,DWRITE_FONT_STRETCH_EXTRA_EXPANDED等)
    //第6引数:フォントサイズ(20, 30等)
    //第7引数:ロケール名(L"")
    //第8引数:テキストフォーマット(&g_pTextFormat)
    hr = pDWriteFactory->CreateTextFormat(L"メイリオ", nullptr, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, 20, L"", &pTextFormat);
    if (FAILED(hr))
        return hr;

    //関数SetTextAlignment()
    //第1引数:テキストの配置(DWRITE_TEXT_ALIGNMENT_LEADING:前, DWRITE_TEXT_ALIGNMENT_TRAILING:後, DWRITE_TEXT_ALIGNMENT_CENTER:中央)
    hr = pTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING);
    if (FAILED(hr))
        return hr;

    //関数CreateSolidColorBrush()
    //第1引数:フォント色(D2D1::ColorF(D2D1::ColorF::Black):黒, D2D1::ColorF(D2D1::ColorF(0.0f, 0.2f, 0.9f, 1.0f)):RGBA指定)
    hr = pRT->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), &pSolidBrush);
    if (FAILED(hr))
        return hr;

    return S_OK;
}

void DirectX11::Render()
{
    pImmediateContext->ClearRenderTargetView(pRenderTargetView, DirectX::Colors::Aquamarine);

    // テキストの描画
    WCHAR wcText1[256] = { 0 };

    swprintf(wcText1, 256, L"%lf", Window::GetFps());

    pRT->BeginDraw();
    pRT->DrawText(wcText1, ARRAYSIZE(wcText1) - 1, pTextFormat, D2D1::RectF(0, 0, 800, 20), pSolidBrush, D2D1_DRAW_TEXT_OPTIONS_NONE);
    pRT->EndDraw();

    pSwapChain->Present(0, 0);
}

DIRECT2Dを試す、その2

前回はDirectXの初期化だけでしたが、今回はいよいよDirect2Dを使用していきます。

サンプルコードはこちらを参考。

http://dioltista.blogspot.com/2019/04/c-directx11-direct2ddirectwrite_27.html

前回作成したWindowにテキストを表示します。

※main関数は大して変更していないので省略。

#pragma once

#pragma comment(lib,"d3d11.lib")
#pragma comment(lib,"d2d1.lib")
#pragma comment(lib,"dwrite.lib")
#include <d3d11_1.h>
#include <directxcolors.h>
#include <d2d1.h>
#include <dwrite.h>

class DirectX11
{
public:
    DirectX11();
    ~DirectX11();
    HRESULT InitDevice();
    void Render();
private:
    ID3D11Device* pd3dDevice;
    ID3D11Device1* pd3dDevice1;
    ID3D11DeviceContext* pImmediateContext;
    ID3D11DeviceContext1* pImmediateContext1;
    IDXGISwapChain* pSwapChain;
    IDXGISwapChain1* pSwapChain1;
    ID3D11RenderTargetView* pRenderTargetView;

    ID2D1Factory* pD2DFactory;
    IDWriteFactory* pDWriteFactory;
    IDWriteTextFormat* pTextFormat;
    ID2D1RenderTarget* pRT;
    ID2D1SolidColorBrush* pSolidBrush;
    IDXGISurface* pDXGISurface;
};
#include "Main.h"
#include "DirectX.h"

DirectX11::DirectX11()
{
    pd3dDevice = nullptr;
    pd3dDevice1 = nullptr;
    pImmediateContext = nullptr;
    pImmediateContext1 = nullptr;
    pSwapChain = nullptr;
    pSwapChain1 = nullptr;
    pRenderTargetView = nullptr;

    pD2DFactory = nullptr;
    pDWriteFactory = nullptr;
    pTextFormat = nullptr;
    pRT = nullptr;
    pSolidBrush = nullptr;
    pDXGISurface = nullptr;
}

DirectX11::~DirectX11()
{
    if (pDXGISurface) pDXGISurface->Release();
    if (pSolidBrush) pSolidBrush->Release();
    if (pRT) pRT->Release();
    if (pTextFormat) pTextFormat->Release();
    if (pDWriteFactory) pDWriteFactory->Release();
    if (pD2DFactory) pD2DFactory->Release();

    if (pImmediateContext) pImmediateContext->ClearState();

    if (pRenderTargetView) pRenderTargetView->Release();
    if (pSwapChain1) pSwapChain1->Release();
    if (pSwapChain) pSwapChain->Release();
    if (pImmediateContext1) pImmediateContext1->Release();
    if (pImmediateContext) pImmediateContext->Release();
    if (pd3dDevice1) pd3dDevice1->Release();
    if (pd3dDevice) pd3dDevice->Release();
}

HRESULT DirectX11::InitDevice()
{
    HRESULT hr = S_OK;

    RECT rc;
    GetClientRect(Window::GethWnd(), &rc);
    UINT width = rc.right - rc.left;
    UINT height = rc.bottom - rc.top;

    UINT createDeviceFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;

#ifdef _DEBUG
    createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

    D3D_DRIVER_TYPE driverTypes[] =
    {
        D3D_DRIVER_TYPE_HARDWARE,
        D3D_DRIVER_TYPE_WARP,
        D3D_DRIVER_TYPE_REFERENCE,
    };
    UINT numDriverTypes = ARRAYSIZE(driverTypes);

    D3D_FEATURE_LEVEL featureLevels[] =
    {
        D3D_FEATURE_LEVEL_11_1,
        D3D_FEATURE_LEVEL_11_0,
        D3D_FEATURE_LEVEL_10_1,
        D3D_FEATURE_LEVEL_10_0,
    };
    UINT numFeatureLevels = ARRAYSIZE(featureLevels);

    D3D_DRIVER_TYPE g_driverType = D3D_DRIVER_TYPE_NULL;
    D3D_FEATURE_LEVEL g_featureLevel = D3D_FEATURE_LEVEL_11_0;
    for (UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++)
    {
        g_driverType = driverTypes[driverTypeIndex];
        hr = D3D11CreateDevice(nullptr, g_driverType, nullptr, createDeviceFlags, featureLevels, numFeatureLevels,
            D3D11_SDK_VERSION, &pd3dDevice, &g_featureLevel, &pImmediateContext);

        if (hr == E_INVALIDARG)
        {
            hr = D3D11CreateDevice(nullptr, g_driverType, nullptr, createDeviceFlags, &featureLevels[1], numFeatureLevels - 1,
                D3D11_SDK_VERSION, &pd3dDevice, &g_featureLevel, &pImmediateContext);
        }

        if (SUCCEEDED(hr))
            break;
    }
    if (FAILED(hr))
        return hr;

    IDXGIFactory1* dxgiFactory = nullptr;
    {
        IDXGIDevice* dxgiDevice = nullptr;
        hr = pd3dDevice->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&dxgiDevice));
        if (SUCCEEDED(hr))
        {
            IDXGIAdapter* adapter = nullptr;
            hr = dxgiDevice->GetAdapter(&adapter);
            if (SUCCEEDED(hr))
            {
                hr = adapter->GetParent(__uuidof(IDXGIFactory1), reinterpret_cast<void**>(&dxgiFactory));
                adapter->Release();
            }
            dxgiDevice->Release();
        }
    }
    if (FAILED(hr))
        return hr;

    IDXGIFactory2* dxgiFactory2 = nullptr;
    hr = dxgiFactory->QueryInterface(__uuidof(IDXGIFactory2), reinterpret_cast<void**>(&dxgiFactory2));
    if (dxgiFactory2)
    {
        hr = pd3dDevice->QueryInterface(__uuidof(ID3D11Device1), reinterpret_cast<void**>(&pd3dDevice1));
        if (SUCCEEDED(hr))
        {
            (void)pImmediateContext->QueryInterface(__uuidof(ID3D11DeviceContext1), reinterpret_cast<void**>(&pImmediateContext1));
        }

        DXGI_SWAP_CHAIN_DESC1 sd = {};
        sd.Width = width;
        sd.Height = height;
        sd.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
        sd.SampleDesc.Count = 1;
        sd.SampleDesc.Quality = 0;
        sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
        sd.BufferCount = 1;

        hr = dxgiFactory2->CreateSwapChainForHwnd(pd3dDevice, Window::GethWnd(), &sd, nullptr, nullptr, &pSwapChain1);
        if (SUCCEEDED(hr))
        {
            hr = pSwapChain1->QueryInterface(__uuidof(IDXGISwapChain), reinterpret_cast<void**>(&pSwapChain));
        }

        dxgiFactory2->Release();
    }
    else
    {
        DXGI_SWAP_CHAIN_DESC sd = {};
        sd.BufferCount = 1;
        sd.BufferDesc.Width = width;
        sd.BufferDesc.Height = height;
        sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
        sd.BufferDesc.RefreshRate.Numerator = 60;
        sd.BufferDesc.RefreshRate.Denominator = 1;
        sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
        sd.OutputWindow = Window::GethWnd();
        sd.SampleDesc.Count = 1;
        sd.SampleDesc.Quality = 0;
        sd.Windowed = TRUE;

        hr = dxgiFactory->CreateSwapChain(pd3dDevice, &sd, &pSwapChain);
    }

    dxgiFactory->MakeWindowAssociation(Window::GethWnd(), DXGI_MWA_NO_ALT_ENTER);

    dxgiFactory->Release();

    if (FAILED(hr))
        return hr;

    ID3D11Texture2D* pBackBuffer = nullptr;
    hr = pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&pBackBuffer));
    if (FAILED(hr))
        return hr;

    hr = pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &pRenderTargetView);
    pBackBuffer->Release();
    if (FAILED(hr))
        return hr;

    pImmediateContext->OMSetRenderTargets(1, &pRenderTargetView, nullptr);

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

    // Direct2D,DirectWriteの初期化
    hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &pD2DFactory);
    if (FAILED(hr))
        return hr;

    hr = pSwapChain->GetBuffer(0, IID_PPV_ARGS(&pDXGISurface));
    if (FAILED(hr))
        return hr;

    FLOAT dpiX;
    FLOAT dpiY;
    //pD2DFactory->GetDesktopDpi(&dpiX, &dpiY);★
    dpiX = (FLOAT)GetDpiForWindow(GetDesktopWindow());
    dpiY = dpiX;

    D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED), dpiX, dpiY);

    hr = pD2DFactory->CreateDxgiSurfaceRenderTarget(pDXGISurface, &props, &pRT);
    if (FAILED(hr))
        return hr;

    hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown**>(&pDWriteFactory));
    if (FAILED(hr))
        return hr;

    //関数CreateTextFormat()
    //第1引数:フォント名(L"メイリオ", L"Arial", L"Meiryo UI"等)
    //第2引数:フォントコレクション(nullptr)
    //第3引数:フォントの太さ(DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_WEIGHT_BOLD等)
    //第4引数:フォントスタイル(DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STYLE_OBLIQUE, DWRITE_FONT_STYLE_ITALIC)
    //第5引数:フォントの幅(DWRITE_FONT_STRETCH_NORMAL,DWRITE_FONT_STRETCH_EXTRA_EXPANDED等)
    //第6引数:フォントサイズ(20, 30等)
    //第7引数:ロケール名(L"")
    //第8引数:テキストフォーマット(&g_pTextFormat)
    hr = pDWriteFactory->CreateTextFormat(L"メイリオ", nullptr, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, 20, L"", &pTextFormat);
    if (FAILED(hr))
        return hr;

    //関数SetTextAlignment()
    //第1引数:テキストの配置(DWRITE_TEXT_ALIGNMENT_LEADING:前, DWRITE_TEXT_ALIGNMENT_TRAILING:後, DWRITE_TEXT_ALIGNMENT_CENTER:中央)
    hr = pTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING);
    if (FAILED(hr))
        return hr;

    //関数CreateSolidColorBrush()
    //第1引数:フォント色(D2D1::ColorF(D2D1::ColorF::Black):黒, D2D1::ColorF(D2D1::ColorF(0.0f, 0.2f, 0.9f, 1.0f)):RGBA指定)
    hr = pRT->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), &pSolidBrush);
    if (FAILED(hr))
        return hr;

    return S_OK;
}

void DirectX11::Render()
{
    pImmediateContext->ClearRenderTargetView(pRenderTargetView, DirectX::Colors::Aquamarine);

    // テキストの描画
    WCHAR wcText1[] = L" 親譲りの無鉄砲で小供の時から損ばかりしている。小学校に居る時分学校の二階から";
    WCHAR wcText2[] = L"飛び降りて一週間ほど腰を抜かした事がある。なぜそんな無闇をしたと聞く人があるか";
    WCHAR wcText3[] = L"も知れぬ。別段深い理由でもない。新築の二階から首を出していたら、同級生の一人が";
    WCHAR wcText4[] = L"冗談に、いくら威張っても、そこから飛び降りる事は出来まい。弱虫やーい。と囃した";
    WCHAR wcText5[] = L"からである。小使に負ぶさって帰って来た時、おやじが大きな眼をして二階ぐらいから";
    WCHAR wcText6[] = L"飛び降りて腰を抜かす奴があるかと云ったから、この次は抜かさずに飛んで見せますと";
    WCHAR wcText7[] = L"答えた。";

    pRT->BeginDraw();
    pSolidBrush->SetColor(D2D1::ColorF(D2D1::ColorF::Blue));//フォント色変更:青
    pRT->DrawText(wcText1, ARRAYSIZE(wcText1) - 1, pTextFormat, D2D1::RectF(0, 0, 800, 20), pSolidBrush, D2D1_DRAW_TEXT_OPTIONS_NONE);
    pRT->DrawText(wcText2, ARRAYSIZE(wcText2) - 1, pTextFormat, D2D1::RectF(0, 20, 800, 40), pSolidBrush, D2D1_DRAW_TEXT_OPTIONS_NONE);
    pSolidBrush->SetOpacity(0.7f);//フォントの透明度変更:70%
    pRT->DrawText(wcText3, ARRAYSIZE(wcText3) - 1, pTextFormat, D2D1::RectF(0, 40, 800, 60), pSolidBrush, D2D1_DRAW_TEXT_OPTIONS_NONE);
    pSolidBrush->SetColor(D2D1::ColorF(D2D1::ColorF::Black));//フォント色変更:黒
    pRT->DrawText(wcText4, ARRAYSIZE(wcText4) - 1, pTextFormat, D2D1::RectF(0, 60, 800, 80), pSolidBrush, D2D1_DRAW_TEXT_OPTIONS_NONE);
    pRT->DrawText(wcText5, ARRAYSIZE(wcText5) - 1, pTextFormat, D2D1::RectF(0, 80, 800, 100), pSolidBrush, D2D1_DRAW_TEXT_OPTIONS_NONE);
    pSolidBrush->SetOpacity(1.0f);//フォントの透明度変更:100%
    pRT->DrawText(wcText6, ARRAYSIZE(wcText6) - 1, pTextFormat, D2D1::RectF(0, 100, 800, 120), pSolidBrush, D2D1_DRAW_TEXT_OPTIONS_NONE);
    pRT->DrawText(wcText7, ARRAYSIZE(wcText7) - 1, pTextFormat, D2D1::RectF(0, 120, 800, 140), pSolidBrush, D2D1_DRAW_TEXT_OPTIONS_NONE);
    pRT->EndDraw();

    pSwapChain->Present(0, 0);
}

サンプルと違うのは★の箇所。

GetDesktopDpi()を使用するのは非推奨らしく、ビルドエラーとなります。

そこで参照したのは以下のページ。

https://learn.microsoft.com/en-us/answers/questions/170411/creating-a-simple-direct2d-application-id2d1factor

    dpiX = (FLOAT)GetDpiForWindow(GetDesktopWindow());
    dpiY = dpiX;

に置き換えることでビルドが通ります。

Direct2Dを試す

まだ案件が決まったわけでは無いので、

ネットでよい記事が無いかを探していたところ、

こちらが見つかりました。

http://dioltista.blogspot.com/2019/04/c-directx11_25.html

#pragma once

#include <windows.h>

class Window
{
public:
    HRESULT InitWindow(HINSTANCE hInstance, int nCmdShow);
    static HWND GethWnd();
private:
    static HWND g_hWnd;
};
#include "Main.h"
#include "DirectX.h"

HWND Window::g_hWnd = nullptr;

//--------------------------------------------------------------------------------------
// 前方宣言
//--------------------------------------------------------------------------------------
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
{
    Window win;

    if (FAILED(win.InitWindow(hInstance, nCmdShow)))
        return 0;

    DirectX11 dx;

    if (FAILED(dx.InitDevice()))
        return 0;

    // メインメッセージループ
    MSG msg = { 0 };
    while (WM_QUIT != msg.message)
    {
        if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        else
        {
            dx.Render();
        }
    }

    return (int)msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    PAINTSTRUCT ps;
    HDC hdc;

    switch (message)
    {
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        EndPaint(hWnd, &ps);
        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        break;

    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }

    return 0;
}

HRESULT Window::InitWindow(HINSTANCE hInstance, int nCmdShow)
{
    WNDCLASSEX wcex;
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = nullptr;
    wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszMenuName = nullptr;
    wcex.lpszClassName = L"WindowClass";
    wcex.hIconSm = nullptr;
    if (!RegisterClassEx(&wcex))
        return E_FAIL;

    RECT rc = { 0, 0, 800, 600 };
    AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE);
    g_hWnd = CreateWindow(L"WindowClass", L"DirectX11の初期化(クラス化)",
        WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
        CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left, rc.bottom - rc.top, nullptr, nullptr, hInstance,
        nullptr);
    if (!g_hWnd)
        return E_FAIL;

    ShowWindow(g_hWnd, nCmdShow);

    return S_OK;
}

HWND Window::GethWnd()
{
    return g_hWnd;
}
#pragma once

#pragma comment(lib,"d3d11.lib")
#include <d3d11_1.h>
#include <directxcolors.h>

class DirectX11
{
public:
    DirectX11();
    ~DirectX11();
    HRESULT InitDevice();
    void Render();
private:
    ID3D11Device* pd3dDevice;
    ID3D11Device1* pd3dDevice1;
    ID3D11DeviceContext* pImmediateContext;
    ID3D11DeviceContext1* pImmediateContext1;
    IDXGISwapChain* pSwapChain;
    IDXGISwapChain1* pSwapChain1;
    ID3D11RenderTargetView* pRenderTargetView;
};
#include "Main.h"
#include "DirectX.h"

DirectX11::DirectX11()
{
    pd3dDevice = nullptr;
    pd3dDevice1 = nullptr;
    pImmediateContext = nullptr;
    pImmediateContext1 = nullptr;
    pSwapChain = nullptr;
    pSwapChain1 = nullptr;
    pRenderTargetView = nullptr;
}

DirectX11::~DirectX11()
{
    if (pImmediateContext) pImmediateContext->ClearState();

    if (pRenderTargetView) pRenderTargetView->Release();
    if (pSwapChain1) pSwapChain1->Release();
    if (pSwapChain) pSwapChain->Release();
    if (pImmediateContext1) pImmediateContext1->Release();
    if (pImmediateContext) pImmediateContext->Release();
    if (pd3dDevice1) pd3dDevice1->Release();
    if (pd3dDevice) pd3dDevice->Release();
}

HRESULT DirectX11::InitDevice()
{
    HRESULT hr = S_OK;

    RECT rc;
    GetClientRect(Window::GethWnd(), &rc);
    UINT width = rc.right - rc.left;
    UINT height = rc.bottom - rc.top;

    UINT createDeviceFlags = 0;
#ifdef _DEBUG
    createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

    D3D_DRIVER_TYPE driverTypes[] =
    {
        D3D_DRIVER_TYPE_HARDWARE,
        D3D_DRIVER_TYPE_WARP,
        D3D_DRIVER_TYPE_REFERENCE,
    };
    UINT numDriverTypes = ARRAYSIZE(driverTypes);

    D3D_FEATURE_LEVEL featureLevels[] =
    {
        D3D_FEATURE_LEVEL_11_1,
        D3D_FEATURE_LEVEL_11_0,
        D3D_FEATURE_LEVEL_10_1,
        D3D_FEATURE_LEVEL_10_0,
    };
    UINT numFeatureLevels = ARRAYSIZE(featureLevels);

    D3D_DRIVER_TYPE g_driverType = D3D_DRIVER_TYPE_NULL;
    D3D_FEATURE_LEVEL g_featureLevel = D3D_FEATURE_LEVEL_11_0;
    for (UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++)
    {
        g_driverType = driverTypes[driverTypeIndex];
        hr = D3D11CreateDevice(nullptr, g_driverType, nullptr, createDeviceFlags, featureLevels, numFeatureLevels,
            D3D11_SDK_VERSION, &pd3dDevice, &g_featureLevel, &pImmediateContext);

        if (hr == E_INVALIDARG)
        {
            hr = D3D11CreateDevice(nullptr, g_driverType, nullptr, createDeviceFlags, &featureLevels[1], numFeatureLevels - 1,
                D3D11_SDK_VERSION, &pd3dDevice, &g_featureLevel, &pImmediateContext);
        }

        if (SUCCEEDED(hr))
            break;
    }
    if (FAILED(hr))
        return hr;

    IDXGIFactory1* dxgiFactory = nullptr;
    {
        IDXGIDevice* dxgiDevice = nullptr;
        hr = pd3dDevice->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&dxgiDevice));
        if (SUCCEEDED(hr))
        {
            IDXGIAdapter* adapter = nullptr;
            hr = dxgiDevice->GetAdapter(&adapter);
            if (SUCCEEDED(hr))
            {
                hr = adapter->GetParent(__uuidof(IDXGIFactory1), reinterpret_cast<void**>(&dxgiFactory));
                adapter->Release();
            }
            dxgiDevice->Release();
        }
    }
    if (FAILED(hr))
        return hr;

    IDXGIFactory2* dxgiFactory2 = nullptr;
    hr = dxgiFactory->QueryInterface(__uuidof(IDXGIFactory2), reinterpret_cast<void**>(&dxgiFactory2));
    if (dxgiFactory2)
    {
        hr = pd3dDevice->QueryInterface(__uuidof(ID3D11Device1), reinterpret_cast<void**>(&pd3dDevice1));
        if (SUCCEEDED(hr))
        {
            (void)pImmediateContext->QueryInterface(__uuidof(ID3D11DeviceContext1), reinterpret_cast<void**>(&pImmediateContext1));
        }

        DXGI_SWAP_CHAIN_DESC1 sd = {};
        sd.Width = width;
        sd.Height = height;
        sd.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
        sd.SampleDesc.Count = 1;
        sd.SampleDesc.Quality = 0;
        sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
        sd.BufferCount = 1;

        hr = dxgiFactory2->CreateSwapChainForHwnd(pd3dDevice, Window::GethWnd(), &sd, nullptr, nullptr, &pSwapChain1);
        if (SUCCEEDED(hr))
        {
            hr = pSwapChain1->QueryInterface(__uuidof(IDXGISwapChain), reinterpret_cast<void**>(&pSwapChain));
        }

        dxgiFactory2->Release();
    }
    else
    {
        DXGI_SWAP_CHAIN_DESC sd = {};
        sd.BufferCount = 1;
        sd.BufferDesc.Width = width;
        sd.BufferDesc.Height = height;
        sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
        sd.BufferDesc.RefreshRate.Numerator = 60;
        sd.BufferDesc.RefreshRate.Denominator = 1;
        sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
        sd.OutputWindow = Window::GethWnd();
        sd.SampleDesc.Count = 1;
        sd.SampleDesc.Quality = 0;
        sd.Windowed = TRUE;

        hr = dxgiFactory->CreateSwapChain(pd3dDevice, &sd, &pSwapChain);
    }

    dxgiFactory->MakeWindowAssociation(Window::GethWnd(), DXGI_MWA_NO_ALT_ENTER);

    dxgiFactory->Release();

    if (FAILED(hr))
        return hr;

    ID3D11Texture2D* pBackBuffer = nullptr;
    hr = pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&pBackBuffer));
    if (FAILED(hr))
        return hr;

    hr = pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &pRenderTargetView);
    pBackBuffer->Release();
    if (FAILED(hr))
        return hr;

    pImmediateContext->OMSetRenderTargets(1, &pRenderTargetView, nullptr);

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

    return S_OK;
}

void DirectX11::Render()
{
    pImmediateContext->ClearRenderTargetView(pRenderTargetView, DirectX::Colors::Aquamarine);
    pSwapChain->Present(0, 0);
}

今使用しているVisual Studio2022でも動作しました。

とはいえ、やっていることはDirectXを初期化して表示しているだけなので、

ここまではほぼテンプレの処理。

大事なのは、ここに何を書くか、という処理ですね。

AVXとはなんぞや?という話

エージェントから案件の紹介を頂きまして、

ほぼ即答で「やります」と回答したのですが、

お仕事の内容が映像系ソフトウェアということで、

Direct2DやAVXのスキル必須とのことでした。

AVXとは??

ChatGTPに質問したら、

x86アーキテクチャのCPUには大量のデータを計算する命令セットが搭載されているとのこと。

で、使い方なんですが、

専用命令セットということなので、アセンブラのコードを使用するのかなと思っていたら、

C++にはその命令セットを使用するためのライブラリを使用可能にするヘッダーファイルがあるらしい。

これはChatGTPが教えてくれたサンプルプログラム。

#include <immintrin.h>
#include <iostream>

int main() {
    constexpr int size = 8;
    float a[size] = { 1, 2, 3, 4, 5, 6, 7, 8 };
    float b[size] = { 8, 7, 6, 5, 4, 3, 2, 1 };
    float c[size];

    __m256 a_vec = _mm256_loadu_ps(a);
    __m256 b_vec = _mm256_loadu_ps(b);
    __m256 c_vec = _mm256_add_ps(a_vec, b_vec);
    _mm256_storeu_ps(c, c_vec);

    for (int i = 0; i < size; i++) {
        std::cout << c[i] << " ";
    }
}

immintrin.hというのがAVX拡張命令を使用するためのヘッダーファイルで、

m256とある関数がその命令セットを使用する関数になります。

どんな関数が使えるのはこちらのサイトを参照した方が解りやすい。

https://chryswoods.com/vector_c++/immintrin.html

英語だけど中堅プログラマーなら理解できる内容でしょ。

RUST勉強中、1/17の積み上げ

サブスクリプション処理を実装していきます。

icedではサブスクリプションを利用することによって、非同期処理側からメッセージを送信する事ができるようです。

サブスクリプションを利用するにはレシピを作成しなければなりません。

pub struct Timer {
    duration: Duration,
}

impl Timer {
    fn new(duration: Duration) -> Timer {
        Timer { duration: duration }
    }
}

impl<H, E> iced_native::subscription::Recipe<H, E> for Timer
where
    H: std::hash::Hasher,
{
    type Output = Instant;

    fn hash(&self, state: &mut H) {
        use std::hash::Hash;
        std::any::TypeId::of::<Self>().hash(state);
        self.duration.hash(state);
    }

    fn stream(
        self: Box<Self>,
        _input: futures::stream::BoxStream<'static, E>,
    ) -> futures::stream::BoxStream<'static, Self::Output> {
        use futures::stream::StreamExt;
        async_std::stream::interval(self.duration)
            .map(|_| Instant::now())
            .boxed()
    }
}

もうここまで来たら何が何だか解りません。

後でRustの文法見直す。

サブスクリプション処理を作成し、Executorの設定を書き直せばメッセージ送信処理は完成です。

impl Application for GUI {
    type Executor = executor::Default;
    type Message = Message;
    type Flags = ();
    fn subscription(&self) -> Subscription<Message> {
        let timer = Timer::new(Duration::from_millis(MILLISEC / FPS));
        iced::Subscription::from_recipe(timer).map(|_| Message::Update)
    }

あとは受信側を作成。

コードはこうなりました。

use iced::{
    button, executor, Align, Application, Button, Column, Command, Element, Font,
    HorizontalAlignment, Length, Row, Settings, Subscription, Text,
};
use iced_futures::{self, futures};
use std::time::{Duration, Instant};

const FONT: Font = Font::External {
    name: "PixelMplus12-Regular",
    bytes: include_bytes!("../rsc/PixelMplus12-Regular.ttf"),
};

const FPS: u64 = 30;
const MILLISEC: u64 = 1000;
const MINUTE: u64 = 60;
const HOUR: u64 = 60 * MINUTE;

#[derive(Debug, Clone)]
pub enum Message {
    Start,
    Stop,
    Reset,
    Update,
}

pub enum TickState {
    Stopped,
    Ticking,
}

pub struct Timer {
    duration: Duration,
}

impl Timer {
    fn new(duration: Duration) -> Timer {
        Timer { duration: duration }
    }
}

impl<H, E> iced_native::subscription::Recipe<H, E> for Timer
where
    H: std::hash::Hasher,
{
    type Output = Instant;

    fn hash(&self, state: &mut H) {
        use std::hash::Hash;
        std::any::TypeId::of::<Self>().hash(state);
        self.duration.hash(state);
    }

    fn stream(
        self: Box<Self>,
        _input: futures::stream::BoxStream<'static, E>,
    ) -> futures::stream::BoxStream<'static, Self::Output> {
        use futures::stream::StreamExt;
        async_std::stream::interval(self.duration)
            .map(|_| Instant::now())
            .boxed()
    }
}

struct GUI {
    last_update: Instant,
    total_duration: Duration,
    tick_state: TickState,
    start_stop_button_state: button::State,
    reset_button_state: button::State,
}

impl Application for GUI {
    type Executor = executor::Default;
    type Message = Message;
    type Flags = ();

    fn new(_flags: ()) -> (GUI, Command<Self::Message>) {
        (
            GUI {
                last_update: Instant::now(),
                total_duration: Duration::default(),
                tick_state: TickState::Stopped,
                start_stop_button_state: button::State::new(),
                reset_button_state: button::State::new(),
            },
            Command::none(),
        )
    }

    fn title(&self) -> String {
        String::from("DEMO")
    }

    fn update(&mut self, message: Self::Message) -> Command<Self::Message> {
        match message {
            Message::Start => {
                self.tick_state = TickState::Ticking;
                self.last_update = Instant::now();
            }
            Message::Stop => {
                self.tick_state = TickState::Stopped;
                self.last_update += Instant::now() - self.last_update;
            }
            Message::Reset => {
                self.last_update = Instant::now();
                self.total_duration = Duration::default();
            }
            Message::Update => match self.tick_state {
                TickState::Ticking => {
                    let now_update = Instant::now();
                    self.total_duration += now_update - self.last_update;
                    self.last_update = now_update;
                }
                _ => {}
            },
        }
        Command::none()
    }

    fn view(&mut self) -> Element<Self::Message> {
        let seconds = self.total_duration.as_secs();
        let duration_text = format!(
            "{:0>2}:{:0>2}:{:0>2}.{:0>2}",
            seconds / HOUR,
            (seconds % HOUR) / MINUTE,
            seconds % MINUTE,
            self.total_duration.subsec_millis() / 10,
        );

        let start_stop_text = match self.tick_state {
            TickState::Stopped => Text::new("Start")
                .horizontal_alignment(HorizontalAlignment::Center)
                .font(FONT),
            TickState::Ticking => Text::new("Stop")
                .horizontal_alignment(HorizontalAlignment::Center)
                .font(FONT),
        };

        let start_stop_message = match self.tick_state {
            TickState::Stopped => Message::Start,
            TickState::Ticking => Message::Stop,
        };
        
        let tick_text = Text::new(duration_text).font(FONT).size(60);
        let start_stop_button = Button::new(&mut self.start_stop_button_state, start_stop_text)
            .min_width(80)
            .on_press(start_stop_message);
        let reset_button = Button::new(
            &mut self.reset_button_state,
            Text::new("Reset")
                .horizontal_alignment(HorizontalAlignment::Center)
                .font(FONT),
        )
            .min_width(80)
            .on_press(Message::Reset);

        Column::new()
            .push(tick_text)
            .push(
                Row::new()
                    .push(start_stop_button)
                    .push(reset_button)
                    .spacing(10),
            )
            .spacing(10)
            .padding(10)
            .width(Length::Fill)
            .height(Length::Fill)
            .align_items(Align::Center)
            .into()
    }

    fn subscription(&self) -> Subscription<Message> {
        let timer = Timer::new(Duration::from_millis(MILLISEC / FPS));
        iced::Subscription::from_recipe(timer).map(|_| Message::Update)
    }
}

fn main() {
    let mut settings = Settings::default();
    settings.window.size = (400u32, 120u32);
    GUI::run(settings);
}

【数学】【PYTHON】ヘロンの公式

2ヶ月開いてしまった・・・

ぼちぼち再開していきます。

ヘロンの公式は三角形の3辺の長さから面積を求める公式です。

三角形の三辺の長さをそれぞれa,b,cとすると、計算式は以下になります。

import math

x = [1, 3, 6]
y = [5, 1, 4]

a = math.sqrt((x[1] - x[0])**2 + (y[1] - y[0])**2)
b = math.sqrt((x[2] - x[1])**2 + (y[2] - y[1])**2)
c = math.sqrt((x[2] - x[0])**2 + (y[2] - y[0])**2)
s = (a + b + c) / 2

ans = math.sqrt(s * (s - a) * (s - b) * (s - c))

print(ans)
$ python3 heron_area.py 
8.999999999999996

A(1,5),B(3,1),C(6,4)、a=辺AB,b=辺BC,c=辺ACとすると、

面積はおよそ9となります。

【ラズパイ】e-Paperを使ってみる。

今回購入したのはこちら。

e-Paperです。

HATになっているので、GPIOのピンを刺すと、ラズパイゼロの大きさにピッタリはまります。

使用する方法はこちらに書いてあるのですが。

https://www.waveshare.com/wiki/2.13inch_e-Paper_HAT_(B)_Manual#Introduction

使用するためには、ラズパイの設定でSPIを有効にする必要があります。

sudo raspi-config

必要なPythonモジュールをインストール

sudo apt-get update
sudo apt-get install python3-pip
sudo apt-get install python3-pil
sudo apt-get install python3-numpy
sudo pip3 install RPi.GPIO
sudo pip3 install spidev

サンプルのダウンロードはGitHubからチェックアウトする方法と、

圧縮ファイルをダウンロードして解凍する方法が記載されていますが、

どちらか一方だけで大丈夫です。

git clone https://github.com/waveshare/e-Paper.git
cd e-Paper/RaspberryPi_JetsonNano/

あとはデモ用のプログラムを実行するだけなのですが、

美味く実行できない事件が発生。

cd python/examples/
python3 epd_2in13b_V4_test.py

おそらくサンプルのバグだと思うのですが、

python3 epd_2in13b_V3_test.py

で上手く実行できました。

次回はサンプルコードの解析を行いたいと思います。

【C#】【アルゴリズム】階段の上り型(動的計画法の例)

以下の様な、「階段の上り方」の問題です。

それぞれの段に行くまでに何通りあるか、というのを考えます。

0段目はスタート地点なので、1通り、

1段目は0段目からでしか行けないので1通り、

2段目は0段目から来るケースと1段目からケースがあるから、0段目と1段目の合計値、

それ以降はi段目はi-1段目とi-2段目の合計値となります。

最終的にN段目の合計値を出力すればOKです。

        public int UpsideStairs(int N)
        {
            int[] dp = new int[45];

            for(int i = 0; i <= N; i++)
            {
                if(i <= 1)
                {
                    dp[i] = 1;
                }
                else
                {
                    dp[i] = dp[i - 1] + dp[i - 2];
                }
            }

            return dp[N];
        }

【数学】【PYTHON】点と直線の距離を計算する

点(x1, y1)と直線ax+by+c=0の距離を求める公式は、

で求められます。

数式をWordで書くのめんどくさ。

点(1,6)から直線y=3/4x-1を計算する処理です。

分数を取り払って3x-4y-1=0に変換してます。

>>> import math
>>> x = 1
>>> y = 6
>>> a = 3
>>> b = -4
>>> c = -4
>>> math.fabs(a*x + b*y + c) / math.sqrt(a**2 + b**2)
5.0

RUST勉強中、11/17の積み上げ

ボタン処理の実装を行っていきます。

ライブラリはicedを使用しています。

use iced::{
    button, executor, Align, Application, Button, Column, Command, Element, Font,
    HorizontalAlignment, Length, Row, Settings, Subscription, Text,
};

const FONT: Font = Font::External {
    name: "PixelMplus12-Regular",
    bytes: include_bytes!("../rsc/PixelMplus12-Regular.ttf"),
};

#[derive(Debug, Clone)]
pub enum Message {
    Start,
    Stop,
    Reset,
}

pub enum TickState {
    Stopped,
    Ticking,
}

struct GUI {
    tick_state: TickState,
    start_stop_button_state: button::State,
    reset_button_state: button::State,
}

impl Application for GUI {
    type Executor = executor::Null;
    type Message = Message;
    type Flags = ();

    fn new(_flags: ()) -> (GUI, Command<Self::Message>) {
        (
            GUI {
                tick_state: TickState::Stopped,
                start_stop_button_state: button::State::new(),
                reset_button_state: button::State::new(),
            },
            Command::none(),
        )
    }

    fn title(&self) -> String {
        String::from("DEMO")
    }

    fn update(&mut self, message: Self::Message) -> Command<Self::Message> {
        match message {
            Message::Start => {
                self.tick_state = TickState::Ticking;
            }
            Message::Stop => {
                self.tick_state = TickState::Stopped;
            }
            Message::Reset => {}
        }
        Command::none()
    }

    fn view(&mut self) -> Element<Self::Message> {
        let duration_text = "00:00:00.00";

        let start_stop_text = match self.tick_state {
            TickState::Stopped => Text::new("Start")
                .horizontal_alignment(HorizontalAlignment::Center)
                .font(FONT),
            TickState::Ticking => Text::new("Stop")
                .horizontal_alignment(HorizontalAlignment::Center)
                .font(FONT),
        };

        let start_stop_message = match self.tick_state {
            TickState::Stopped => Message::Start,
            TickState::Ticking => Message::Stop,
        };
        
        let tick_text = Text::new("00:00:00.00").font(FONT).size(60);
        let start_stop_button = Button::new(&mut self.start_stop_button_state, start_stop_text)
            .min_width(80)
            .on_press(start_stop_message);
        let reset_button = Button::new(
            &mut self.reset_button_state,
            Text::new("Reset")
                .horizontal_alignment(HorizontalAlignment::Center)
                .font(FONT),
        )
            .min_width(80)
            .on_press(Message::Reset);

        Column::new()
            .push(tick_text)
            .push(
                Row::new()
                    .push(start_stop_button)
                    .push(reset_button)
                    .spacing(10),
            )
            .spacing(10)
            .padding(10)
            .width(Length::Fill)
            .height(Length::Fill)
            .align_items(Align::Center)
            .into()
    }
}

fn main() {
    let mut settings = Settings::default();
    settings.window.size = (400u32, 120u32);
    GUI::run(settings);
}

Messageの定義とTickStateの定義を追加しています。

またアプリケーションの状態として、tick_stateを追加しています。

型はTickStateです。

viewの処理に追加し、tick_stateの状態でボタンに表示するテキストstart_stop_textを決めています。

また、ボタン処理に飛ばすメッセージstart_stop_messageの値も決めています。

そして、ボタン押下時にstart_stop_messageを飛ばし、

それをupdate()で受け取って、tick_stateの値を変えています。