diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,13 @@
 # Changelog for dear-imgui
 
+## [2.2.1]
+
+- Added `DearImGui.SDL.Renderer` backend and `sdlrenderer` example.
+- Added `DearImgui.withCloseableWindow`.
+- Added `DearImgui.Raw.framerate`.
+- Added dynamic rendering and color attachment format options for `DearImGui.Vulkan` backend.
+- Fixed Windows builds by using `system-cxx-std-lib` for GHC>=9.4.
+
 ## [2.2.0]
 
 - `imgui` updated to [1.89.9].
@@ -116,6 +124,7 @@
 [2.1.2]: https://github.com/haskell-game/dear-imgui.hs/tree/v2.1.2
 [2.1.3]: https://github.com/haskell-game/dear-imgui.hs/tree/v2.1.3
 [2.2.0]: https://github.com/haskell-game/dear-imgui.hs/tree/v2.2.0
+[2.2.1]: https://github.com/haskell-game/dear-imgui.hs/tree/v2.2.1
 
 [1.89.9]: https://github.com/ocornut/imgui/releases/tag/v1.89.9
 [1.87]: https://github.com/ocornut/imgui/releases/tag/v1.87
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -31,29 +31,30 @@
 With this done, the following module is the "Hello, World!" of ImGui:
 
 ``` haskell
-{-# language BlockArguments #-}
-{-# language LambdaCase #-}
 {-# language OverloadedStrings #-}
 
 module Main ( main ) where
 
-import Control.Exception
-import Control.Monad.IO.Class
-import Control.Monad.Managed
 import DearImGui
-import DearImGui.OpenGL2
+import DearImGui.OpenGL3
 import DearImGui.SDL
 import DearImGui.SDL.OpenGL
+
 import Graphics.GL
 import SDL
 
+import Control.Monad.Managed
+import Control.Monad.IO.Class ()
+import Control.Monad (when, unless)
+import Control.Exception (bracket, bracket_)
+
 main :: IO ()
 main = do
   -- Initialize SDL
   initializeAll
 
-  runManaged do
-    -- Create a window using SDL. As we're using OpenGL, we need to enable OpenGL too.
+  runManaged $ do
+    -- Create a window using SDL; as we're using OpenGL, we enable OpenGL too
     window <- do
       let title = "Hello, Dear ImGui!"
       let config = defaultWindow { windowGraphicsContext = OpenGLContext defaultOpenGL }
@@ -61,64 +62,59 @@
 
     -- Create an OpenGL context
     glContext <- managed $ bracket (glCreateContext window) glDeleteContext
-
     -- Create an ImGui context
     _ <- managed $ bracket createContext destroyContext
 
     -- Initialize ImGui's SDL2 backend
-    _ <- managed_ $ bracket_ (sdl2InitForOpenGL window glContext) sdl2Shutdown
-
+    managed_ $ bracket_ (sdl2InitForOpenGL window glContext) sdl2Shutdown
     -- Initialize ImGui's OpenGL backend
-    _ <- managed_ $ bracket_ openGL2Init openGL2Shutdown
+    managed_ $ bracket_ openGL3Init openGL3Shutdown
 
     liftIO $ mainLoop window
 
-
 mainLoop :: Window -> IO ()
-mainLoop window = unlessQuit do
+mainLoop window = unlessQuit $ do
   -- Tell ImGui we're starting a new frame
-  openGL2NewFrame
+  openGL3NewFrame
   sdl2NewFrame
   newFrame
 
   -- Build the GUI
-  withWindowOpen "Hello, ImGui!" do
+  withWindowOpen "Hello, ImGui!" $ do
     -- Add a text widget
     text "Hello, ImGui!"
 
     -- Add a button widget, and call 'putStrLn' when it's clicked
-    button "Clickety Click" >>= \case
-      False -> return ()
-      True  -> putStrLn "Ow!"
+    button "Clickety Click" >>= \clicked ->
+      when clicked $ putStrLn "Ow!"
 
   -- Show the ImGui demo window
   showDemoWindow
 
   -- Render
   glClear GL_COLOR_BUFFER_BIT
-
   render
-  openGL2RenderDrawData =<< getDrawData
+  openGL3RenderDrawData =<< getDrawData
 
   glSwapWindow window
-
   mainLoop window
-
   where
-    -- Process the event loop
-    unlessQuit action = do
-      shouldQuit <- checkEvents
-      if shouldQuit then pure () else action
+  -- Process the event loop
+  unlessQuit action = do
+    shouldQuit <- gotQuitEvent
+    unless shouldQuit action
 
-    checkEvents = do
-      pollEventWithImGui >>= \case
-        Nothing ->
-          return False
-        Just event ->
-          (isQuit event ||) <$> checkEvents
+  gotQuitEvent = do
+    ev <- pollEventWithImGui
 
-    isQuit event =
-      SDL.eventPayload event == SDL.QuitEvent
+    case ev of
+      Nothing ->
+        return False
+      Just event ->
+        (isQuit event ||) <$> gotQuitEvent
+
+  isQuit event =
+    eventPayload event == QuitEvent
 ```
 
 # Hacking
diff --git a/dear-imgui.cabal b/dear-imgui.cabal
--- a/dear-imgui.cabal
+++ b/dear-imgui.cabal
@@ -1,7 +1,7 @@
 cabal-version: 3.0
 
 name: dear-imgui
-version: 2.2.0
+version: 2.2.1
 author: Oliver Charles
 maintainer: ollie@ocharles.org.uk, aenor.realm@gmail.com
 license: BSD-3-Clause
@@ -94,6 +94,15 @@
   manual:
     True
 
+flag sdl-renderer
+  description:
+    Enable SDL Renderer backend (requires the SDL_RenderGeometry function available in SDL 2.0.18+).
+    The sdl configuration flag must also be enabled when using this flag.
+  default:
+    False
+  manual:
+    True
+
 flag glfw
   description:
     Enable GLFW backend.
@@ -140,7 +149,7 @@
 common common
   build-depends:
       base
-        >= 4.12 && < 4.19
+        >= 4.12 && < 4.20
   default-language:
       Haskell2010
 
@@ -170,8 +179,10 @@
     imgui/imgui_draw.cpp
     imgui/imgui_tables.cpp
     imgui/imgui_widgets.cpp
-  extra-libraries:
-    stdc++
+  if impl(ghc >= 9.4)
+    build-depends: system-cxx-std-lib
+  else
+    extra-libraries: stdc++
   include-dirs:
     imgui
   build-depends:
@@ -222,7 +233,7 @@
     other-modules:
       DearImGui.Vulkan.Types
     build-depends:
-        vulkan
+        vulkan >= 3.20
       , unliftio
     cxx-sources:
       imgui/backends/imgui_impl_vulkan.cpp
@@ -241,7 +252,7 @@
     exposed-modules:
       DearImGui.SDL
     build-depends:
-      sdl2
+      sdl2 >= 2
     cxx-sources:
       imgui/backends/imgui_impl_sdl2.cpp
 
@@ -260,6 +271,12 @@
       exposed-modules:
         DearImGui.SDL.Vulkan
 
+    if flag(sdl-renderer)
+      exposed-modules:
+        DearImGui.SDL.Renderer
+      cxx-sources:
+        imgui/backends/imgui_impl_sdlrenderer2.cpp
+
   if flag(glfw)
     exposed-modules:
       DearImGui.GLFW
@@ -291,23 +308,23 @@
     , DearImGui.Generator.Types
   build-depends:
       template-haskell
-        >= 2.15 && < 2.21
+        >= 2.15 && < 2.22
     , containers
-       ^>= 0.6.2.1
+        >= 0.6.2.1 && < 0.8
     , directory
         >= 1.3 && < 1.4
     , filepath
-        >= 1.4 && < 1.5
+        >= 1.4 && < 1.6
     , inline-c
         >= 0.9.0.0 && < 0.10
     , megaparsec
-        >= 9.0 && < 9.3
+        >= 9.0 && < 9.7
     , parser-combinators
         >= 1.2.0 && < 1.4
     , scientific
         >= 0.3.6.2 && < 0.3.8
     , text
-        >= 1.2.4 && < 2.1
+        >= 1.2.4 && < 2.2
     , th-lift
         >= 0.7 && < 0.9
     , transformers
@@ -358,6 +375,14 @@
   if (!flag(examples) || !flag(sdl) || !flag(opengl3))
     buildable: False
 
+executable sdlrenderer
+  import: common, exe-flags
+  main-is: Renderer.hs
+  hs-source-dirs: examples/sdl
+  build-depends: sdl2, dear-imgui, managed, text
+  if (!flag(examples) || !flag(sdl) || !flag(sdl-renderer))
+    buildable: False
+
 executable vulkan
   import: common, exe-flags
   main-is: Main.hs
@@ -380,7 +405,7 @@
       , sdl2
           >= 2.5.3.0 && < 2.6
       , text
-          >= 1.2.4 && < 2.1
+          >= 1.2.4 && < 2.2
       , transformers
           >= 0.5.6 && < 0.7
       , unliftio
@@ -390,7 +415,7 @@
       , vector
          >= 0.12.1.2 && < 0.14
       , vulkan
-         >= 3.12
+         >= 3.20
       , vulkan-utils
          >= 0.5
       , VulkanMemoryAllocator
diff --git a/examples/Readme.hs b/examples/Readme.hs
--- a/examples/Readme.hs
+++ b/examples/Readme.hs
@@ -1,29 +1,30 @@
 -- NOTE: If this is file is edited, please also copy and paste it into
 -- README.md.
 
-{-# language BlockArguments #-}
-{-# language LambdaCase #-}
 {-# language OverloadedStrings #-}
 
 module Main ( main ) where
 
-import Control.Exception
-import Control.Monad.IO.Class
-import Control.Monad.Managed
 import DearImGui
-import DearImGui.OpenGL2
+import DearImGui.OpenGL3
 import DearImGui.SDL
 import DearImGui.SDL.OpenGL
+
 import Graphics.GL
 import SDL
 
+import Control.Monad.Managed
+import Control.Monad.IO.Class ()
+import Control.Monad (when, unless)
+import Control.Exception (bracket, bracket_)
+
 main :: IO ()
 main = do
   -- Initialize SDL
   initializeAll
 
-  runManaged do
-    -- Create a window using SDL. As we're using OpenGL, we need to enable OpenGL too.
+  runManaged $ do
+    -- Create a window using SDL; as we're using OpenGL, we enable OpenGL too
     window <- do
       let title = "Hello, Dear ImGui!"
       let config = defaultWindow { windowGraphicsContext = OpenGLContext defaultOpenGL }
@@ -31,61 +32,56 @@
 
     -- Create an OpenGL context
     glContext <- managed $ bracket (glCreateContext window) glDeleteContext
-
     -- Create an ImGui context
     _ <- managed $ bracket createContext destroyContext
 
     -- Initialize ImGui's SDL2 backend
-    _ <- managed_ $ bracket_ (sdl2InitForOpenGL window glContext) sdl2Shutdown
-
+    managed_ $ bracket_ (sdl2InitForOpenGL window glContext) sdl2Shutdown
     -- Initialize ImGui's OpenGL backend
-    _ <- managed_ $ bracket_ openGL2Init openGL2Shutdown
+    managed_ $ bracket_ openGL3Init openGL3Shutdown
 
     liftIO $ mainLoop window
 
-
 mainLoop :: Window -> IO ()
-mainLoop window = unlessQuit do
+mainLoop window = unlessQuit $ do
   -- Tell ImGui we're starting a new frame
-  openGL2NewFrame
+  openGL3NewFrame
   sdl2NewFrame
   newFrame
 
   -- Build the GUI
-  withWindowOpen "Hello, ImGui!" do
+  withWindowOpen "Hello, ImGui!" $ do
     -- Add a text widget
     text "Hello, ImGui!"
 
     -- Add a button widget, and call 'putStrLn' when it's clicked
-    button "Clickety Click" >>= \case
-      False -> return ()
-      True  -> putStrLn "Ow!"
+    button "Clickety Click" >>= \clicked ->
+      when clicked $ putStrLn "Ow!"
 
   -- Show the ImGui demo window
   showDemoWindow
 
   -- Render
   glClear GL_COLOR_BUFFER_BIT
-
   render
-  openGL2RenderDrawData =<< getDrawData
+  openGL3RenderDrawData =<< getDrawData
 
   glSwapWindow window
-
   mainLoop window
-
   where
-    -- Process the event loop
-    unlessQuit action = do
-      shouldQuit <- checkEvents
-      if shouldQuit then pure () else action
+  -- Process the event loop
+  unlessQuit action = do
+    shouldQuit <- gotQuitEvent
+    unless shouldQuit action
 
-    checkEvents = do
-      pollEventWithImGui >>= \case
-        Nothing ->
-          return False
-        Just event ->
-          (isQuit event ||) <$> checkEvents
+  gotQuitEvent = do
+    ev <- pollEventWithImGui
 
-    isQuit event =
-      SDL.eventPayload event == SDL.QuitEvent
+    case ev of
+      Nothing ->
+        return False
+      Just event ->
+        (isQuit event ||) <$> gotQuitEvent
+
+  isQuit event =
+    eventPayload event == QuitEvent
diff --git a/examples/sdl/Renderer.hs b/examples/sdl/Renderer.hs
new file mode 100644
--- /dev/null
+++ b/examples/sdl/Renderer.hs
@@ -0,0 +1,146 @@
+{-# language BlockArguments #-}
+{-# language LambdaCase #-}
+{-# language OverloadedStrings #-}
+
+-- | Port of [example_sdl2_sdlrenderer2](https://github.com/ocornut/imgui/blob/54c1bdecebf3c9bb9259c07c5f5666bb4bd5c3ea/examples/example_sdl2_sdlrenderer2/main.cpp).
+--
+-- Minor differences:
+-- - No changing of the clear color via @ImGui::ColorEdit3@ as a Haskell binding
+-- doesn't yet exist for this function.
+-- - No high DPI renderer scaling as this seems to be in flux [upstream](https://github.com/ocornut/imgui/issues/6065)
+
+module Main ( main ) where
+
+import Control.Exception (bracket, bracket_)
+import Control.Monad.IO.Class (MonadIO(liftIO))
+import Control.Monad.Managed (managed, managed_, runManaged)
+import Data.IORef (IORef, newIORef)
+import Data.Text (pack)
+import DearImGui
+import DearImGui.SDL (pollEventWithImGui, sdl2NewFrame, sdl2Shutdown)
+import DearImGui.SDL.Renderer
+  ( sdl2InitForSDLRenderer, sdlRendererInit, sdlRendererNewFrame, sdlRendererRenderDrawData
+  , sdlRendererShutdown
+  )
+import SDL (V4(V4), ($=), ($~), get)
+import Text.Printf (printf)
+import qualified SDL
+
+
+main :: IO ()
+main = do
+  -- Initialize SDL2
+  SDL.initializeAll
+
+  runManaged do
+    -- Create a window using SDL2
+    window <- do
+      let title = "ImGui + SDL2 Renderer"
+      let config = SDL.defaultWindow
+            { SDL.windowInitialSize = SDL.V2 1280 720
+            , SDL.windowResizable = True
+            , SDL.windowPosition = SDL.Centered
+            }
+      managed $ bracket (SDL.createWindow title config) SDL.destroyWindow
+
+    -- Create an SDL2 renderer
+    renderer <- managed do
+      bracket
+        (SDL.createRenderer window (-1) SDL.defaultRenderer)
+        SDL.destroyRenderer
+
+    -- Create an ImGui context
+    _ <- managed $ bracket createContext destroyContext
+
+    -- Initialize ImGui's SDL2 backend
+    _ <- managed_ do
+      bracket_ (sdl2InitForSDLRenderer window renderer) sdl2Shutdown
+
+    -- Initialize ImGui's SDL2 renderer backend
+    _ <- managed_ $ bracket_ (sdlRendererInit renderer) sdlRendererShutdown
+
+    liftIO $ mainLoop renderer
+
+
+mainLoop :: SDL.Renderer -> IO ()
+mainLoop renderer = do
+  refs <- newRefs
+  go refs
+  where
+  go refs = unlessQuit do
+    -- Tell ImGui we're starting a new frame
+    sdlRendererNewFrame
+    sdl2NewFrame
+    newFrame
+
+    -- Show the ImGui demo window
+    get (refsShowDemoWindow refs) >>= \case
+      False -> pure ()
+      True -> showDemoWindow
+
+    withWindowOpen "Hello, world!" do
+      text "This is some useful text."
+      _ <- checkbox "Demo Window" $ refsShowDemoWindow refs
+      _ <- checkbox "Another Window" $ refsShowAnotherWindow refs
+      _ <- sliderFloat "float" (refsFloat refs) 0 1
+
+      button "Button" >>= \case
+        False -> pure ()
+        True -> refsCounter refs $~ succ
+      sameLine
+      counter <- get $ refsCounter refs
+      text $ "counter = " <> pack (show counter)
+
+      fr <- framerate
+      text
+        $ pack
+        $ printf "Application average %.3f ms/frame (%.1f FPS)" (1000 / fr) fr
+
+    get (refsShowAnotherWindow refs) >>= \case
+      False -> pure ()
+      True ->
+        withCloseableWindow "Another Window" (refsShowAnotherWindow refs) do
+          text "Hello from another window!"
+          button "Close Me" >>= \case
+            False -> pure ()
+            True -> refsShowAnotherWindow refs $= False
+
+    -- Render
+    SDL.rendererDrawColor renderer $= V4 0 0 0 255
+    SDL.clear renderer
+    render
+    sdlRendererRenderDrawData =<< getDrawData
+    SDL.present renderer
+
+    go refs
+
+  -- Process the event loop
+  unlessQuit action = do
+    shouldQuit <- checkEvents
+    if shouldQuit then pure () else action
+
+  checkEvents = do
+    pollEventWithImGui >>= \case
+      Nothing ->
+        return False
+      Just event ->
+        (isQuit event ||) <$> checkEvents
+
+  isQuit event =
+    SDL.eventPayload event == SDL.QuitEvent
+
+
+data Refs = Refs
+  { refsShowDemoWindow :: IORef Bool
+  , refsShowAnotherWindow :: IORef Bool
+  , refsFloat :: IORef Float
+  , refsCounter :: IORef Int
+  }
+
+newRefs :: IO Refs
+newRefs =
+  Refs
+    <$> newIORef True
+    <*> newIORef False
+    <*> newIORef 0
+    <*> newIORef 0
diff --git a/examples/vulkan/Backend.hs b/examples/vulkan/Backend.hs
--- a/examples/vulkan/Backend.hs
+++ b/examples/vulkan/Backend.hs
@@ -9,6 +9,7 @@
 {-# LANGUAGE MonoLocalBinds #-}
 {-# LANGUAGE NamedFieldPuns #-}
 {-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE PatternSynonyms #-}
 {-# LANGUAGE RecordWildCards #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TypeApplications #-}
@@ -99,6 +100,7 @@
 
 -- vulkan
 import qualified Vulkan
+import Vulkan.Core10.DeviceInitialization (pattern INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR)
 import qualified Vulkan.CStruct.Extends as Vulkan
 import qualified Vulkan.Requirement     as Vulkan
 import qualified Vulkan.Zero            as Vulkan
@@ -156,9 +158,10 @@
 
 data VulkanRequirements =
   VulkanRequirements
-    { instanceRequirements :: [ Vulkan.InstanceRequirement ]
-    , deviceRequirements   :: [ Vulkan.DeviceRequirement   ]
-    , queueFlags           :: Vulkan.QueueFlags
+    { instanceRequirements    :: [ Vulkan.InstanceRequirement ]
+    , instanceRequirementsOpt :: [ Vulkan.InstanceRequirement ]
+    , deviceRequirements      :: [ Vulkan.DeviceRequirement   ]
+    , queueFlags              :: Vulkan.QueueFlags
     }
 
 data ValidationLayerName
@@ -167,12 +170,12 @@
   deriving stock ( Eq, Show )
 
 initialiseVulkanContext :: MonadVulkan m => InstanceType -> ByteString -> VulkanRequirements -> m VulkanContext
-initialiseVulkanContext instanceType appName ( VulkanRequirements { instanceRequirements, deviceRequirements, queueFlags } ) = do
+initialiseVulkanContext instanceType appName ( VulkanRequirements { instanceRequirements, instanceRequirementsOpt, deviceRequirements, queueFlags } ) = do
   logDebug "Creating Vulkan instance"
   instanceInfo    <- vulkanInstanceInfo appName
   instance'       <- case instanceType of
-    NormalInstance -> Vulkan.Utils.createInstanceFromRequirements      instanceRequirements [] instanceInfo
-    DebugInstance  -> Vulkan.Utils.createDebugInstanceFromRequirements instanceRequirements [] instanceInfo
+    NormalInstance -> Vulkan.Utils.createInstanceFromRequirements      instanceRequirements instanceRequirementsOpt instanceInfo
+    DebugInstance  -> Vulkan.Utils.createDebugInstanceFromRequirements instanceRequirements instanceRequirementsOpt instanceInfo
   physicalDevice  <- logDebug "Creating physical device"      *> createPhysicalDevice instance'
   queueFamily     <- logDebug "Finding suitable queue family" *> findQueueFamilyIndex physicalDevice queueFlags
   let
@@ -236,7 +239,7 @@
     createInfo =
       Vulkan.InstanceCreateInfo
         { Vulkan.next                  = ()
-        , Vulkan.flags                 = Vulkan.zero
+        , Vulkan.flags                 = INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR
         , Vulkan.applicationInfo       = Just appInfo
         , Vulkan.enabledLayerNames     = Boxed.Vector.fromList enabledLayers
         , Vulkan.enabledExtensionNames = mempty
@@ -400,8 +403,6 @@
 createSwapchain physicalDevice device surface surfaceFormat imageUsage imageCount oldSwapchain = do
 
   surfaceCapabilities <- Vulkan.getPhysicalDeviceSurfaceCapabilitiesKHR physicalDevice ( Vulkan.SurfaceKHR surface )
-
-  ( _, presentModes ) <- Vulkan.getPhysicalDeviceSurfacePresentModesKHR physicalDevice ( Vulkan.SurfaceKHR surface )
 
   let
     presentMode :: Vulkan.PresentModeKHR
diff --git a/examples/vulkan/Main.hs b/examples/vulkan/Main.hs
--- a/examples/vulkan/Main.hs
+++ b/examples/vulkan/Main.hs
@@ -148,10 +148,14 @@
         , mouseMode  = SDL.AbsoluteLocation
         }
   let
+    compatExtensions =
+      [ Vulkan.KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME
+      ]
     vulkanReqs :: VulkanRequirements
     vulkanReqs =
       VulkanRequirements
         { instanceRequirements = instanceExtensions windowExtensions
+        , instanceRequirementsOpt = instanceExtensions compatExtensions
         , deviceRequirements   = []
         , queueFlags           = Vulkan.QUEUE_GRAPHICS_BIT
         }
@@ -387,14 +391,16 @@
       , device
       , queueFamily
       , queue
-      , pipelineCache  = Vulkan.NULL_HANDLE
-      , descriptorPool = imGuiDescriptorPool
-      , subpass        = 0
+      , pipelineCache         = Vulkan.NULL_HANDLE
+      , descriptorPool        = imGuiDescriptorPool
+      , subpass               = 0
       , minImageCount
       , imageCount
-      , msaaSamples    = Vulkan.SAMPLE_COUNT_1_BIT
-      , mbAllocator    = Nothing
-      , checkResult    = \case { Vulkan.SUCCESS -> pure (); e -> throw $ Vulkan.VulkanException e }
+      , msaaSamples           = Vulkan.SAMPLE_COUNT_1_BIT
+      , mbAllocator           = Nothing
+      , useDynamicRendering   = False
+      , colorAttachmentFormat = Nothing
+      , checkResult           = \case { Vulkan.SUCCESS -> pure (); e -> throw $ Vulkan.VulkanException e }
       }
 
   logDebug "Initialising ImGui SDL2 for Vulkan"
diff --git a/imgui/backends/imgui_impl_sdlrenderer2.cpp b/imgui/backends/imgui_impl_sdlrenderer2.cpp
new file mode 100644
--- /dev/null
+++ b/imgui/backends/imgui_impl_sdlrenderer2.cpp
@@ -0,0 +1,262 @@
+// dear imgui: Renderer Backend for SDL_Renderer for SDL2
+// (Requires: SDL 2.0.17+)
+
+// Note how SDL_Renderer is an _optional_ component of SDL2.
+// For a multi-platform app consider using e.g. SDL+DirectX on Windows and SDL+OpenGL on Linux/OSX.
+// If your application will want to render any non trivial amount of graphics other than UI,
+// please be aware that SDL_Renderer currently offers a limited graphic API to the end-user and
+// it might be difficult to step out of those boundaries.
+
+// Implemented features:
+//  [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID!
+//  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
+
+// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
+// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
+// Read online: https://github.com/ocornut/imgui/tree/master/docs
+
+// CHANGELOG
+//  2023-05-30: Renamed imgui_impl_sdlrenderer.h/.cpp to imgui_impl_sdlrenderer2.h/.cpp to accommodate for upcoming SDL3.
+//  2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
+//  2021-12-21: Update SDL_RenderGeometryRaw() format to work with SDL 2.0.19.
+//  2021-12-03: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
+//  2021-10-06: Backup and restore modified ClipRect/Viewport.
+//  2021-09-21: Initial version.
+
+#include "imgui.h"
+#ifndef IMGUI_DISABLE
+#include "imgui_impl_sdlrenderer2.h"
+#include <stdint.h>     // intptr_t
+
+// Clang warnings with -Weverything
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wsign-conversion"    // warning: implicit conversion changes signedness
+#endif
+
+// SDL
+#include <SDL.h>
+#if !SDL_VERSION_ATLEAST(2,0,17)
+#error This backend requires SDL 2.0.17+ because of SDL_RenderGeometry() function
+#endif
+
+// SDL_Renderer data
+struct ImGui_ImplSDLRenderer2_Data
+{
+    SDL_Renderer*   SDLRenderer;
+    SDL_Texture*    FontTexture;
+    ImGui_ImplSDLRenderer2_Data() { memset((void*)this, 0, sizeof(*this)); }
+};
+
+// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
+// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
+static ImGui_ImplSDLRenderer2_Data* ImGui_ImplSDLRenderer2_GetBackendData()
+{
+    return ImGui::GetCurrentContext() ? (ImGui_ImplSDLRenderer2_Data*)ImGui::GetIO().BackendRendererUserData : nullptr;
+}
+
+// Functions
+bool ImGui_ImplSDLRenderer2_Init(SDL_Renderer* renderer)
+{
+    ImGuiIO& io = ImGui::GetIO();
+    IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
+    IM_ASSERT(renderer != nullptr && "SDL_Renderer not initialized!");
+
+    // Setup backend capabilities flags
+    ImGui_ImplSDLRenderer2_Data* bd = IM_NEW(ImGui_ImplSDLRenderer2_Data)();
+    io.BackendRendererUserData = (void*)bd;
+    io.BackendRendererName = "imgui_impl_sdlrenderer2";
+    io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset;  // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
+
+    bd->SDLRenderer = renderer;
+
+    return true;
+}
+
+void ImGui_ImplSDLRenderer2_Shutdown()
+{
+    ImGui_ImplSDLRenderer2_Data* bd = ImGui_ImplSDLRenderer2_GetBackendData();
+    IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?");
+    ImGuiIO& io = ImGui::GetIO();
+
+    ImGui_ImplSDLRenderer2_DestroyDeviceObjects();
+
+    io.BackendRendererName = nullptr;
+    io.BackendRendererUserData = nullptr;
+    io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset;
+    IM_DELETE(bd);
+}
+
+static void ImGui_ImplSDLRenderer2_SetupRenderState()
+{
+	ImGui_ImplSDLRenderer2_Data* bd = ImGui_ImplSDLRenderer2_GetBackendData();
+
+	// Clear out any viewports and cliprect set by the user
+    // FIXME: Technically speaking there are lots of other things we could backup/setup/restore during our render process.
+	SDL_RenderSetViewport(bd->SDLRenderer, nullptr);
+	SDL_RenderSetClipRect(bd->SDLRenderer, nullptr);
+}
+
+void ImGui_ImplSDLRenderer2_NewFrame()
+{
+    ImGui_ImplSDLRenderer2_Data* bd = ImGui_ImplSDLRenderer2_GetBackendData();
+    IM_ASSERT(bd != nullptr && "Did you call ImGui_ImplSDLRenderer2_Init()?");
+
+    if (!bd->FontTexture)
+        ImGui_ImplSDLRenderer2_CreateDeviceObjects();
+}
+
+void ImGui_ImplSDLRenderer2_RenderDrawData(ImDrawData* draw_data)
+{
+	ImGui_ImplSDLRenderer2_Data* bd = ImGui_ImplSDLRenderer2_GetBackendData();
+
+	// If there's a scale factor set by the user, use that instead
+    // If the user has specified a scale factor to SDL_Renderer already via SDL_RenderSetScale(), SDL will scale whatever we pass
+    // to SDL_RenderGeometryRaw() by that scale factor. In that case we don't want to be also scaling it ourselves here.
+    float rsx = 1.0f;
+	float rsy = 1.0f;
+	SDL_RenderGetScale(bd->SDLRenderer, &rsx, &rsy);
+    ImVec2 render_scale;
+	render_scale.x = (rsx == 1.0f) ? draw_data->FramebufferScale.x : 1.0f;
+	render_scale.y = (rsy == 1.0f) ? draw_data->FramebufferScale.y : 1.0f;
+
+	// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
+	int fb_width = (int)(draw_data->DisplaySize.x * render_scale.x);
+	int fb_height = (int)(draw_data->DisplaySize.y * render_scale.y);
+	if (fb_width == 0 || fb_height == 0)
+		return;
+
+    // Backup SDL_Renderer state that will be modified to restore it afterwards
+    struct BackupSDLRendererState
+    {
+        SDL_Rect    Viewport;
+        bool        ClipEnabled;
+        SDL_Rect    ClipRect;
+    };
+    BackupSDLRendererState old = {};
+    old.ClipEnabled = SDL_RenderIsClipEnabled(bd->SDLRenderer) == SDL_TRUE;
+    SDL_RenderGetViewport(bd->SDLRenderer, &old.Viewport);
+    SDL_RenderGetClipRect(bd->SDLRenderer, &old.ClipRect);
+
+	// Will project scissor/clipping rectangles into framebuffer space
+	ImVec2 clip_off = draw_data->DisplayPos;         // (0,0) unless using multi-viewports
+	ImVec2 clip_scale = render_scale;
+
+    // Render command lists
+    ImGui_ImplSDLRenderer2_SetupRenderState();
+    for (int n = 0; n < draw_data->CmdListsCount; n++)
+    {
+        const ImDrawList* cmd_list = draw_data->CmdLists[n];
+        const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data;
+        const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data;
+
+        for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
+        {
+            const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
+            if (pcmd->UserCallback)
+            {
+                // User callback, registered via ImDrawList::AddCallback()
+                // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
+                if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
+                    ImGui_ImplSDLRenderer2_SetupRenderState();
+                else
+                    pcmd->UserCallback(cmd_list, pcmd);
+            }
+            else
+            {
+                // Project scissor/clipping rectangles into framebuffer space
+                ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y);
+                ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y);
+                if (clip_min.x < 0.0f) { clip_min.x = 0.0f; }
+                if (clip_min.y < 0.0f) { clip_min.y = 0.0f; }
+                if (clip_max.x > (float)fb_width) { clip_max.x = (float)fb_width; }
+                if (clip_max.y > (float)fb_height) { clip_max.y = (float)fb_height; }
+                if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
+                    continue;
+
+                SDL_Rect r = { (int)(clip_min.x), (int)(clip_min.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y) };
+                SDL_RenderSetClipRect(bd->SDLRenderer, &r);
+
+                const float* xy = (const float*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + IM_OFFSETOF(ImDrawVert, pos));
+                const float* uv = (const float*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + IM_OFFSETOF(ImDrawVert, uv));
+#if SDL_VERSION_ATLEAST(2,0,19)
+                const SDL_Color* color = (const SDL_Color*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + IM_OFFSETOF(ImDrawVert, col)); // SDL 2.0.19+
+#else
+                const int* color = (const int*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + IM_OFFSETOF(ImDrawVert, col)); // SDL 2.0.17 and 2.0.18
+#endif
+
+                // Bind texture, Draw
+				SDL_Texture* tex = (SDL_Texture*)pcmd->GetTexID();
+                SDL_RenderGeometryRaw(bd->SDLRenderer, tex,
+                    xy, (int)sizeof(ImDrawVert),
+                    color, (int)sizeof(ImDrawVert),
+                    uv, (int)sizeof(ImDrawVert),
+                    cmd_list->VtxBuffer.Size - pcmd->VtxOffset,
+                    idx_buffer + pcmd->IdxOffset, pcmd->ElemCount, sizeof(ImDrawIdx));
+            }
+        }
+    }
+
+    // Restore modified SDL_Renderer state
+    SDL_RenderSetViewport(bd->SDLRenderer, &old.Viewport);
+    SDL_RenderSetClipRect(bd->SDLRenderer, old.ClipEnabled ? &old.ClipRect : nullptr);
+}
+
+// Called by Init/NewFrame/Shutdown
+bool ImGui_ImplSDLRenderer2_CreateFontsTexture()
+{
+    ImGuiIO& io = ImGui::GetIO();
+    ImGui_ImplSDLRenderer2_Data* bd = ImGui_ImplSDLRenderer2_GetBackendData();
+
+    // Build texture atlas
+    unsigned char* pixels;
+    int width, height;
+    io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);   // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
+
+    // Upload texture to graphics system
+    // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
+    bd->FontTexture = SDL_CreateTexture(bd->SDLRenderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STATIC, width, height);
+    if (bd->FontTexture == nullptr)
+    {
+        SDL_Log("error creating texture");
+        return false;
+    }
+    SDL_UpdateTexture(bd->FontTexture, nullptr, pixels, 4 * width);
+    SDL_SetTextureBlendMode(bd->FontTexture, SDL_BLENDMODE_BLEND);
+    SDL_SetTextureScaleMode(bd->FontTexture, SDL_ScaleModeLinear);
+
+    // Store our identifier
+    io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontTexture);
+
+    return true;
+}
+
+void ImGui_ImplSDLRenderer2_DestroyFontsTexture()
+{
+    ImGuiIO& io = ImGui::GetIO();
+    ImGui_ImplSDLRenderer2_Data* bd = ImGui_ImplSDLRenderer2_GetBackendData();
+    if (bd->FontTexture)
+    {
+        io.Fonts->SetTexID(0);
+        SDL_DestroyTexture(bd->FontTexture);
+        bd->FontTexture = nullptr;
+    }
+}
+
+bool ImGui_ImplSDLRenderer2_CreateDeviceObjects()
+{
+    return ImGui_ImplSDLRenderer2_CreateFontsTexture();
+}
+
+void ImGui_ImplSDLRenderer2_DestroyDeviceObjects()
+{
+    ImGui_ImplSDLRenderer2_DestroyFontsTexture();
+}
+
+//-----------------------------------------------------------------------------
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
+#endif // #ifndef IMGUI_DISABLE
diff --git a/src/DearImGui.hs b/src/DearImGui.hs
--- a/src/DearImGui.hs
+++ b/src/DearImGui.hs
@@ -45,6 +45,7 @@
     -- * Windows
   , withWindow
   , withWindowOpen
+  , withCloseableWindow
   , withFullscreen
   , fullscreenFlags
 
@@ -278,6 +279,11 @@
   , Raw.beginTooltip
   , Raw.endTooltip
 
+    -- ** Disabled blocks
+  , withDisabled
+  , Raw.beginDisabled
+  , Raw.endDisabled
+
     -- * Popups/Modals
 
     -- ** Generic
@@ -335,6 +341,7 @@
   , Raw.getBackgroundDrawList
   , Raw.getForegroundDrawList
   , Raw.imCol32
+  , Raw.framerate
 
     -- * Types
   , module DearImGui.Enums
@@ -415,6 +422,26 @@
 withWindowOpen name action =
   withWindow name (`when` action)
 
+-- | Append items to a closeable window unless it is collapsed or fully clipped.
+--
+-- You may append multiple times to the same window during the same frame
+-- by calling 'withWindowOpen' in multiple places.
+--
+-- The 'Bool' state variable will be set to 'False' when the window's close
+-- button is pressed.
+withCloseableWindow :: (HasSetter ref Bool, MonadUnliftIO m) => Text -> ref -> m () -> m ()
+withCloseableWindow name ref action = bracket open close (`when` action)
+  where
+    open = liftIO do
+      with 1 \boolPtr -> do
+        Text.withCString name \namePtr -> do
+          isVisible <- Raw.begin namePtr (Just boolPtr) Nothing
+          isOpen <- peek boolPtr
+          when (isOpen == 0) $ ref $=! False
+          pure isVisible
+
+    close = liftIO . const Raw.end
+
 -- | Append items to a fullscreen window.
 --
 -- The action runs inside a window that is set to behave as a backdrop.
@@ -1740,6 +1767,17 @@
 -- Can contain any kind of items.
 withTooltip ::  MonadUnliftIO m => m a -> m a
 withTooltip = bracket_ Raw.beginTooltip Raw.endTooltip
+
+
+-- | Action wrapper for disabled blocks.
+--
+-- See 'Raw.beginDisabled' and 'Raw.endDisabled' for more info.
+withDisabled :: (MonadUnliftIO m, HasGetter ref Bool) => ref -> m a -> m a
+withDisabled disabledRef action = do
+  disabled <- get disabledRef
+  if disabled then bracket_ (Raw.beginDisabled 1) Raw.endDisabled action else action
+
+
 
 -- | Returns 'True' if the popup is open, and you can start outputting to it.
 --
diff --git a/src/DearImGui/Raw.hs b/src/DearImGui/Raw.hs
--- a/src/DearImGui/Raw.hs
+++ b/src/DearImGui/Raw.hs
@@ -65,6 +65,8 @@
   , setNextWindowSizeConstraints
   , setNextWindowCollapsed
   , setNextWindowBgAlpha
+  , beginDisabled
+  , endDisabled
 
     -- ** Child Windows
   , beginChild
@@ -249,6 +251,7 @@
   , getBackgroundDrawList
   , getForegroundDrawList
   , imCol32
+  , framerate
 
     -- * Types
   , module DearImGui.Enums
@@ -1581,6 +1584,29 @@
   [C.exp| void { SetNextWindowBgAlpha($(float alpha)) } |]
 
 
+-- | Begin a block that may be disabled. This disables all user interactions
+-- and dims item visuals.
+--
+-- Always call a matching 'endDisabled' for each 'beginDisabled' call.
+--
+-- The boolean argument is only intended to facilitate use of boolean
+-- expressions. If you can avoid calling @beginDisabled 0@ altogether,
+-- that should be preferred.
+--
+-- Wraps @ImGui::BeginDisabled()@
+beginDisabled :: (MonadIO m) => CBool -> m ()
+beginDisabled disabled = liftIO do
+  [C.exp| void { BeginDisabled($(bool disabled)) } |]
+
+
+-- | Ends a block that may be disabled.
+--
+-- Wraps @ImGui::EndDisabled()@
+endDisabled :: (MonadIO m) => m ()
+endDisabled = liftIO do
+  [C.exp| void { EndDisabled() } |]
+
+
 -- | undo a sameLine or force a new line when in an horizontal-layout context.
 --
 -- Wraps @ImGui::NewLine()@
@@ -1778,6 +1804,12 @@
 wantCaptureKeyboard :: MonadIO m => m Bool
 wantCaptureKeyboard = liftIO do
   (0 /=) <$> [C.exp| bool { GetIO().WantCaptureKeyboard } |]
+
+-- | Estimate of application framerate (rolling average over 60 frames), in
+-- frame per second. Solely for convenience.
+framerate :: MonadIO m => m Float
+framerate = liftIO do
+  realToFrac <$> [C.exp| float { GetIO().Framerate } |]
 
 -- | This draw list will be the first rendering one.
 --
diff --git a/src/DearImGui/Raw/IO.hs b/src/DearImGui/Raw/IO.hs
--- a/src/DearImGui/Raw/IO.hs
+++ b/src/DearImGui/Raw/IO.hs
@@ -120,14 +120,11 @@
 
 {- TODO:
 
-bool   WantCaptureMouse;         // Set when Dear ImGui will use mouse inputs, in this case do not dispatch them to your main game/application (either way, always pass on mouse inputs to imgui). (e.g. unclicked mouse is hovering over an imgui window, widget is active, mouse was clicked over an imgui window, etc.).
-bool   WantCaptureKeyboard;      // Set when Dear ImGui will use keyboard inputs, in this case do not dispatch them to your main game/application (either way, always pass keyboard inputs to imgui). (e.g. InputText active, or an imgui window is focused and navigation is enabled, etc.).
 bool   WantTextInput;            // Mobile/console: when set, you may display an on-screen keyboard. This is set by Dear ImGui when it wants textual keyboard input to happen (e.g. when a InputText widget is active).
 bool   WantSetMousePos;          // MousePos has been altered, backend should reposition mouse on next frame. Rarely used! Set only when ImGuiConfigFlags_NavEnableSetMousePos flag is enabled.
 bool   WantSaveIniSettings;      // When manual .ini load/save is active (io.IniFilename == NULL), this will be set to notify your application that you can call SaveIniSettingsToMemory() and save yourself. Important: clear io.WantSaveIniSettings yourself after saving!
 bool   NavActive;                // Keyboard/Gamepad navigation is currently allowed (will handle ImGuiKey_NavXXX events) = a window is focused and it doesn't use the ImGuiWindowFlags_NoNavInputs flag.
 bool   NavVisible;               // Keyboard/Gamepad navigation is visible and allowed (will handle ImGuiKey_NavXXX events).
-float  Framerate;                // Rough estimate of application framerate, in frame per second. Solely for convenience. Rolling average estimation based on io.DeltaTime over 120 frames.
 int    MetricsRenderVertices;    // Vertices output during last call to Render()
 int    MetricsRenderIndices;     // Indices output during last call to Render() = number of triangles * 3
 int    MetricsRenderWindows;     // Number of visible windows
diff --git a/src/DearImGui/SDL.hs b/src/DearImGui/SDL.hs
--- a/src/DearImGui/SDL.hs
+++ b/src/DearImGui/SDL.hs
@@ -21,12 +21,14 @@
   , sdl2Shutdown
   , pollEventWithImGui
   , pollEventsWithImGui
+    -- *** Raw
+  , dispatchRawEvent
   )
   where
 
 -- base
 import Control.Monad
-  ( when )
+  ( void, when )
 import Foreign.Marshal.Alloc
   ( alloca )
 import Foreign.Ptr
@@ -42,6 +44,7 @@
 import SDL
 import SDL.Raw.Enum as Raw
 import qualified SDL.Raw.Event as Raw
+import qualified SDL.Raw.Types as Raw
 
 -- transformers
 import Control.Monad.IO.Class
@@ -77,10 +80,23 @@
     nEvents <- Raw.peepEvents evPtr 1 Raw.SDL_PEEKEVENT Raw.SDL_FIRSTEVENT Raw.SDL_LASTEVENT
 
     when (nEvents > 0) do
-      let evPtr' = castPtr evPtr :: Ptr ()
-      [C.exp| void { ImGui_ImplSDL2_ProcessEvent((SDL_Event*) $(void* evPtr')) } |]
+      void $ dispatchRawEvent evPtr
 
     pollEvent
+
+-- | Dispatch a raw 'Raw.Event' value to Dear ImGui.
+--
+-- You may want this function instead of 'pollEventWithImGui' if you do not use
+-- @sdl2@'s higher-level 'Event' type (e.g. your application has its own polling
+-- mechanism).
+--
+-- __It is your application's responsibility to both manage the input__
+-- __pointer's memory and to fill the memory location with a raw 'Raw.Event'__
+-- __value.__
+dispatchRawEvent :: MonadIO m => Ptr Raw.Event -> m Bool
+dispatchRawEvent evPtr = liftIO do
+  let evPtr' = castPtr evPtr :: Ptr ()
+  (0 /=) <$> [C.exp| bool { ImGui_ImplSDL2_ProcessEvent((const SDL_Event*) $(void* evPtr')) } |]
 
 -- | Like the SDL2 'pollEvents' function, while also dispatching the events to
 -- Dear ImGui. See 'pollEventWithImGui'.
diff --git a/src/DearImGui/SDL/Renderer.hs b/src/DearImGui/SDL/Renderer.hs
new file mode 100644
--- /dev/null
+++ b/src/DearImGui/SDL/Renderer.hs
@@ -0,0 +1,74 @@
+{-# LANGUAGE BlockArguments #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE PatternSynonyms #-}
+{-# LANGUAGE QuasiQuotes #-}
+{-# LANGUAGE TemplateHaskell #-}
+
+{-|
+Module: DearImGUI.SDL.Renderer
+
+Initialising the SDL2 renderer backend for Dear ImGui.
+-}
+
+module DearImGui.SDL.Renderer
+  ( sdl2InitForSDLRenderer
+  , sdlRendererInit
+  , sdlRendererShutdown
+  , sdlRendererNewFrame
+  , sdlRendererRenderDrawData
+  )
+  where
+
+-- inline-c
+import qualified Language.C.Inline as C
+
+-- inline-c-cpp
+import qualified Language.C.Inline.Cpp as Cpp
+
+-- sdl2
+import SDL.Internal.Types
+  ( Renderer(..), Window(..) )
+
+-- transformers
+import Control.Monad.IO.Class
+  ( MonadIO, liftIO )
+
+-- DearImGui
+import DearImGui
+  ( DrawData(..) )
+
+
+C.context (Cpp.cppCtx <> C.bsCtx)
+C.include "imgui.h"
+C.include "backends/imgui_impl_sdlrenderer2.h"
+C.include "backends/imgui_impl_sdl2.h"
+C.include "SDL.h"
+Cpp.using "namespace ImGui"
+
+
+-- | Wraps @ImGui_ImplSDL2_InitForSDLRenderer@.
+sdl2InitForSDLRenderer :: MonadIO m => Window -> Renderer -> m Bool
+sdl2InitForSDLRenderer (Window windowPtr) (Renderer renderPtr) = liftIO do
+  (0 /=) <$> [C.exp| bool { ImGui_ImplSDL2_InitForSDLRenderer((SDL_Window*)$(void* windowPtr), (SDL_Renderer*)$(void* renderPtr)) } |]
+
+-- | Wraps @ImGui_ImplSDLRenderer2_Init@.
+sdlRendererInit :: MonadIO m => Renderer -> m Bool
+sdlRendererInit (Renderer renderPtr) = liftIO do
+  (0 /=) <$> [C.exp| bool { ImGui_ImplSDLRenderer2_Init((SDL_Renderer*)$(void* renderPtr)) } |]
+
+-- | Wraps @ImGui_ImplSDLRenderer2_Shutdown@.
+sdlRendererShutdown :: MonadIO m => m ()
+sdlRendererShutdown = liftIO do
+  [C.exp| void { ImGui_ImplSDLRenderer2_Shutdown(); } |]
+
+-- | Wraps @ImGui_ImplSDLRenderer2_NewFrame@.
+sdlRendererNewFrame :: MonadIO m => m ()
+sdlRendererNewFrame = liftIO do
+  [C.exp| void { ImGui_ImplSDLRenderer2_NewFrame(); } |]
+
+-- | Wraps @ImGui_ImplSDLRenderer2_RenderDrawData@.
+sdlRendererRenderDrawData :: MonadIO m => DrawData -> m ()
+sdlRendererRenderDrawData (DrawData ptr) = liftIO do
+  [C.exp| void { ImGui_ImplSDLRenderer2_RenderDrawData((ImDrawData*) $( void* ptr )) } |]
diff --git a/src/DearImGui/Vulkan.hs b/src/DearImGui/Vulkan.hs
--- a/src/DearImGui/Vulkan.hs
+++ b/src/DearImGui/Vulkan.hs
@@ -31,6 +31,8 @@
   ( Word32 )
 import Foreign.Marshal.Alloc
   ( alloca )
+import Foreign.Marshal.Utils
+  ( fromBool )
 import Foreign.Ptr
   ( FunPtr, Ptr, freeHaskellFunPtr, nullPtr )
 import Foreign.Storable
@@ -70,19 +72,21 @@
 
 data InitInfo =
   InitInfo
-  { instance'      :: !Vulkan.Instance
-  , physicalDevice :: !Vulkan.PhysicalDevice
-  , device         :: !Vulkan.Device
-  , queueFamily    :: !Word32
-  , queue          :: !Vulkan.Queue
-  , pipelineCache  :: !Vulkan.PipelineCache
-  , descriptorPool :: !Vulkan.DescriptorPool
-  , subpass        :: !Word32
-  , minImageCount  :: !Word32
-  , imageCount     :: !Word32
-  , msaaSamples    :: !Vulkan.SampleCountFlagBits
-  , mbAllocator    :: Maybe Vulkan.AllocationCallbacks
-  , checkResult    :: Vulkan.Result -> IO ()
+  { instance'             :: !Vulkan.Instance
+  , physicalDevice        :: !Vulkan.PhysicalDevice
+  , device                :: !Vulkan.Device
+  , queueFamily           :: !Word32
+  , queue                 :: !Vulkan.Queue
+  , pipelineCache         :: !Vulkan.PipelineCache
+  , descriptorPool        :: !Vulkan.DescriptorPool
+  , subpass               :: !Word32
+  , minImageCount         :: !Word32
+  , imageCount            :: !Word32
+  , msaaSamples           :: !Vulkan.SampleCountFlagBits
+  , colorAttachmentFormat :: !(Maybe Vulkan.Format)
+  , useDynamicRendering   :: !Bool
+  , mbAllocator           :: Maybe Vulkan.AllocationCallbacks
+  , checkResult           :: Vulkan.Result -> IO ()
   }
 
 -- | Wraps @ImGui_ImplVulkan_Init@ and @ImGui_ImplVulkan_Shutdown@.
@@ -112,6 +116,10 @@
     withCallbacks f = case mbAllocator of
       Nothing        -> f nullPtr
       Just callbacks -> alloca ( \ ptr -> poke ptr callbacks *> f ptr )
+    useDynamicRendering' :: Cpp.CBool
+    useDynamicRendering' = fromBool useDynamicRendering
+    colorAttachmentFormat' :: Vulkan.Format
+    colorAttachmentFormat' = fromMaybe Vulkan.FORMAT_UNDEFINED colorAttachmentFormat
   liftIO do
     checkResultFunPtr <- $( C.mkFunPtr [t| Vulkan.Result -> IO () |] ) checkResult
     initResult <- withCallbacks \ callbacksPtr ->
@@ -134,8 +142,8 @@
           initInfo.MSAASamples = $(VkSampleCountFlagBits msaaSamples);
           initInfo.Allocator = $(VkAllocationCallbacks* callbacksPtr);
           initInfo.CheckVkResultFn = $( void (*checkResultFunPtr)(VkResult) );
-          initInfo.UseDynamicRendering = false;
-          // TODO: initInfo.ColorAttachmentFormat
+          initInfo.UseDynamicRendering = $(bool useDynamicRendering');
+          initInfo.ColorAttachmentFormat = $(VkFormat colorAttachmentFormat');
           return ImGui_ImplVulkan_Init(&initInfo, $(VkRenderPass renderPass) );
         }|]
     pure ( checkResultFunPtr, initResult /= 0 )
diff --git a/src/DearImGui/Vulkan/Types.hs b/src/DearImGui/Vulkan/Types.hs
--- a/src/DearImGui/Vulkan/Types.hs
+++ b/src/DearImGui/Vulkan/Types.hs
@@ -35,6 +35,7 @@
   , ( C.TypeName "VkImageView"          , [t| Vulkan.ImageView           |] )
   , ( C.TypeName "VkImageLayout"        , [t| Vulkan.ImageLayout         |] )
   , ( C.TypeName "VkDescriptorSet"      , [t| Vulkan.DescriptorSet       |] )
+  , ( C.TypeName "VkFormat"             , [t| Vulkan.Format              |])
   ]
 
 vulkanCtx :: C.Context
