Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Using SDL+Direct3D #2612

Closed
TerensTare opened this issue Jun 7, 2019 · 11 comments
Closed

Using SDL+Direct3D #2612

TerensTare opened this issue Jun 7, 2019 · 11 comments
Labels

Comments

@TerensTare
Copy link

Hello there!
I have just discovered ImGui and I thought of using it with SDL.
The problem is that I have an old PC and it supports onnly up to OpenGL1.1.
However, I have installed Windows 10 on it, so its DirectX capabilities are better than its OpenGL capabilities.

I was wondering : Is there a way to use ImGui with SDL2 with Direct3D as a renderer?
thank you in advance.

@48cf
Copy link

48cf commented Jun 7, 2019

Well, as long as you get SDL2 to render properly with DirectX, implementing ImGui should be pretty straightforward. Just put needed API implementation calls (you can refer to the examples for that) in your code, and render your GUI with ImGui.

@ocornut
Copy link
Owner

ocornut commented Jun 7, 2019

The _opengl2.cpp binding is misnamed and should work on your machine. Doesn’t it?

You can use SDL with DirectX, yes. I suppose we could provide an example at some point.

@ocornut ocornut changed the title Question Using SDL+Direct3D Jun 7, 2019
@TerensTare
Copy link
Author

Well thank you for everything.
Hope you have a good day!
And most important: Happy Coding!

@TerensTare
Copy link
Author

TerensTare commented Jun 15, 2019

I tried creating my bindings for this case, and I ended up with these files:


// imgui_impl_sdl_dx12.cpp

#include "imgui/imgui_impl_sdl_dx12.h"
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_ttf.h>
#include <SDL2/SDL_mixer.h>
#include <iostream>

using std::cout;
using std::endl;

#pragma comment(lib, "d3d12.lib")
#pragma comment(lib, "dxgi.lib")
#pragma comment(lib, "d3dcompiler.lib")

bool CreateDeviceD3D(HWND hWnd)
{
    // Setup swap chain
    DXGI_SWAP_CHAIN_DESC1 sd;
    {
        ZeroMemory(&sd, sizeof(sd));
        sd.BufferCount = NUM_BACK_BUFFERS;
        sd.Width = 0;
        sd.Height = 0;
        sd.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
        sd.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
        sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
        sd.SampleDesc.Count = 1;
        sd.SampleDesc.Quality = 0;
        sd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
        sd.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
        sd.Scaling = DXGI_SCALING_STRETCH;
        sd.Stereo = FALSE;
    }

    if (DX12_ENABLE_DEBUG_LAYER)
    {
        ID3D12Debug *dx12Debug = NULL;
        if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&dx12Debug))))
        {
            dx12Debug->EnableDebugLayer();
            dx12Debug->Release();
        }
    }

    D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_11_0;
    if (D3D12CreateDevice(NULL, featureLevel, IID_PPV_ARGS(&g_pd3dDevice)) != S_OK)
        return false;

    {
        D3D12_DESCRIPTOR_HEAP_DESC desc = {};
        desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
        desc.NumDescriptors = NUM_BACK_BUFFERS;
        desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
        desc.NodeMask = 1;
        if (g_pd3dDevice->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&g_pd3dRtvDescHeap)) != S_OK)
            return false;

        SIZE_T rtvDescriptorSize = g_pd3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
        D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = g_pd3dRtvDescHeap->GetCPUDescriptorHandleForHeapStart();
        for (UINT i = 0; i < NUM_BACK_BUFFERS; i++)
        {
            g_mainRenderTargetDescriptor[i] = rtvHandle;
            rtvHandle.ptr += rtvDescriptorSize;
        }
    }

    {
        D3D12_DESCRIPTOR_HEAP_DESC desc = {};
        desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
        desc.NumDescriptors = 1;
        desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
        if (g_pd3dDevice->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&g_pd3dSrvDescHeap)) != S_OK)
            return false;
    }

    {
        D3D12_COMMAND_QUEUE_DESC desc = {};
        desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
        desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
        desc.NodeMask = 1;
        if (g_pd3dDevice->CreateCommandQueue(&desc, IID_PPV_ARGS(&g_pd3dCommandQueue)) != S_OK)
            return false;
    }

    for (UINT i = 0; i < NUM_FRAMES_IN_FLIGHT; i++)
        if (g_pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&g_frameContext[i].CommandAllocator)) != S_OK)
            return false;

    if (g_pd3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, g_frameContext[0].CommandAllocator, NULL, IID_PPV_ARGS(&g_pd3dCommandList)) != S_OK ||
        g_pd3dCommandList->Close() != S_OK)
        return false;

    if (g_pd3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&g_fence)) != S_OK)
        return false;

    g_fenceEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (g_fenceEvent == NULL)
        return false;

    {
        IDXGIFactory4 *dxgiFactory = NULL;
        IDXGISwapChain1 *swapChain1 = NULL;
        if (CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory)) != S_OK ||
            dxgiFactory->CreateSwapChainForHwnd(g_pd3dCommandQueue, hWnd, &sd, NULL, NULL, &swapChain1) != S_OK ||
            swapChain1->QueryInterface(IID_PPV_ARGS(&g_pSwapChain)) != S_OK)
            return false;
        swapChain1->Release();
        dxgiFactory->Release();
        g_pSwapChain->SetMaximumFrameLatency(NUM_BACK_BUFFERS);
        g_hSwapChainWaitableObject = g_pSwapChain->GetFrameLatencyWaitableObject();
    }

    CreateRenderTarget();
    return true;
}

void CleanupDeviceD3D()
{
    CleanupRenderTarget();
    if (g_pSwapChain)
    {
        g_pSwapChain->Release();
        g_pSwapChain = NULL;
    }
    if (g_hSwapChainWaitableObject != NULL)
    {
        CloseHandle(g_hSwapChainWaitableObject);
    }
    for (UINT i = 0; i < NUM_FRAMES_IN_FLIGHT; i++)
        if (g_frameContext[i].CommandAllocator)
        {
            g_frameContext[i].CommandAllocator->Release();
            g_frameContext[i].CommandAllocator = NULL;
        }
    if (g_pd3dCommandQueue)
    {
        g_pd3dCommandQueue->Release();
        g_pd3dCommandQueue = NULL;
    }
    if (g_pd3dCommandList)
    {
        g_pd3dCommandList->Release();
        g_pd3dCommandList = NULL;
    }
    if (g_pd3dRtvDescHeap)
    {
        g_pd3dRtvDescHeap->Release();
        g_pd3dRtvDescHeap = NULL;
    }
    if (g_pd3dSrvDescHeap)
    {
        g_pd3dSrvDescHeap->Release();
        g_pd3dSrvDescHeap = NULL;
    }
    if (g_fence)
    {
        g_fence->Release();
        g_fence = NULL;
    }
    if (g_fenceEvent)
    {
        CloseHandle(g_fenceEvent);
        g_fenceEvent = NULL;
    }
    if (g_pd3dDevice)
    {
        g_pd3dDevice->Release();
        g_pd3dDevice = NULL;
    }
}

void CreateRenderTarget()
{
    ID3D12Resource *pBackBuffer;
    for (UINT i = 0; i < NUM_BACK_BUFFERS; i++)
    {
        g_pSwapChain->GetBuffer(i, IID_PPV_ARGS(&pBackBuffer));
        g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, g_mainRenderTargetDescriptor[i]);
        g_mainRenderTargetResource[i] = pBackBuffer;
    }
}

void CleanupRenderTarget()
{
    WaitForLastSubmittedFrame();

    for (UINT i = 0; i < NUM_BACK_BUFFERS; i++)
        if (g_mainRenderTargetResource[i])
        {
            g_mainRenderTargetResource[i]->Release();
            g_mainRenderTargetResource[i] = NULL;
        }
}

void WaitForLastSubmittedFrame()
{
    FrameContext *frameCtxt = &g_frameContext[g_frameIndex % NUM_FRAMES_IN_FLIGHT];

    UINT64 fenceValue = frameCtxt->FenceValue;
    if (fenceValue == 0)
        return; // No fence was signaled

    frameCtxt->FenceValue = 0;
    if (g_fence->GetCompletedValue() >= fenceValue)
        return;

    g_fence->SetEventOnCompletion(fenceValue, g_fenceEvent);
    WaitForSingleObject(g_fenceEvent, INFINITE);
}

FrameContext *WaitForNextFrameResources()
{
    UINT nextFrameIndex = g_frameIndex + 1;
    g_frameIndex = nextFrameIndex;

    HANDLE waitableObjects[] = {g_hSwapChainWaitableObject, NULL};
    DWORD numWaitableObjects = 1;

    FrameContext *frameCtxt = &g_frameContext[nextFrameIndex % NUM_FRAMES_IN_FLIGHT];
    UINT64 fenceValue = frameCtxt->FenceValue;
    if (fenceValue != 0) // means no fence was signaled
    {
        frameCtxt->FenceValue = 0;
        g_fence->SetEventOnCompletion(fenceValue, g_fenceEvent);
        waitableObjects[1] = g_fenceEvent;
        numWaitableObjects = 2;
    }

    WaitForMultipleObjects(numWaitableObjects, waitableObjects, TRUE, INFINITE);

    return frameCtxt;
}

void ResizeSwapChain(HWND hWnd, int width, int height)
{
    DXGI_SWAP_CHAIN_DESC1 sd;
    g_pSwapChain->GetDesc1(&sd);
    sd.Width = width;
    sd.Height = height;

    IDXGIFactory4 *dxgiFactory = NULL;
    g_pSwapChain->GetParent(IID_PPV_ARGS(&dxgiFactory));

    g_pSwapChain->Release();
    CloseHandle(g_hSwapChainWaitableObject);

    IDXGISwapChain1 *swapChain1 = NULL;
    dxgiFactory->CreateSwapChainForHwnd(g_pd3dCommandQueue, hWnd, &sd, NULL, NULL, &swapChain1);
    swapChain1->QueryInterface(IID_PPV_ARGS(&g_pSwapChain));
    swapChain1->Release();
    dxgiFactory->Release();

    g_pSwapChain->SetMaximumFrameLatency(NUM_BACK_BUFFERS);

    g_hSwapChainWaitableObject = g_pSwapChain->GetFrameLatencyWaitableObject();
    assert(g_hSwapChainWaitableObject != NULL);
}

// Win32 message handler
extern LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
        return true;

    switch (msg)
    {
    case WM_SIZE:
        if (g_pd3dDevice != NULL && wParam != SIZE_MINIMIZED)
        {
            ImGui_ImplDX12_InvalidateDeviceObjects();
            CleanupRenderTarget();
            ResizeSwapChain(hWnd, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam));
            CreateRenderTarget();
            ImGui_ImplDX12_CreateDeviceObjects();
        }
        return 0;
    case WM_SYSCOMMAND:
        if ((wParam & 0xfff0) == SC_KEYMENU) // Disable ALT application menu
            return 0;
        break;
    case WM_DESTROY:
        ::PostQuitMessage(0);
        return 0;
    }
    return ::DefWindowProc(hWnd, msg, wParam, lParam);
}

WinData ImGui_ImplSDL2_InitD3D()
{
    WNDCLASSEX wc = {sizeof(WNDCLASSEX), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, _T("ImGui Example"), NULL};
    ::RegisterClassEx(&wc);
    HWND hwnd = ::CreateWindow(wc.lpszClassName, _T("Dear ImGui DirectX12 Example"), WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800, NULL, NULL, wc.hInstance, NULL);

    if (!CreateDeviceD3D(hwnd))
    {
        CleanupDeviceD3D();
        ::UnregisterClass(wc.lpszClassName, wc.hInstance);
        return WinData{0, 0};
    }
    return WinData{hwnd, wc};
}

void ImGui_ImplSDL2_UpdateD3D(HWND hWnd)
{
    ::ShowWindow(hWnd, SW_SHOWDEFAULT);
    ::UpdateWindow(hWnd);
}

void ImGui_ImplSDL2_InitImGui(HWND hWnd)
{
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    ImGuiIO &io = ImGui::GetIO();
    (void)io;

    ImGui::StyleColorsDark();

    ImGui_ImplWin32_Init(hWnd);
    ImGui_ImplDX12_Init(g_pd3dDevice, NUM_FRAMES_IN_FLIGHT,
                        DXGI_FORMAT_R8G8B8A8_UNORM,
                        g_pd3dSrvDescHeap->GetCPUDescriptorHandleForHeapStart(),
                        g_pd3dSrvDescHeap->GetGPUDescriptorHandleForHeapStart());

    MSG msg;
    ZeroMemory(&msg, sizeof(msg));
    while (msg.message != WM_QUIT)
    {
        if (::PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
        {
            ::TranslateMessage(&msg);
            ::DispatchMessage(&msg);
            continue;
        }

        // Start the Dear ImGui frame
        ImGui_ImplDX12_NewFrame();
        ImGui_ImplWin32_NewFrame();
        ImGui::NewFrame();
    }
}

SDL_Data ImGui_ImplSDL2_InitSDL2(HWND hWnd)
{
    if (SDL_Init(SDL_INIT_EVERYTHING != 0))
    {
        cout << "Couldn't initialize SDL.\n\tError: " << SDL_GetError() << endl;
        return {0, 0};
    }

    // SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl");
    SDL_SetHint(SDL_HINT_RENDER_DRIVER, "direct3d");

    if (IMG_Init(IMG_INIT_PNG) & IMG_INIT_PNG != IMG_INIT_PNG)
    {
        cout << "Couldn't initialize image library for loading .png images.\n\tError: " << IMG_GetError() << endl;
        return {0, 0};
    }

    if (TTF_Init() == -1)
    {
        cout << "Couldn't initialize TTF library.\n\tError: " << TTF_GetError() << endl;
        return {0, 0};
    }

    int Mix_Flags = MIX_INIT_MP3 | MIX_INIT_OGG;
    if (Mix_Init(Mix_Flags) & Mix_Flags != Mix_Flags)
    {
        cout << "Couldn't initialize sound library for loading .mp3 files.\n\tError: " << Mix_GetError() << endl;
        return {0, 0};
    }

    SDL_Window *g_Window = SDL_CreateWindowFrom(hWnd);

    if (g_Window == NULL)
    {
        cout << "Couldn't create a window!\n\tError: " << SDL_GetError() << endl;
        return {0, 0};
    }

    SDL_Renderer *g_Renderer = SDL_CreateRenderer(g_Window, -1, 0);

    if (g_Renderer == NULL)
    {
        cout << "Couldn't create a renderer for the current window.\n\tError: " << SDL_GetError() << endl;
        return {0, 0};
    }

    SDL_SetRenderDrawColor(g_Renderer, 255, 255, 255, 255);
    cout << "Succesfully initialized everything." << endl;
    bool g_Init = true;
    return {g_Window, g_Renderer};
}

void ImGui_ImplSDL2_RenderD3D(ImVec4 clear_color)
{
    FrameContext *frameCtxt = WaitForNextFrameResources();
    UINT backBufferIdx = g_pSwapChain->GetCurrentBackBufferIndex();
    frameCtxt->CommandAllocator->Reset();

    D3D12_RESOURCE_BARRIER barrier = {};
    barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
    barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
    barrier.Transition.pResource = g_mainRenderTargetResource[backBufferIdx];
    barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
    barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
    barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;

    g_pd3dCommandList->Reset(frameCtxt->CommandAllocator, NULL);
    g_pd3dCommandList->ResourceBarrier(1, &barrier);
    g_pd3dCommandList->ClearRenderTargetView(g_mainRenderTargetDescriptor[backBufferIdx], (float *)&clear_color, 0, NULL);
    g_pd3dCommandList->OMSetRenderTargets(1, &g_mainRenderTargetDescriptor[backBufferIdx], FALSE, NULL);
    g_pd3dCommandList->SetDescriptorHeaps(1, &g_pd3dSrvDescHeap);
    ImGui::Render();
    ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), g_pd3dCommandList);
    barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
    barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
    g_pd3dCommandList->ResourceBarrier(1, &barrier);
    g_pd3dCommandList->Close();

    g_pd3dCommandQueue->ExecuteCommandLists(1, (ID3D12CommandList *const *)&g_pd3dCommandList);

    // g_pSwapChain->Present(1, 0); // Present with vsync
    g_pSwapChain->Present(0, 0); // Present without vsync

    UINT64 fenceValue = g_fenceLastSignaledValue + 1;
    g_pd3dCommandQueue->Signal(g_fence, fenceValue);
    g_fenceLastSignaledValue = fenceValue;
    frameCtxt->FenceValue = fenceValue;
}

void ImGui_ImplSDL2_Update()
{
    WaitForLastSubmittedFrame();
}

void ImGui_ImplSDL2_ClearSDL(SDL_Window *_win, SDL_Renderer *_ren)
{
    SDL_DestroyWindow(_win);
    _win = NULL;
    SDL_DestroyRenderer(_ren);
    _ren = NULL;

    Mix_Quit();
    TTF_Quit();
    IMG_Quit();
    SDL_Quit();
}

void ImGui_ImplSDL2_Close(HWND hWnd, WNDCLASSEX wc)
{
    ImGui_ImplDX12_Shutdown();
    ImGui_ImplWin32_Shutdown();
    ImGui::DestroyContext();

    CleanupDeviceD3D();
    ::DestroyWindow(hWnd);
    ::UnregisterClass(wc.lpszClassName, wc.hInstance);
}

int main(int, char **)
{
    WinData wd = ImGui_ImplSDL2_InitD3D();
    ImGui_ImplSDL2_UpdateD3D(wd.hWnd);
    ImGui_ImplSDL2_InitImGui(wd.hWnd);
    ImGui_ImplSDL2_Draw(true, false);
    SDL_Data dt = ImGui_ImplSDL2_InitSDL2(wd.hWnd);
    ImGui_ImplSDL2_RenderD3D();
    ImGui_ImplSDL2_Update();
    ImGui_ImplSDL2_ClearSDL(dt.win, dt.ren);
    ImGui_ImplSDL2_Close(wd.hWnd, wd.Wc);

return 0;
}

// functions cycle
// initd3d v
// updated3d v
// initimgui v
// drawimgui todo in main game file
// initsdl v
// renderd3d v
// updateimgui
// close v

// imgui_impl_sdl_dx12.h

#include "imgui.h"
#include "imgui_impl_win32.h"
#include "imgui_impl_dx12.h"
#include <SDL2/SDL.h>
#include <d3d12.h>
#include <dxgi1_4.h>
#include <tchar.h>

#define DX12_ENABLE_DEBUG_LAYER 0

struct FrameContext
{
    ID3D12CommandAllocator *CommandAllocator;
    UINT64 FenceValue;
};

// Data
static int const NUM_FRAMES_IN_FLIGHT = 3;
static FrameContext g_frameContext[NUM_FRAMES_IN_FLIGHT] = {};
static UINT g_frameIndex = 0;

static int const NUM_BACK_BUFFERS = 3;
static ID3D12Device *g_pd3dDevice = NULL;
static ID3D12DescriptorHeap *g_pd3dRtvDescHeap = NULL;
static ID3D12DescriptorHeap *g_pd3dSrvDescHeap = NULL;
static ID3D12CommandQueue *g_pd3dCommandQueue = NULL;
static ID3D12GraphicsCommandList *g_pd3dCommandList = NULL;
static ID3D12Fence *g_fence = NULL;
static HANDLE g_fenceEvent = NULL;
static UINT64 g_fenceLastSignaledValue = 0;
static IDXGISwapChain3 *g_pSwapChain = NULL;
static HANDLE g_hSwapChainWaitableObject = NULL;
static ID3D12Resource *g_mainRenderTargetResource[NUM_BACK_BUFFERS] = {};
static D3D12_CPU_DESCRIPTOR_HANDLE g_mainRenderTargetDescriptor[NUM_BACK_BUFFERS] = {};

// Helper functions
bool CreateDeviceD3D(HWND hWnd);
void CleanupDeviceD3D();
void CreateRenderTarget();
void CleanupRenderTarget();
void WaitForLastSubmittedFrame();
FrameContext *WaitForNextFrameResources();
void ResizeSwapChain(HWND hWnd, int width, int height);
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

struct WinData
{
public:
    HWND hWnd;
    WNDCLASSEX Wc;
};

struct SDL_Data
{
public:
    SDL_Window *win;
    SDL_Renderer *ren;
};

// ImGui related functions
IMGUI_IMPL_API WinData ImGui_ImplSDL2_InitD3D();
IMGUI_IMPL_API void ImGui_ImplSDL2_UpdateD3D(HWND hWnd);
IMGUI_IMPL_API void ImGui_ImplSDL2_InitImGui(HWND hWnd);
IMGUI_IMPL_API void ImGui_ImplSDL2_RenderD3D(ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f));
IMGUI_IMPL_API SDL_Data ImGui_ImplSDL2_InitSDL2(HWND hWnd);
IMGUI_IMPL_API void ImGui_ImplSDL2_Update();
IMGUI_IMPL_API void ImGui_ImplSDL2_ClearSDL(SDL_Window *_win, SDL_Renderer *ren);
IMGUI_IMPL_API void ImGui_ImplSDL2_Close(HWND hWnd, WNDCLASSEX wc);

void ImGui_ImplSDL2_SetState()
{
    bool show_demo_window = true;
    bool show_another_window = false;
}

void ImGui_ImplSDL2_Draw(bool show_demo_window, bool show_another_window, ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f))
{
    if (show_demo_window)
        ImGui::ShowDemoWindow(&show_demo_window);

    // 2. Show a simple window that we create ourselves. We use a Begin/End pair to created a named window.
    {
        static float f = 0.0f;
        static int counter = 0;

        ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it.

        ImGui::Text("This is some useful text.");          // Display some text (you can use a format strings too)
        ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state
        ImGui::Checkbox("Another Window", &show_another_window);

        ImGui::SliderFloat("float", &f, 0.0f, 1.0f);             // Edit 1 float using a slider from 0.0f to 1.0f
        ImGui::ColorEdit3("clear color", (float *)&clear_color); // Edit 3 floats representing a color

        if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated)
            counter++;
        ImGui::SameLine();
        ImGui::Text("counter = %d", counter);

        ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
        ImGui::End();
    }

    // 3. Show another simple window.
    if (show_another_window)
    {
        ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked)
        ImGui::Text("Hello from another window!");
        if (ImGui::Button("Close Me"))
            show_another_window = false;
        ImGui::End();
    }
}

I am using VS2019's compiler and compiling in 64-bit mode with:


REM create.bat

cl /EHs *.cpp /I .\include\imgui /I .\include /link /LIBPATH:.\lib\x64 SDL2.lib SDL2main.lib SDL2_net.lib SDL2_ttf.lib SDL2_image.lib SDL2_mixer.lib /subsystem:windows

@ocornut
Copy link
Owner

ocornut commented Jun 18, 2019

@TerensTare We have now added an example of using SDL2+DirectX11 in the repository:
https://github.com/ocornut/imgui/tree/master/examples/example_sdl_directx11

Basically don't pass SDL_WINDOW_OPENGL nor SDL_WINDOW_VULKAN, when creating the window, then retrieve the HWND from SDL using SDL_GetWindowWMInfo and you can create a DX context from there.

@ocornut ocornut closed this as completed Jun 18, 2019
@DaveInDev
Copy link

@TerensTare Hi Terens, reading this post, I am a bit confused : was your goal to use both imgui/DX and SDL2/DX renderer together on the same window (I mean with SDL2 calling graphic primitives like SDL_RenderRect or any of those presented in https://wiki.libsdl.org/CategoryRender) ?
Because if you succeeded, I would be glad to see an example of you main rendering loop and how you initialize both SDL_Renderer and imgui DX context, in order for them to draw on the same window. Because despite numerous attempts, I just got a flickering between imgui and the SDL2 renderer....

@TerensTare
Copy link
Author

Hey there @binbinhfr , I wanted to create a small tool that uses ImGui. As I mentioned earlier, my machine is way too old for OpenGL3, so I decided to go for DirectX. The problem with DirectX is that it needs many lines of code to do sth "small". Meanwhile, there was SDL2 which uses DirectX for rendering by default on Windows (at least I think so), and that made it the perfect tool for me. That means I wanted to use SDL2 to shorten the lines of code and make the code more readable. I ended up using the new example provided by @ocornut which ran perfectly fine for me.

@DaveInDev
Copy link

ok man, thx for your answer. So if I understand right, at the end, you do not use SDL, but only ImGui with the DirectX backend provided by @ocornut in the example section ?

@TerensTare
Copy link
Author

TerensTare commented Apr 20, 2020

Yes, that's it (kinda). I just used SDL2 to create the window and then handle events. Then I just let DirectX and ImGui do the rest (thinks like rendering, clearing the screen, etc.).

@DaveInDev
Copy link

so, as @ocornut advised me, you are using imgui, not only for its GUI features (menus, buttons, etc...) but also for rendering other things on screen ?

@TerensTare
Copy link
Author

Not really. I was forced to do the rendering of "other stuff" (things that are NOT widgets) with DirectX. I learned some stuff by checking the source code of the render folder of SDL2.

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants