diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,14 @@
 # Changelog for keid-core
 
+## 0.1.4.0
+
+- Changed `Render.Samplers.allocate` to use `maxAnisotropy` directly.
+- Relaxed `DescriptorSet.allocatePool` to any `MonadResource`.
+- Added KHR_Surface to headless requirements.
+- Added `Render.Pass.setViewportScissor`.
+- Added TRANSFER_SRC to Offscreen image usage.
+- Published `Render.Samplers.params`.
+
 ## 0.1.3.1
 
 - Added `Engine.Setup.setupHeadless` to use without a surface.
diff --git a/keid-core.cabal b/keid-core.cabal
--- a/keid-core.cabal
+++ b/keid-core.cabal
@@ -5,7 +5,7 @@
 -- see: https://github.com/sol/hpack
 
 name:           keid-core
-version:        0.1.3.1
+version:        0.1.4.0
 synopsis:       Core parts of Keid engine.
 category:       Game Engine
 author:         IC Rainbow
diff --git a/src/Engine/Setup.hs b/src/Engine/Setup.hs
--- a/src/Engine/Setup.hs
+++ b/src/Engine/Setup.hs
@@ -7,6 +7,7 @@
 import Vulkan.Core10 qualified as Vk
 import Vulkan.Extensions.VK_EXT_debug_utils qualified as Ext
 import Vulkan.Extensions.VK_KHR_get_physical_device_properties2 qualified as Khr
+import Vulkan.Extensions.VK_KHR_surface qualified as Khr
 import Vulkan.Requirement (InstanceRequirement(..))
 import Vulkan.Utils.Initialization (createInstanceFromRequirements)
 import Vulkan.Utils.QueueAssignment (QueueFamilyIndex(..))
@@ -89,11 +90,9 @@
 setupHeadless opts = do
   logDebug $ displayShow opts
 
-  let windowReqs = []
-
   logDebug "Creating instance"
   ghInstance <- createInstanceFromRequirements
-    (deviceProps : debugUtils : windowReqs)
+    (deviceProps : debugUtils : headlessReqs)
     mempty
     zero
 
@@ -142,6 +141,14 @@
   Nothing
   Ext.EXT_DEBUG_UTILS_EXTENSION_NAME
   minBound
+
+headlessReqs :: [InstanceRequirement]
+headlessReqs =
+  [ RequireInstanceExtension
+      Nothing
+      Khr.KHR_SURFACE_EXTENSION_NAME
+      minBound
+  ]
 
 τ :: Float
 τ = 2 * pi
diff --git a/src/Render/Pass.hs b/src/Render/Pass.hs
--- a/src/Render/Pass.hs
+++ b/src/Render/Pass.hs
@@ -1,5 +1,8 @@
+{-# LANGUAGE OverloadedLists #-}
+
 module Render.Pass
   ( usePass
+  , setViewportScissor
 
   , beginInfo
   ) where
@@ -26,3 +29,30 @@
   , Vk.renderArea  = getRenderArea rp
   , Vk.clearValues = getClearValues rp
   }
+
+setViewportScissor
+  :: ( HasRenderPass rp
+     , MonadIO io
+     )
+  => Vk.CommandBuffer
+  -> Vk.Extent2D
+  -> rp
+  -> io ()
+setViewportScissor cb Vk.Extent2D{width, height} rp = do
+  Vk.cmdSetViewport
+    cb
+    0
+    [ Vk.Viewport
+        { x        = 0
+        , y        = 0
+        , width    = realToFrac width
+        , height   = realToFrac height
+        , minDepth = 0
+        , maxDepth = 1
+        }
+    ]
+  Vk.cmdSetScissor
+    cb
+    0
+    [ getRenderArea rp
+    ]
diff --git a/src/Render/Pass/Offscreen.hs b/src/Render/Pass/Offscreen.hs
--- a/src/Render/Pass/Offscreen.hs
+++ b/src/Render/Pass/Offscreen.hs
@@ -280,7 +280,10 @@
         sLayers
         sSamples
         sFormat
-        (Vk.IMAGE_USAGE_COLOR_ATTACHMENT_BIT .|. Vk.IMAGE_USAGE_SAMPLED_BIT)
+        ( Vk.IMAGE_USAGE_COLOR_ATTACHMENT_BIT .|.
+          Vk.IMAGE_USAGE_SAMPLED_BIT .|.
+          Vk.IMAGE_USAGE_TRANSFER_SRC_BIT
+        )
     )
     (Image.destroy context)
 
diff --git a/src/Render/Samplers.hs b/src/Render/Samplers.hs
--- a/src/Render/Samplers.hs
+++ b/src/Render/Samplers.hs
@@ -6,6 +6,7 @@
   , indices
 
   , Params
+  , params
   , create
   , destroy
   ) where
@@ -21,7 +22,7 @@
 import Vulkan.NamedType ((:::))
 import Vulkan.Zero (zero)
 
-import Engine.Vulkan.Types (HasSwapchain(..), HasVulkan(..), MonadVulkan)
+import Engine.Vulkan.Types (HasVulkan(..), MonadVulkan)
 import Resource.Collection qualified as Collection
 
 data Collection a = Collection
@@ -61,14 +62,13 @@
 allocate
   :: ( MonadVulkan env m
      , Resource.MonadResource m
-     , HasSwapchain a
      )
-  => a
+  => "max anisotropy" ::: Float
   -> m (Resource.ReleaseKey, Collection Vk.Sampler)
-allocate swapchain = do
+allocate maxAnisotropy = do
   context <- ask
   Resource.allocate
-    (for params $ create context $ getAnisotropy swapchain)
+    (for params $ create context maxAnisotropy)
     (traverse_ $ destroy context)
 
 create
diff --git a/src/Resource/CommandBuffer.hs b/src/Resource/CommandBuffer.hs
--- a/src/Resource/CommandBuffer.hs
+++ b/src/Resource/CommandBuffer.hs
@@ -51,8 +51,8 @@
   -> (forall a . Queues a -> a)
   -> (Vk.CommandBuffer -> m ())
   -> m ()
-oneshot_ context commandQueues pickQueue action =
-  Vk.withCommandBuffers device commandBufferAllocateInfo bracket $ \case
+oneshot_ context pools pickQueue action =
+  Vk.withCommandBuffers device commandBufferAllocateInfo bracket \case
     (Vector.toList -> [buf]) -> do
       let
         oneTime :: Vk.CommandBufferBeginInfo '[]
@@ -62,23 +62,23 @@
 
       Vk.useCommandBuffer buf oneTime $ action buf
 
-      Vk.withFence device zero Nothing bracket $ \fence -> do
-        Vk.queueSubmit transferQueue (wrap buf) fence
+      Vk.withFence device zero Nothing bracket \fence -> do
+        Vk.queueSubmit queue (wrap buf) fence
         Vk.waitForFences device (pure fence) True maxBound >>= \case
           Vk.SUCCESS ->
-            -- traceM "oneshot_: transfer finished"
+            -- traceM "oneshot_: queue finished"
             pure ()
           err ->
-            error $ "copyBuffer failed: " <> show err
+            error $ "oneshot_ failed: " <> show err
     _ ->
       error "assert: exactly the requested buffer was given"
   where
     device = getDevice context
-    transferQueue = snd . pickQueue $ getQueues context
+    queue = snd . pickQueue $ getQueues context
 
     commandBufferAllocateInfo :: Vk.CommandBufferAllocateInfo
     commandBufferAllocateInfo = zero
-      { Vk.commandPool        = pickQueue commandQueues
+      { Vk.commandPool        = pickQueue pools
       , Vk.level              = Vk.COMMAND_BUFFER_LEVEL_PRIMARY
       , Vk.commandBufferCount = 1
       }
diff --git a/src/Resource/DescriptorSet.hs b/src/Resource/DescriptorSet.hs
--- a/src/Resource/DescriptorSet.hs
+++ b/src/Resource/DescriptorSet.hs
@@ -14,8 +14,13 @@
 import Engine.Vulkan.Types (HasVulkan(getDevice))
 
 allocatePool
-  :: (Resource.MonadResource m, MonadReader env m, HasVulkan env)
-  => Word32 -> TypeMap Word32 -> m (Resource.ReleaseKey, Vk.DescriptorPool)
+  :: ( Resource.MonadResource m
+     , MonadReader env m
+     , HasVulkan env
+     )
+  => Word32
+  -> TypeMap Word32
+  -> m (Resource.ReleaseKey, Vk.DescriptorPool)
 allocatePool maxSets sizes = do
   device <- asks getDevice
   Vk.withDescriptorPool device (mkPoolCI maxSets sizes) Nothing Resource.allocate
