dear-imgui-sdl3 0.16.0

SDL3 platform backend with optional OpenGL3, SDLRenderer3, and SDLGPU3 renderers for dear-imgui-rs
#include <SDL3/SDL.h>

#include "backends/imgui_impl_sdlgpu3.h"
#include "native_bridge.h"

#define SDL_ClaimWindowForGPUDevice dear_imgui_sdl3_hook_claim_window_for_gpu_device
#define SDL_SetGPUSwapchainParameters dear_imgui_sdl3_hook_set_gpu_swapchain_parameters

#include "backends/imgui_impl_sdlgpu3.cpp"

#undef SDL_SetGPUSwapchainParameters
#undef SDL_ClaimWindowForGPUDevice

namespace {

struct DearImguiSdlGpu3RenderOps {
    decltype(&SDL_AcquireGPUCommandBuffer) acquire_command_buffer;
    decltype(&SDL_AcquireGPUSwapchainTexture) acquire_swapchain_texture;
    decltype(&SDL_BeginGPURenderPass) begin_render_pass;
    decltype(&SDL_EndGPURenderPass) end_render_pass;
    decltype(&SDL_SubmitGPUCommandBuffer) submit_command_buffer;
    decltype(&SDL_CancelGPUCommandBuffer) cancel_command_buffer;
    void (*prepare_draw_data)(ImDrawData*, SDL_GPUCommandBuffer*);
    void (*render_draw_data)(
        ImDrawData*,
        SDL_GPUCommandBuffer*,
        SDL_GPURenderPass*,
        SDL_GPUGraphicsPipeline*
    );
};

const DearImguiSdlGpu3RenderOps real_render_ops = {
    SDL_AcquireGPUCommandBuffer,
    SDL_AcquireGPUSwapchainTexture,
    SDL_BeginGPURenderPass,
    SDL_EndGPURenderPass,
    SDL_SubmitGPUCommandBuffer,
    SDL_CancelGPUCommandBuffer,
    ImGui_ImplSDLGPU3_PrepareDrawData,
    ImGui_ImplSDLGPU3_RenderDrawData,
};

std::uint64_t dear_imgui_sdlgpu3_render_checked(
    const DearImguiSdlGpu3RenderOps& ops,
    SDL_GPUDevice* device,
    SDL_Window* window,
    ImDrawData* draw_data,
    std::uint64_t* first_fault
) {
    if (first_fault != nullptr)
        *first_fault = 0;

    if (device == nullptr || window == nullptr || draw_data == nullptr) {
        if (first_fault != nullptr)
            *first_fault = DEAR_IMGUI_SDL3_FAULT_NATIVE_PROTOCOL;
        return DEAR_IMGUI_SDL3_FAULT_NATIVE_PROTOCOL;
    }

    SDL_GPUCommandBuffer* command_buffer = ops.acquire_command_buffer(device);
    if (command_buffer == nullptr) {
        if (first_fault != nullptr)
            *first_fault = DEAR_IMGUI_SDL3_FAULT_SDLGPU_COMMAND_BUFFER;
        return DEAR_IMGUI_SDL3_FAULT_SDLGPU_COMMAND_BUFFER;
    }

    SDL_GPUTexture* swapchain_texture = nullptr;
    if (!ops.acquire_swapchain_texture(
            command_buffer,
            window,
            &swapchain_texture,
            nullptr,
            nullptr)) {
        std::uint64_t faults = DEAR_IMGUI_SDL3_FAULT_SDLGPU_SWAPCHAIN;
        if (!ops.cancel_command_buffer(command_buffer))
            faults |= DEAR_IMGUI_SDL3_FAULT_SDLGPU_CANCEL;
        if (first_fault != nullptr)
            *first_fault = DEAR_IMGUI_SDL3_FAULT_SDLGPU_SWAPCHAIN;
        return faults;
    }

    std::uint64_t faults = 0;
    if (swapchain_texture != nullptr) {
        ops.prepare_draw_data(draw_data, command_buffer);
        SDL_GPUColorTargetInfo target_info = {};
        target_info.texture = swapchain_texture;
        target_info.clear_color = SDL_FColor{ 0.0f, 0.0f, 0.0f, 1.0f };
        target_info.load_op = SDL_GPU_LOADOP_CLEAR;
        target_info.store_op = SDL_GPU_STOREOP_STORE;
        target_info.mip_level = 0;
        target_info.layer_or_depth_plane = 0;
        target_info.cycle = false;
        SDL_GPURenderPass* render_pass = ops.begin_render_pass(
            command_buffer,
            &target_info,
            1,
            nullptr
        );
        if (render_pass == nullptr) {
            faults |= DEAR_IMGUI_SDL3_FAULT_SDLGPU_RENDER_PASS;
            if (first_fault != nullptr)
                *first_fault = DEAR_IMGUI_SDL3_FAULT_SDLGPU_RENDER_PASS;
        } else {
            ops.render_draw_data(draw_data, command_buffer, render_pass, nullptr);
            ops.end_render_pass(render_pass);
        }
    }

    // A swapchain texture may have been acquired, so submit even after a render-pass failure.
    // SDL explicitly forbids cancelling a command buffer after swapchain acquisition.
    if (!ops.submit_command_buffer(command_buffer)) {
        faults |= DEAR_IMGUI_SDL3_FAULT_SDLGPU_SUBMIT;
        if (first_fault != nullptr && *first_fault == 0)
            *first_fault = DEAR_IMGUI_SDL3_FAULT_SDLGPU_SUBMIT;
    }
    return faults;
}

#if defined(DEAR_IMGUI_SDL3_NATIVE_SELF_TEST)
enum class DearImguiSdlGpu3FailStage {
    None,
    CommandBuffer,
    Swapchain,
    RenderPass,
    Submit,
};

struct DearImguiSdlGpu3FakeState {
    DearImguiSdlGpu3FailStage fail_stage = DearImguiSdlGpu3FailStage::None;
    bool cancel_fails = false;
    int acquire_command_calls = 0;
    int acquire_swapchain_calls = 0;
    int begin_pass_calls = 0;
    int prepare_calls = 0;
    int render_calls = 0;
    int end_pass_calls = 0;
    int submit_calls = 0;
    int cancel_calls = 0;
};

thread_local DearImguiSdlGpu3FakeState fake_render_state;

SDL_GPUCommandBuffer* SDLCALL fake_acquire_command_buffer(SDL_GPUDevice*) {
    fake_render_state.acquire_command_calls++;
    return fake_render_state.fail_stage == DearImguiSdlGpu3FailStage::CommandBuffer
        ? nullptr
        : reinterpret_cast<SDL_GPUCommandBuffer*>(static_cast<std::uintptr_t>(0x501));
}

bool SDLCALL fake_acquire_swapchain_texture(
    SDL_GPUCommandBuffer*,
    SDL_Window*,
    SDL_GPUTexture** texture,
    std::uint32_t*,
    std::uint32_t*
) {
    fake_render_state.acquire_swapchain_calls++;
    if (fake_render_state.fail_stage == DearImguiSdlGpu3FailStage::Swapchain)
        return false;
    *texture = reinterpret_cast<SDL_GPUTexture*>(static_cast<std::uintptr_t>(0x502));
    return true;
}

SDL_GPURenderPass* SDLCALL fake_begin_render_pass(
    SDL_GPUCommandBuffer*,
    const SDL_GPUColorTargetInfo*,
    std::uint32_t,
    const SDL_GPUDepthStencilTargetInfo*
) {
    fake_render_state.begin_pass_calls++;
    return fake_render_state.fail_stage == DearImguiSdlGpu3FailStage::RenderPass
        ? nullptr
        : reinterpret_cast<SDL_GPURenderPass*>(static_cast<std::uintptr_t>(0x503));
}

void SDLCALL fake_end_render_pass(SDL_GPURenderPass*) {
    fake_render_state.end_pass_calls++;
}

bool SDLCALL fake_submit_command_buffer(SDL_GPUCommandBuffer*) {
    fake_render_state.submit_calls++;
    return fake_render_state.fail_stage != DearImguiSdlGpu3FailStage::Submit;
}

bool SDLCALL fake_cancel_command_buffer(SDL_GPUCommandBuffer*) {
    fake_render_state.cancel_calls++;
    return !fake_render_state.cancel_fails;
}

void fake_prepare_draw_data(ImDrawData*, SDL_GPUCommandBuffer*) {
    fake_render_state.prepare_calls++;
}

void fake_render_draw_data(
    ImDrawData*,
    SDL_GPUCommandBuffer*,
    SDL_GPURenderPass*,
    SDL_GPUGraphicsPipeline*
) {
    fake_render_state.render_calls++;
}

const DearImguiSdlGpu3RenderOps fake_render_ops = {
    fake_acquire_command_buffer,
    fake_acquire_swapchain_texture,
    fake_begin_render_pass,
    fake_end_render_pass,
    fake_submit_command_buffer,
    fake_cancel_command_buffer,
    fake_prepare_draw_data,
    fake_render_draw_data,
};
#endif

} // namespace

extern "C" std::uint64_t dear_imgui_sdl3_backend_sdlgpu3_render_viewport(
    ImGuiViewport* viewport,
    std::uint64_t* first_fault
) {
    ImGui_ImplSDLGPU3_Data* data = ImGui_ImplSDLGPU3_GetBackendData();
    if (viewport == nullptr || data == nullptr) {
        if (first_fault != nullptr)
            *first_fault = DEAR_IMGUI_SDL3_FAULT_NATIVE_PROTOCOL;
        return DEAR_IMGUI_SDL3_FAULT_NATIVE_PROTOCOL;
    }
    SDL_Window* window = SDL_GetWindowFromID((SDL_WindowID)(intptr_t)viewport->PlatformHandle);
    return dear_imgui_sdlgpu3_render_checked(
        real_render_ops,
        data->InitInfo.Device,
        window,
        viewport->DrawData,
        first_fault
    );
}

#if defined(DEAR_IMGUI_SDL3_NATIVE_SELF_TEST)
extern "C" std::uint64_t dear_imgui_sdl3_backend_sdlgpu3_render_contract_self_test() {
    std::uint64_t failures = 0;
    SDL_GPUDevice* device = reinterpret_cast<SDL_GPUDevice*>(static_cast<std::uintptr_t>(0x601));
    SDL_Window* window = reinterpret_cast<SDL_Window*>(static_cast<std::uintptr_t>(0x602));
    ImDrawData* draw_data = reinterpret_cast<ImDrawData*>(static_cast<std::uintptr_t>(0x603));
    std::uint64_t first_fault = 0;

    fake_render_state = {};
    fake_render_state.fail_stage = DearImguiSdlGpu3FailStage::CommandBuffer;
    std::uint64_t faults = dear_imgui_sdlgpu3_render_checked(
        fake_render_ops, device, window, draw_data, &first_fault
    );
    if (faults != DEAR_IMGUI_SDL3_FAULT_SDLGPU_COMMAND_BUFFER
        || first_fault != DEAR_IMGUI_SDL3_FAULT_SDLGPU_COMMAND_BUFFER
        || fake_render_state.acquire_swapchain_calls != 0
        || fake_render_state.begin_pass_calls != 0
        || fake_render_state.submit_calls != 0)
        failures |= UINT64_C(1) << 0;

    fake_render_state = {};
    fake_render_state.fail_stage = DearImguiSdlGpu3FailStage::Swapchain;
    faults = dear_imgui_sdlgpu3_render_checked(
        fake_render_ops, device, window, draw_data, &first_fault
    );
    if (faults != DEAR_IMGUI_SDL3_FAULT_SDLGPU_SWAPCHAIN
        || first_fault != DEAR_IMGUI_SDL3_FAULT_SDLGPU_SWAPCHAIN
        || fake_render_state.cancel_calls != 1
        || fake_render_state.prepare_calls != 0
        || fake_render_state.begin_pass_calls != 0
        || fake_render_state.submit_calls != 0)
        failures |= UINT64_C(1) << 1;

    fake_render_state = {};
    fake_render_state.fail_stage = DearImguiSdlGpu3FailStage::Swapchain;
    fake_render_state.cancel_fails = true;
    faults = dear_imgui_sdlgpu3_render_checked(
        fake_render_ops, device, window, draw_data, &first_fault
    );
    if (faults != (DEAR_IMGUI_SDL3_FAULT_SDLGPU_SWAPCHAIN
            | DEAR_IMGUI_SDL3_FAULT_SDLGPU_CANCEL)
        || first_fault != DEAR_IMGUI_SDL3_FAULT_SDLGPU_SWAPCHAIN
        || fake_render_state.cancel_calls != 1
        || fake_render_state.prepare_calls != 0
        || fake_render_state.begin_pass_calls != 0
        || fake_render_state.submit_calls != 0)
        failures |= UINT64_C(1) << 4;

    fake_render_state = {};
    fake_render_state.fail_stage = DearImguiSdlGpu3FailStage::RenderPass;
    faults = dear_imgui_sdlgpu3_render_checked(
        fake_render_ops, device, window, draw_data, &first_fault
    );
    if (faults != DEAR_IMGUI_SDL3_FAULT_SDLGPU_RENDER_PASS
        || first_fault != DEAR_IMGUI_SDL3_FAULT_SDLGPU_RENDER_PASS
        || fake_render_state.prepare_calls != 1
        || fake_render_state.render_calls != 0
        || fake_render_state.end_pass_calls != 0
        || fake_render_state.submit_calls != 1
        || fake_render_state.cancel_calls != 0)
        failures |= UINT64_C(1) << 2;

    fake_render_state = {};
    fake_render_state.fail_stage = DearImguiSdlGpu3FailStage::Submit;
    faults = dear_imgui_sdlgpu3_render_checked(
        fake_render_ops, device, window, draw_data, &first_fault
    );
    if (faults != DEAR_IMGUI_SDL3_FAULT_SDLGPU_SUBMIT
        || first_fault != DEAR_IMGUI_SDL3_FAULT_SDLGPU_SUBMIT
        || fake_render_state.prepare_calls != 1
        || fake_render_state.render_calls != 1
        || fake_render_state.end_pass_calls != 1
        || fake_render_state.submit_calls != 1)
        failures |= UINT64_C(1) << 3;

    return failures;
}
#endif