packages feed

keid-core 0.1.2.0 → 0.1.3.0

raw patch · 12 files changed

+646/−174 lines, 12 filesdep +serialisePVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: serialise

API changes (from Hackage documentation)

- Engine.Vulkan.Types: instance Engine.Vulkan.Types.HasSwapchain context => Engine.Vulkan.Types.HasSwapchain (a, context)
- Resource.Image: createColorResource :: (MonadIO io, HasVulkan ctx, HasSwapchain ctx) => ctx -> Extent2D -> io AllocatedImage
- Resource.Image: createDepthResource :: (MonadIO io, HasVulkan context, HasSwapchain context) => context -> Extent2D -> ("shadowmap layers" ::: Maybe Word32) -> io AllocatedImage
- Resource.Image: destroyAllocatedImage :: (MonadIO io, HasVulkan context) => context -> AllocatedImage -> io ()
- Resource.Image: transitionImageLayout :: HasVulkan context => context -> Queues CommandPool -> Image -> ("mip levels" ::: Word32) -> ("layer count" ::: Word32) -> Format -> ("old" ::: ImageLayout) -> ("new" ::: ImageLayout) -> RIO env ()
+ Render.Pass: beginInfo :: HasRenderPass a => a -> Word32 -> RenderPassBeginInfo '[]
+ Render.Pass: usePass :: (MonadIO io, HasRenderPass a) => a -> Word32 -> CommandBuffer -> io r -> io r
+ Render.Pass.Offscreen: Offscreen :: RenderPass -> Extent2D -> AllocatedImage -> AllocatedImage -> Word32 -> Word32 -> Framebuffer -> Rect2D -> Vector ClearValue -> RefCounted -> Offscreen
+ Render.Pass.Offscreen: Settings :: Text -> Extent2D -> Format -> Format -> Word32 -> Bool -> SampleCountFlagBits -> Bool -> Settings
+ Render.Pass.Offscreen: [$sel:oClear:Offscreen] :: Offscreen -> Vector ClearValue
+ Render.Pass.Offscreen: [$sel:oColor:Offscreen] :: Offscreen -> AllocatedImage
+ Render.Pass.Offscreen: [$sel:oDepth:Offscreen] :: Offscreen -> AllocatedImage
+ Render.Pass.Offscreen: [$sel:oExtent:Offscreen] :: Offscreen -> Extent2D
+ Render.Pass.Offscreen: [$sel:oFrameBuffer:Offscreen] :: Offscreen -> Framebuffer
+ Render.Pass.Offscreen: [$sel:oLayers:Offscreen] :: Offscreen -> Word32
+ Render.Pass.Offscreen: [$sel:oMipLevels:Offscreen] :: Offscreen -> Word32
+ Render.Pass.Offscreen: [$sel:oRelease:Offscreen] :: Offscreen -> RefCounted
+ Render.Pass.Offscreen: [$sel:oRenderArea:Offscreen] :: Offscreen -> Rect2D
+ Render.Pass.Offscreen: [$sel:oRenderPass:Offscreen] :: Offscreen -> RenderPass
+ Render.Pass.Offscreen: [$sel:sDepthFormat:Settings] :: Settings -> Format
+ Render.Pass.Offscreen: [$sel:sExtent:Settings] :: Settings -> Extent2D
+ Render.Pass.Offscreen: [$sel:sFormat:Settings] :: Settings -> Format
+ Render.Pass.Offscreen: [$sel:sLabel:Settings] :: Settings -> Text
+ Render.Pass.Offscreen: [$sel:sLayers:Settings] :: Settings -> Word32
+ Render.Pass.Offscreen: [$sel:sMipMap:Settings] :: Settings -> Bool
+ Render.Pass.Offscreen: [$sel:sMultiView:Settings] :: Settings -> Bool
+ Render.Pass.Offscreen: [$sel:sSamples:Settings] :: Settings -> SampleCountFlagBits
+ Render.Pass.Offscreen: allocate :: (MonadResource m, MonadVulkan env m, HasLogFunc env) => Settings -> m Offscreen
+ Render.Pass.Offscreen: colorCube :: Offscreen -> Texture CubeMap
+ Render.Pass.Offscreen: colorTexture :: Offscreen -> Texture Flat
+ Render.Pass.Offscreen: data Offscreen
+ Render.Pass.Offscreen: data Settings
+ Render.Pass.Offscreen: depthCube :: Offscreen -> Texture CubeMap
+ Render.Pass.Offscreen: depthTexture :: Offscreen -> Texture Flat
+ Render.Pass.Offscreen: instance Engine.Vulkan.Types.HasRenderPass Render.Pass.Offscreen.Offscreen
+ Render.Pass.Offscreen: instance Engine.Vulkan.Types.RenderPass Render.Pass.Offscreen.Offscreen
+ Render.Pass.Offscreen: instance GHC.Classes.Eq Render.Pass.Offscreen.Settings
+ Render.Pass.Offscreen: instance GHC.Show.Show Render.Pass.Offscreen.Settings
+ Resource.Image: [$sel:aiFormat:AllocatedImage] :: AllocatedImage -> Format
+ Resource.Image: [$sel:aiImageRange:AllocatedImage] :: AllocatedImage -> ImageSubresourceRange
+ Resource.Image: create :: (MonadIO io, HasVulkan ctx) => ctx -> Maybe Text -> ImageAspectFlags -> Extent2D -> ("mip levels" ::: Word32) -> ("stored layers" ::: Word32) -> SampleCountFlagBits -> Format -> ImageUsageFlags -> io AllocatedImage
+ Resource.Image: destroy :: (MonadIO io, HasVulkan context) => context -> AllocatedImage -> io ()
+ Resource.Image: transitionLayout :: (HasVulkan context, MonadUnliftIO m) => context -> Queues CommandPool -> Image -> ("mip levels" ::: Word32) -> ("layer count" ::: Word32) -> Format -> ("old" ::: ImageLayout) -> ("new" ::: ImageLayout) -> m ()
+ Resource.Mesh.Codec: decodeCBOR :: (Serialise a, MonadFail m) => String -> ByteString -> Int -> ByteString -> m a
+ Resource.Mesh.Codec: encodeCBOR :: Serialise a => a -> (Int, ByteString, ByteString)
+ Resource.Mesh.Types: decodeStorable :: forall a s. (Storable a, Typeable a) => Decoder s a
+ Resource.Mesh.Types: encodeStorable :: forall a. Storable a => a -> Encoding
+ Resource.Mesh.Types: instance Codec.Serialise.Class.Serialise Resource.Mesh.Types.Measurements
+ Resource.Mesh.Types: instance Codec.Serialise.Class.Serialise Resource.Mesh.Types.Meta
+ Resource.Mesh.Types: instance Codec.Serialise.Class.Serialise a => Codec.Serialise.Class.Serialise (Resource.Mesh.Types.AxisAligned a)
+ Resource.Model: instance Codec.Serialise.Class.Serialise Resource.Model.IndexRange
+ Resource.Model: instance GHC.Generics.Generic Resource.Model.IndexRange
- Resource.Image: AllocatedImage :: Allocation -> Image -> ImageView -> AllocatedImage
+ Resource.Image: AllocatedImage :: Allocation -> Format -> Image -> ImageView -> ImageSubresourceRange -> AllocatedImage
- Resource.Image: copyBufferToImage :: (HasVulkan context, Foldable t, Integral deviceSize) => context -> Queues CommandPool -> Buffer -> Image -> ("base extent" ::: Extent3D) -> ("mip offsets" ::: t deviceSize) -> ("layer count" ::: Word32) -> RIO env ()
+ Resource.Image: copyBufferToImage :: (HasVulkan context, Foldable t, Integral deviceSize, MonadUnliftIO m) => context -> Queues CommandPool -> Buffer -> Image -> ("base extent" ::: Extent3D) -> ("mip offsets" ::: t deviceSize) -> ("layer count" ::: Word32) -> m ()
- Resource.Mesh.Codec: encodeFile :: forall vp vi va vn attrs nodes meta env. (Vector vp Packed, Vector vi Word32, Vector va attrs, Vector vn nodes, Storable attrs, Storable nodes, Storable meta, HasLogFunc env) => FilePath -> vp Packed -> vi Word32 -> va attrs -> vn nodes -> meta -> RIO env ()
+ Resource.Mesh.Codec: encodeFile :: forall vp vi va vn attrs nodes meta env. (Vector vp Packed, Vector vi Word32, Vector va attrs, Vector vn nodes, Storable attrs, Storable nodes, Serialise meta, HasLogFunc env) => FilePath -> vp Packed -> vi Word32 -> va attrs -> vn nodes -> meta -> RIO env ()
- Resource.Mesh.Codec: loadBlobs :: forall attrs env nodes meta. (Storable attrs, Storable meta, Storable nodes, Typeable nodes, HasLogFunc env) => FilePath -> RIO env (meta, Vector nodes, (Vector Packed, Vector Word32, Vector attrs))
+ Resource.Mesh.Codec: loadBlobs :: forall attrs env nodes meta m. (Storable attrs, Serialise meta, Storable nodes, Typeable nodes, HasLogFunc env, MonadReader env m, MonadIO m) => FilePath -> m (meta, Vector nodes, (Vector Packed, Vector Word32, Vector attrs))
- Resource.Mesh.Codec: loadIndexed :: (Storable attrs, Storable nodes, Storable meta, Show meta, Typeable nodes, HasVulkan env, HasLogFunc env, MonadResource (RIO env)) => Queues CommandPool -> FilePath -> RIO env (ReleaseKey, (meta, Vector nodes, Indexed 'Staged Packed attrs))
+ Resource.Mesh.Codec: loadIndexed :: (Storable attrs, Storable nodes, Serialise meta, Show meta, Typeable nodes, HasLogFunc env, MonadResource m, MonadVulkan env m) => Queues CommandPool -> FilePath -> m (ReleaseKey, (meta, Vector nodes, Indexed 'Staged Packed attrs))
- Resource.Texture.Ktx1: load :: (TextureLayers a, Typeable a, HasCallStack) => Queues CommandPool -> Source -> StageRIO st (Texture a)
+ Resource.Texture.Ktx1: load :: (TextureLayers a, MonadVulkan env m, MonadThrow m, HasLogFunc env, Typeable a, HasCallStack) => Queues CommandPool -> Source -> m (Texture a)
- Resource.Texture.Ktx1: loadBytes :: TextureLayers a => Queues CommandPool -> ByteString -> StageRIO st (Texture a)
+ Resource.Texture.Ktx1: loadBytes :: (TextureLayers a, MonadVulkan env m, MonadThrow m, HasLogFunc env) => Queues CommandPool -> ByteString -> m (Texture a)
- Resource.Texture.Ktx1: loadKtx1 :: forall a st. TextureLayers a => Queues CommandPool -> Ktx -> StageRIO st (Texture a)
+ Resource.Texture.Ktx1: loadKtx1 :: forall a m env. (TextureLayers a, MonadVulkan env m, MonadThrow m, HasLogFunc env) => Queues CommandPool -> Ktx -> m (Texture a)

Files

ChangeLog.md view
@@ -1,5 +1,15 @@ # Changelog for keid-core +## 0.1.3.0++- Changed mesh codec to use `Serialise` class instead of `Storable`.+- Replaced RIO in resource loaders with constraints.+- Added `Render.Pass` base module and `Render.Pass.Offscreen`.+- `Resource.Image.AllocatedImage` got format and subresource information.+- Removed `Resource.Image.createColorResource`, `Resource.Image.createDepthResource`.+  * Added unified `Resource.Image.create` instead.+  * Function names shortened to match qulified import style.+ ## 0.1.2.0  - Added `Resource.Source` type to mix external and embedded byte sources.
keid-core.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack  name:           keid-core-version:        0.1.2.0+version:        0.1.3.0 synopsis:       Core parts of Keid engine. category:       Game Engine author:         IC Rainbow@@ -57,6 +57,8 @@       Render.Code       Render.Code.Noise       Render.Draw+      Render.Pass+      Render.Pass.Offscreen       Render.Samplers       Resource.Buffer       Resource.Collection@@ -157,6 +159,7 @@     , resourcet     , rio >=0.1.12.0     , rio-app+    , serialise     , tagged     , template-haskell     , text
src/Engine/Vulkan/Types.hs view
@@ -58,16 +58,6 @@   getDevice             = getDevice . fst   getAllocator          = getAllocator . fst -instance (HasSwapchain context) => HasSwapchain (a, context) where-  getSurfaceExtent  = getSurfaceExtent . snd-  getSurfaceFormat  = getSurfaceFormat . snd-  getDepthFormat    = getDepthFormat . snd-  getMultisample    = getMultisample . snd-  getAnisotropy     = getAnisotropy . snd-  getSwapchainViews = getSwapchainViews . snd-  getMinImageCount  = getMinImageCount . snd-  getImageCount     = getImageCount . snd- -- TODO getPipelineCache :: {- HasVulkan ctx => -} ctx -> Vk.PipelineCache getPipelineCache _ctx = Vk.NULL_HANDLE
+ src/Render/Pass.hs view
@@ -0,0 +1,28 @@+module Render.Pass+  ( usePass++  , beginInfo+  ) where++import RIO++import RIO.Vector.Partial ((!))+import Vulkan.Core10 qualified as Vk+import Vulkan.Zero (zero)++import Engine.Vulkan.Types (HasRenderPass(..))++usePass :: (MonadIO io, HasRenderPass a) => a -> Word32 -> Vk.CommandBuffer -> io r -> io r+usePass render imageIndex cb =+  Vk.cmdUseRenderPass+    cb+    (beginInfo render imageIndex)+    Vk.SUBPASS_CONTENTS_INLINE++beginInfo :: HasRenderPass a => a -> Word32 -> Vk.RenderPassBeginInfo '[]+beginInfo rp imageIndex = zero+  { Vk.renderPass  = getRenderPass rp+  , Vk.framebuffer = getFramebuffers rp ! fromIntegral imageIndex+  , Vk.renderArea  = getRenderArea rp+  , Vk.clearValues = getClearValues rp+  }
+ src/Render/Pass/Offscreen.hs view
@@ -0,0 +1,347 @@+{-# LANGUAGE OverloadedLists #-}++module Render.Pass.Offscreen+  ( Settings(..)+  , allocate+  , Offscreen(..)+  , colorTexture+  , colorCube+  , depthTexture+  , depthCube+  ) where++import RIO++import Control.Monad.Trans.Resource qualified as Resource+import Data.Bits ((.|.))+import Data.Vector qualified as Vector+import Vulkan.Core10 qualified as Vk+import Vulkan.Core11.Promoted_From_VK_KHR_multiview qualified as Khr+import Vulkan.CStruct.Extends (pattern (:&), pattern (::&))+import Vulkan.Utils.Debug qualified as Debug+import Vulkan.Zero (zero)++import Engine.Types.RefCounted (RefCounted, newRefCounted, resourceTRefCount)+import Engine.Vulkan.Types (HasVulkan(..), HasRenderPass(..), RenderPass(..), MonadVulkan)+import Resource.Image (AllocatedImage)+import Resource.Image qualified as Image+import Resource.Texture (CubeMap, Flat, Texture(..))++{- XXX: Consider spec wrt. parameters and intended use!++https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkCmdBlitImage.html+-}+data Settings = Settings+  { sLabel       :: Text+  , sExtent      :: Vk.Extent2D+  , sFormat      :: Vk.Format+  , sDepthFormat :: Vk.Format+  , sLayers      :: Word32+  , sMultiView   :: Bool -- ^ Makes sense only for multiple layers.+  , sSamples     :: Vk.SampleCountFlagBits -- ^ Multisample prevents mipmapping and cubes.+  , sMipMap      :: Bool+  }+  deriving (Eq, Show)++data Offscreen = Offscreen+  { oRenderPass  :: Vk.RenderPass+  , oExtent      :: Vk.Extent2D+  , oColor       :: AllocatedImage+  , oDepth       :: AllocatedImage+  , oLayers      :: Word32+  , oMipLevels   :: Word32++  , oFrameBuffer :: Vk.Framebuffer+  , oRenderArea  :: Vk.Rect2D+  , oClear       :: Vector Vk.ClearValue+  , oRelease     :: RefCounted+  }++instance HasRenderPass Offscreen where+  getRenderPass   = oRenderPass+  getFramebuffers = Vector.replicate 10 . oFrameBuffer+  getClearValues  = oClear+  getRenderArea   = oRenderArea++instance RenderPass Offscreen where+  allocateRenderpass_ = error "Don't allocate Offscreen via class"+  updateRenderpass    = error "Don't recreate Offscreen on resize"++  refcountRenderpass = resourceTRefCount . oRelease++colorTexture :: Offscreen -> Texture Flat+colorTexture Offscreen{..} = Texture+  { tFormat         = Image.aiFormat oColor+  , tMipLevels      = oMipLevels+  , tLayers         = oLayers+  , tAllocatedImage = oColor+  }++colorCube :: Offscreen -> Texture CubeMap+colorCube Offscreen{..} = Texture+  { tFormat         = Image.aiFormat oColor+  , tMipLevels      = oMipLevels+  , tLayers         = oLayers -- TODO: get from type param+  , tAllocatedImage = oColor+  }++depthTexture :: Offscreen -> Texture Flat+depthTexture Offscreen{..} = Texture+  { tFormat         = Image.aiFormat oDepth+  , tMipLevels      = oMipLevels+  , tLayers         = oLayers+  , tAllocatedImage = oDepth+  }++depthCube :: Offscreen -> Texture CubeMap+depthCube Offscreen{..} = Texture+  { tFormat         = Image.aiFormat oDepth+  , tMipLevels      = oMipLevels+  , tLayers         = oLayers -- TODO: get from type param+  , tAllocatedImage = oDepth+  }++allocate+  :: ( Resource.MonadResource m+     , MonadVulkan env m+     , HasLogFunc env+     )+  => Settings+  -> m Offscreen+allocate settings@Settings{..} = do+  logDebug $ "Allocating Offscreen resources for " <> display sLabel++  (_rpKey, renderPass) <- allocateRenderPass settings++  (refcounted, color, depth, framebuffer) <- allocateFramebuffer settings renderPass++  pure Offscreen+    { oRenderPass  = renderPass+    , oRenderArea  = fullSurface+    , oExtent      = sExtent+    , oMipLevels   = 1+    , oLayers      = sLayers+    , oClear       = clear+    , oColor       = color+    , oDepth       = depth+    , oFrameBuffer = framebuffer+    , oRelease     = refcounted+    }+  where+    fullSurface = Vk.Rect2D+      { Vk.offset = zero+      , Vk.extent = sExtent+      }++    clear = Vector.fromList+      [ Vk.Color $ Vk.Float32 0 0 0 1+      , Vk.DepthStencil (Vk.ClearDepthStencilValue 1.0 0)+      ]++-- ** Render pass++allocateRenderPass+  :: ( MonadVulkan env m+     , Resource.MonadResource m+     )+  => Settings+  -> m (Resource.ReleaseKey, Vk.RenderPass)+allocateRenderPass settings = do+  device <- asks getDevice++  res <-+    if sMultiView settings then+      Vk.withRenderPass device createInfoMulti Nothing Resource.allocate+    else+      Vk.withRenderPass device createInfo Nothing Resource.allocate+  Debug.nameObject device (snd res) $ "Offscreen:" <> encodeUtf8 (sLabel settings)+  pure res+  where+    createInfo =+      zero+        { Vk.attachments  = Vector.fromList [color, depth]+        , Vk.subpasses    = Vector.fromList [subpass]+        , Vk.dependencies = deps+        }++    createInfoMulti =+      createInfo+        ::& Khr.RenderPassMultiviewCreateInfo+          { Khr.viewMasks        = [2 ^ sLayers settings - 1]+          , Khr.viewOffsets      = []+          , Khr.correlationMasks = [0]+          }+        :& ()++    color = zero+      { Vk.format         = sFormat settings+      , Vk.samples        = sSamples settings+      , Vk.initialLayout  = Vk.IMAGE_LAYOUT_UNDEFINED+      , Vk.finalLayout    = Vk.IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL+      , Vk.loadOp         = Vk.ATTACHMENT_LOAD_OP_CLEAR+      , Vk.storeOp        = Vk.ATTACHMENT_STORE_OP_STORE+      , Vk.stencilLoadOp  = Vk.ATTACHMENT_LOAD_OP_DONT_CARE+      , Vk.stencilStoreOp = Vk.ATTACHMENT_STORE_OP_DONT_CARE+      }++    depth = zero+      { Vk.format         = sDepthFormat settings+      , Vk.samples        = sSamples settings+      , Vk.initialLayout  = Vk.IMAGE_LAYOUT_UNDEFINED+      , Vk.finalLayout    = Vk.IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL+      , Vk.loadOp         = Vk.ATTACHMENT_LOAD_OP_CLEAR+      , Vk.storeOp        = Vk.ATTACHMENT_STORE_OP_DONT_CARE+      , Vk.stencilLoadOp  = Vk.ATTACHMENT_LOAD_OP_DONT_CARE+      , Vk.stencilStoreOp = Vk.ATTACHMENT_STORE_OP_DONT_CARE+      }++    subpass = zero+      { Vk.pipelineBindPoint = Vk.PIPELINE_BIND_POINT_GRAPHICS+      , Vk.colorAttachments = Vector.singleton zero+          { Vk.attachment = 0+          , Vk.layout     = Vk.IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL+          }+      , Vk.depthStencilAttachment = Just zero+          { Vk.attachment = 1+          , Vk.layout     = Vk.IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL+          }+      }++    {- BUG: Validation Error: [ SYNC-HAZARD-READ_AFTER_WRITE ]++      vkCmdDraw: Hazard READ_AFTER_WRITE for VkImageView 0x2c0a7b0[],+        in VkCommandBuffer 0x3297680[],+        and VkPipeline 0x3203390[Global.Render.EnvCube.Pipeline],+        VkDescriptorSet 0x3248050[],+        type: VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,+        imageLayout: VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,+        binding #3, index 5.+      Access info (+        usage: SYNC_FRAGMENT_SHADER_SHADER_STORAGE_READ,+        prior_usage: SYNC_COLOR_ATTACHMENT_OUTPUT_COLOR_ATTACHMENT_WRITE,+        write_barriers: 0,+        command: vkCmdBeginRenderPass,+        seq_no: 26,+        reset_no: 1+      ).+    -}+    deps =+      [ zero+          { Vk.dependencyFlags = Vk.DEPENDENCY_BY_REGION_BIT+          , Vk.srcSubpass      = Vk.SUBPASS_EXTERNAL+          , Vk.dstSubpass      = 0+          , Vk.srcStageMask    = Vk.PIPELINE_STAGE_FRAGMENT_SHADER_BIT+          , Vk.dstStageMask    = Vk.PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT .|. Vk.PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT .|. Vk.PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT+          , Vk.srcAccessMask   = Vk.ACCESS_SHADER_READ_BIT .|. Vk.ACCESS_SHADER_WRITE_BIT+          , Vk.dstAccessMask   = Vk.ACCESS_COLOR_ATTACHMENT_WRITE_BIT .|. Vk.ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT+          }+      , zero+          { Vk.dependencyFlags = Vk.DEPENDENCY_BY_REGION_BIT+          , Vk.srcSubpass      = 0+          , Vk.dstSubpass      = Vk.SUBPASS_EXTERNAL+          , Vk.srcStageMask    = Vk.PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT .|. Vk.PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT .|. Vk.PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT+          , Vk.dstStageMask    = Vk.PIPELINE_STAGE_FRAGMENT_SHADER_BIT+          , Vk.srcAccessMask   = Vk.ACCESS_COLOR_ATTACHMENT_WRITE_BIT .|. Vk.ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT+          , Vk.dstAccessMask   = Vk.ACCESS_SHADER_READ_BIT+          }+      ]++-- ** Framebuffer++type FramebufferOffscreen =+  ( RefCounted+  , Image.AllocatedImage+  , Image.AllocatedImage+  , Vk.Framebuffer+  )++allocateFramebuffer+  :: ( Resource.MonadResource m+     , MonadVulkan env m+     , HasLogFunc env+     )+  => Settings+  -> Vk.RenderPass+  -> m FramebufferOffscreen+allocateFramebuffer Settings{..} renderPass = do+  context <- ask+  let device = getDevice context+  let+    Vk.Extent2D{width, height} = sExtent+    mipLevels = extentMips sExtent++  (colorKey, color) <- Resource.allocate+    ( Image.create+        context+        (Just $ sLabel <> ".color")+        Vk.IMAGE_ASPECT_COLOR_BIT+        sExtent+        (if sMipMap then mipLevels else 1)+        sLayers+        sSamples+        sFormat+        (Vk.IMAGE_USAGE_COLOR_ATTACHMENT_BIT .|. Vk.IMAGE_USAGE_SAMPLED_BIT)+    )+    (Image.destroy context)++  (depthKey, depth) <- Resource.allocate+    ( Image.create+        context+        (Just $ sLabel <> ".depth")+        Vk.IMAGE_ASPECT_DEPTH_BIT+        sExtent+        (if sMipMap then mipLevels else 1)+        sLayers+        sSamples+        sDepthFormat+        (Vk.IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT .|. Vk.IMAGE_USAGE_SAMPLED_BIT)+    )+    (Image.destroy context)++  let+    attachments = Vector.fromList+      [ Image.aiImageView color+      , Image.aiImageView depth+      ]++    {- XXX:+      If the render pass uses multiview, then layers must be one and each attachment+      requires a number of layers that is greater than the maximum bit index set in+      the view mask in the subpasses in which it is used.+    -}+    fbNumLayers =+      if sMultiView then+        1+      else+        sLayers++    fbCI = zero+      { Vk.renderPass  = renderPass+      , Vk.width       = width+      , Vk.height      = height+      , Vk.attachments = attachments+      , Vk.layers      = fbNumLayers+      }++  (framebufferKey, framebuffer) <- Vk.withFramebuffer device fbCI Nothing Resource.allocate+  Debug.nameObject device framebuffer $ encodeUtf8 sLabel <> ".FB"++  releaseDebug <- toIO $ logDebug "Releasing Offscreen resources"+  release <- newRefCounted do+    releaseDebug+    Resource.release colorKey+    Resource.release depthKey+    Resource.release framebufferKey++  pure (release, color, depth, framebuffer)++extentMips :: Num t => Vk.Extent2D -> t+extentMips Vk.Extent2D{width, height} = go 1 (min width height)+  where+    go levels = \case+      0 ->+        levels+      1 ->+        levels+      side ->+        go (levels + 1) (side `div` 2)
src/Render/Samplers.hs view
@@ -25,14 +25,14 @@ import Resource.Collection qualified as Collection  data Collection a = Collection-  { linearMipRepeat  :: a-  , linearMip        :: a-  , linearRepeat     :: a-  , linear           :: a-  , nearestMipRepeat :: a-  , nearestMip       :: a-  , nearestRepeat    :: a-  , nearest          :: a+  { linearMipRepeat  :: a -- 0+  , linearMip        :: a -- 1+  , linearRepeat     :: a -- 2+  , linear           :: a -- 3+  , nearestMipRepeat :: a -- 4+  , nearestMip       :: a -- 5+  , nearestRepeat    :: a -- 6+  , nearest          :: a -- 7   }   deriving stock (Show, Functor, Foldable, Traversable, Generic, Generic1)   deriving Applicative via (Co Collection)
src/Resource/Image.hs view
@@ -1,4 +1,11 @@-module Resource.Image where+module Resource.Image+  ( AllocatedImage(..)+  , create+  , destroy+  , subresource+  , transitionLayout+  , copyBufferToImage+  ) where  import RIO @@ -11,137 +18,65 @@ import Vulkan.Zero (zero) import VulkanMemoryAllocator qualified as VMA -import Engine.Vulkan.Types (HasVulkan(..), HasSwapchain(..), Queues(..))+import Engine.Vulkan.Types (HasVulkan(..), Queues(..)) import Resource.CommandBuffer (oneshot_)  data AllocatedImage = AllocatedImage-  { aiAllocation :: VMA.Allocation-  , aiImage      :: Vk.Image-  , aiImageView  :: Vk.ImageView+  { aiAllocation  :: VMA.Allocation+  , aiFormat      :: Vk.Format+  , aiImage       :: Vk.Image+  , aiImageView   :: Vk.ImageView+  , aiImageRange  :: Vk.ImageSubresourceRange   }   deriving (Show) -createColorResource+create   :: ( MonadIO io      , HasVulkan ctx-     , HasSwapchain ctx      )   => ctx+  -> Maybe Text+  -> Vk.ImageAspectFlags   -> Vk.Extent2D+  -> "mip levels" ::: Word32+  -> "stored layers" ::: Word32+  -> Vk.SampleCountFlagBits+  -> Vk.Format+  -> Vk.ImageUsageFlags   -> io AllocatedImage-createColorResource context Vk.Extent2D{width, height} = do+create context mlabel aspect Vk.Extent2D{width, height} mipLevels numLayers samples format usage = do   let     device    = getDevice context     allocator = getAllocator context-    format    = getSurfaceFormat context-    msaa      = getMultisample context    (image, allocation, _info) <- VMA.createImage     allocator-    (imageCI format msaa)-    imageAllocationCI-  Debug.nameObject device image "ColorResource.image"--  imageView <- Vk.createImageView-    device-    (imageViewCI image format)-    Nothing-  Debug.nameObject device image "ColorResource.view"--  pure AllocatedImage-    { aiAllocation = allocation-    , aiImage      = image-    , aiImageView  = imageView-    }-  where-    imageCI format msaa = zero-      { Vk.imageType     = Vk.IMAGE_TYPE_2D-      , Vk.format        = format-      , Vk.extent        = Vk.Extent3D width height 1-      , Vk.mipLevels     = 1-      , Vk.arrayLayers   = 1-      , Vk.tiling        = Vk.IMAGE_TILING_OPTIMAL-      , Vk.initialLayout = Vk.IMAGE_LAYOUT_UNDEFINED-      , Vk.usage         = Vk.IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT .|. Vk.IMAGE_USAGE_COLOR_ATTACHMENT_BIT-      , Vk.sharingMode   = Vk.SHARING_MODE_EXCLUSIVE-      , Vk.samples       = msaa-      }--    imageAllocationCI = zero-      { VMA.usage         = VMA.MEMORY_USAGE_GPU_ONLY-      , VMA.requiredFlags = Vk.MEMORY_PROPERTY_DEVICE_LOCAL_BIT-      }--    imageViewCI image format = zero-      { Vk.image            = image-      , Vk.viewType         = Vk.IMAGE_VIEW_TYPE_2D-      , Vk.format           = format-      , Vk.components       = zero-      , Vk.subresourceRange = subr-      }--    subr = zero-      { Vk.aspectMask     = Vk.IMAGE_ASPECT_COLOR_BIT-      , Vk.baseMipLevel   = 0-      , Vk.levelCount     = 1-      , Vk.baseArrayLayer = 0-      , Vk.layerCount     = 1-      }--createDepthResource-  :: ( MonadIO io-     , HasVulkan context-     , HasSwapchain context-     )-  => context-  -> Vk.Extent2D-  -> "shadowmap layers" ::: Maybe Word32-  -> io AllocatedImage-createDepthResource context Vk.Extent2D{width, height} depthLayers = do-  let-    device    = getDevice context-    allocator = getAllocator context--    depthFormat = getDepthFormat context-    msaa        = getMultisample context--    (samples, usage, numLayers) =-      case depthLayers of-        Nothing ->-          ( msaa-          , Vk.IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT-          , 1-          )-        Just nl ->-          ( Vk.SAMPLE_COUNT_1_BIT-          , Vk.IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT .|.-            Vk.IMAGE_USAGE_SAMPLED_BIT-          , nl-          )--  (image, allocation, _info) <- VMA.createImage-    allocator-    (imageCI depthFormat usage samples numLayers)+    imageCI     imageAllocationCI-  Debug.nameObject device image "DepthResource.image"+  for_ mlabel \label ->+    Debug.nameObject device image $ encodeUtf8 label <> ".label"    imageView <- Vk.createImageView     device-    (imageViewCI depthFormat image numLayers)+    (imageViewCI image)     Nothing-  Debug.nameObject device image "DepthResource.view"+  for_ mlabel \label ->+    Debug.nameObject device image $ encodeUtf8 label <> ".view"    pure AllocatedImage     { aiAllocation = allocation+    , aiFormat     = format     , aiImage      = image     , aiImageView  = imageView+    , aiImageRange = subr     }   where-    imageCI format usage samples numLayers = zero+    imageCI = zero       { Vk.imageType     = Vk.IMAGE_TYPE_2D+      , Vk.flags         = createFlags       , Vk.format        = format       , Vk.extent        = Vk.Extent3D width height 1-      , Vk.mipLevels     = 1+      , Vk.mipLevels     = mipLevels       , Vk.arrayLayers   = numLayers       , Vk.tiling        = Vk.IMAGE_TILING_OPTIMAL       , Vk.initialLayout = Vk.IMAGE_LAYOUT_UNDEFINED@@ -155,54 +90,53 @@       , VMA.requiredFlags = Vk.MEMORY_PROPERTY_DEVICE_LOCAL_BIT       } -    imageViewCI format image numLayers = zero+    imageViewCI image = zero       { Vk.image            = image       , Vk.viewType         = viewType       , Vk.format           = format       , Vk.components       = zero-      , Vk.subresourceRange = subr numLayers+      , Vk.subresourceRange = subr       }-      where-        viewType =-          if numLayers > 1 then-            Vk.IMAGE_VIEW_TYPE_2D_ARRAY-          else-            Vk.IMAGE_VIEW_TYPE_2D -    subr numLayers = zero-      { Vk.aspectMask     = Vk.IMAGE_ASPECT_DEPTH_BIT-      , Vk.baseMipLevel   = 0-      , Vk.levelCount     = 1-      , Vk.baseArrayLayer = 0-      , Vk.layerCount     = numLayers-      }+    (createFlags, viewType) =+      case numLayers of+        1 ->+          (zero, Vk.IMAGE_VIEW_TYPE_2D)+        6 ->+          (Vk.IMAGE_CREATE_CUBE_COMPATIBLE_BIT, Vk.IMAGE_VIEW_TYPE_CUBE)+        _ ->+          (zero, Vk.IMAGE_VIEW_TYPE_2D_ARRAY) -destroyAllocatedImage+    subr = subresource aspect mipLevels numLayers++destroy   :: ( MonadIO io      , HasVulkan context      )   => context   -> AllocatedImage   -> io ()-destroyAllocatedImage context AllocatedImage{..} = do+destroy context AllocatedImage{..} = do   -- traceM "destroyAllocatedImage"   Vk.destroyImageView (getDevice context) aiImageView Nothing   VMA.destroyImage (getAllocator context) aiImage aiAllocation  -------------------------------------------- -transitionImageLayout-  :: (HasVulkan context)+transitionLayout+  :: ( HasVulkan context+     , MonadUnliftIO m+     )   => context   -> Queues Vk.CommandPool   -> Vk.Image   -> "mip levels" ::: Word32   -> "layer count" ::: Word32   -> Vk.Format-  -> ("old" ::: Vk.ImageLayout)-  -> ("new" ::: Vk.ImageLayout)-  -> RIO env ()-transitionImageLayout ctx pool image mipLevels layerCount format old new =+  -> "old" ::: Vk.ImageLayout+  -> "new" ::: Vk.ImageLayout+  -> m ()+transitionLayout ctx pool image mipLevels layerCount format old new =   case (old, new) of     (Vk.IMAGE_LAYOUT_UNDEFINED, Vk.IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) ->       oneshot_ ctx pool qTransfer \buf ->@@ -269,6 +203,27 @@       , Vk.subresourceRange    = subresource aspectMask mipLevels layerCount       } +-- imageBarrier cb oldLayout newLayout aspectMask srcStage srcMask dstStage dstMask image mipLevels layerCount =+--   Vk.cmdPipelineBarrier+--     cb+--     srcStage+--     dstStage+--     zero+--     mempty+--     mempty+--     (Vector.singleton barrier)+--   where+--     barrier = SomeStruct zero+--       { Vk.srcAccessMask       = srcMask+--       , Vk.dstAccessMask       = dstMask+--       , Vk.oldLayout           = oldLayout+--       , Vk.newLayout           = newLayout+--       , Vk.srcQueueFamilyIndex = Vk.QUEUE_FAMILY_IGNORED+--       , Vk.dstQueueFamilyIndex = Vk.QUEUE_FAMILY_IGNORED+--       , Vk.image               = image+--       , Vk.subresourceRange    = subresource aspectMask mipLevels layerCount+--       }+ subresource   :: Vk.ImageAspectFlags   -> "mip levels" ::: Word32@@ -283,7 +238,11 @@   }  copyBufferToImage-  :: (HasVulkan context, Foldable t, Integral deviceSize)+  :: ( HasVulkan context+     , Foldable t+     , Integral deviceSize+     , MonadUnliftIO m+     )   => context   -> Queues Vk.CommandPool   -> Vk.Buffer@@ -291,7 +250,7 @@   -> "base extent" ::: Vk.Extent3D   -> "mip offsets" ::: t deviceSize   -> "layer count" ::: Word32-  -> RIO env ()+  -> m () copyBufferToImage ctx pool src dst Vk.Extent3D{..} mipOffsets layerCount =   oneshot_ ctx pool qTransfer \cmd ->     Vk.cmdCopyBufferToImage cmd src dst Vk.IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL $
src/Resource/Mesh/Codec.hs view
@@ -3,6 +3,7 @@ import RIO  import Codec.Compression.Zstd qualified as Zstd+import Codec.Serialise qualified as CBOR import Crypto.Hash.MD5 qualified as MD5 import Data.Binary.Get (runGet) import Data.Binary.Get qualified as Get@@ -10,6 +11,7 @@ import Data.Binary.Put qualified as Put import Data.ByteString.Internal qualified as BSI import Data.ByteString.Unsafe (unsafePackCStringLen)+import Data.Typeable (typeRep, typeRepTyCon) import Data.Vector qualified as Vector import Data.Vector.Generic qualified as Generic import Data.Vector.Storable qualified as Storable@@ -21,16 +23,15 @@ import UnliftIO.Resource (MonadResource) import UnliftIO.Resource qualified as Resource import Vulkan.Core10 qualified as Vk-import Data.Typeable -import Engine.Vulkan.Types (HasVulkan, Queues)+import Engine.Vulkan.Types (MonadVulkan, Queues) import Resource.Buffer qualified as Buffer import Resource.Model qualified as Model  -- * Format meta  pattern VER_BREAKS :: Word8-pattern VER_BREAKS = 2+pattern VER_BREAKS = 3  pattern VER_TWEAKS :: Word8 pattern VER_TWEAKS = 0@@ -45,7 +46,7 @@      , Generic.Vector vn nodes      , Storable attrs      , Storable nodes-     , Storable meta+     , CBOR.Serialise meta      , HasLogFunc env      )   => FilePath@@ -68,7 +69,7 @@   (nodDigest, nodCompressed) <- encodeItems nodes   logDebug $ "Node digest: " <> displayShow nodDigest -  (metDigest, metCompressed) <- encodeItems $ Storable.singleton meta+  let (metSize, metDigest, metCompressed) = encodeCBOR meta   logDebug $ "Meta digest: " <> displayShow metDigest    withFile fp WriteMode \out -> do@@ -102,7 +103,7 @@       Put.putWord32le . fromIntegral $ ByteString.length nodCompressed        -- 0x30 + 4 + 4-      Put.putWord32le . fromIntegral $ Foreign.sizeOf (error "sizeOf" :: meta)+      Put.putWord32le $ fromIntegral metSize       Put.putWord32le . fromIntegral $ ByteString.length metCompressed        -- 0x38 + 4 + 4@@ -155,21 +156,30 @@       !zBuf = Zstd.compress Zstd.maxCLevel buf     pure (bufHash, zBuf) +encodeCBOR :: CBOR.Serialise a => a -> (Int, ByteString, ByteString)+encodeCBOR stuff =+  ( ByteString.length buf+  , MD5.hash buf+  , Zstd.compress Zstd.maxCLevel buf+  )+  where+    buf = BSL.toStrict (CBOR.serialise stuff)+ -- * Decoding  loadIndexed   :: ( Storable attrs      , Storable nodes-     , Storable meta+     , CBOR.Serialise meta      , Show meta      , Typeable nodes-     , HasVulkan env      , HasLogFunc env-     , MonadResource (RIO env)+     , MonadResource m+     , MonadVulkan env m      )   => Queues Vk.CommandPool   -> FilePath-  -> RIO env+  -> m     ( Resource.ReleaseKey     , (meta, Storable.Vector nodes, Model.Indexed 'Buffer.Staged Vec3.Packed attrs)     )@@ -185,15 +195,17 @@   pure (key, (meta, nodes, indexed))  loadBlobs-  :: forall attrs env nodes meta+  :: forall attrs env nodes meta m   .  ( Storable attrs-     , Storable meta+     , CBOR.Serialise meta      , Storable nodes      , Typeable nodes      , HasLogFunc env+     , MonadReader env m+     , MonadIO m      )   => FilePath-  -> RIO env+  -> m     ( meta     , Storable.Vector nodes     , ( Storable.Vector Vec3.Packed@@ -233,7 +245,7 @@        sizeOfMeta <- fmap fromIntegral Get.getWord32le       lenMeta    <- fmap fromIntegral Get.getWord32le-      guardEq "Meta size" (Foreign.sizeOf (error "sizeOf" :: meta)) sizeOfMeta+      -- guardEq "Meta size" (Foreign.sizeOf (error "sizeOf" :: meta)) sizeOfMeta        _reserved32 <- Get.getWord32le       _reserved32 <- Get.getWord32le@@ -267,10 +279,11 @@       indices   <- decodeItems "Indices"    indDigest (Just numIndices)   zIndices       attrs     <- decodeItems "Attributes" attDigest (Just numPositions) zAttrs       nodes     <- decodeItems "Nodes"      nodDigest Nothing             zNodes-      meta      <- decodeItems "Metadata"   metDigest (Just 1)            zMetas -      pure (verTweaks, Storable.head meta, nodes, (positions, indices, attrs))+      meta      <- decodeCBOR  "Metadata"   metDigest sizeOfMeta          zMetas +      pure (verTweaks, meta, nodes, (positions, indices, attrs))+   let (verTweaks, meta, nodes, blobs) = runGet getter blob    when (verTweaks /= VER_TWEAKS) $@@ -324,6 +337,33 @@       pure items   where     itemSize = Foreign.sizeOf @item undefined++decodeCBOR+  :: ( CBOR.Serialise a+     , MonadFail m+     )+  => String+  -> ByteString+  -> Int+  -> ByteString+  -> m a+decodeCBOR label digest expectedSize zBytes =+  case Zstd.decompress zBytes of+    Zstd.Error err ->+      fail $ label <> ": zstd error (" <> err <> ")"+    Zstd.Skip ->+      fail $ label <> ": empty zstd"+    Zstd.Decompress bytes -> do+      guardEq (label <> " size") expectedSize (ByteString.length bytes)+      guardEq (label <> " hash") digest (MD5.hash bytes)+      case CBOR.deserialiseOrFail (BSL.fromStrict bytes) of+        Right value ->+          pure value+        Left (CBOR.DeserialiseFailure _off err) ->+          fail $ unlines+            [ label <> " deserialise failure:"+            , err+            ]  -- * Utils 
src/Resource/Mesh/Types.hs view
@@ -23,17 +23,27 @@   , sizeAa    , HasRange(..)++  , encodeStorable+  , decodeStorable   ) where  import RIO +import Codec.Serialise qualified as CBOR+import Codec.Serialise.Decoding qualified as CBOR (Decoder, decodeBytes)+import Codec.Serialise.Encoding qualified as CBOR (Encoding) import Control.Foldl qualified as L+import Data.ByteString.Unsafe qualified as BS+import Data.Typeable (typeRep, typeRepTyCon) import Foreign (Storable(..), castPtr)+import Foreign qualified import Foreign.Storable.Generic (GStorable) import Geomancy (Transform(..), Vec2, Vec4, withVec3) import Geomancy.Mat4 qualified as Mat4 import Geomancy.Vec3 qualified as Vec3 import RIO.Vector.Storable qualified as Storable+import System.IO.Unsafe (unsafePerformIO) import Vulkan.Zero (Zero(..))  import Resource.Model (IndexRange(..))@@ -74,6 +84,8 @@     pokeElemOff (castPtr ptr) 1 aaY     pokeElemOff (castPtr ptr) 2 aaZ +instance (CBOR.Serialise a) => CBOR.Serialise (AxisAligned a)+ -- * Whole-scene metadata  data Meta = Meta@@ -106,6 +118,27 @@       Mat4.toListRowMajor (mTransformBB b)     ] +instance CBOR.Serialise Meta where+  encode Meta{..} = mconcat+    [ CBOR.encode    mOpaqueIndices+    , CBOR.encode    mBlendedIndices+    , CBOR.encode    mOpaqueNodes+    , CBOR.encode    mBlendedNodes+    , encodeStorable mBoundingSphere+    , encodeStorable mTransformBB+    , CBOR.encode    mMeasurements+    ]++  decode = do+    mOpaqueIndices  <- CBOR.decode+    mBlendedIndices <- CBOR.decode+    mOpaqueNodes    <- CBOR.decode+    mBlendedNodes   <- CBOR.decode+    mBoundingSphere <- decodeStorable+    mTransformBB    <- decodeStorable+    mMeasurements   <- CBOR.decode+    pure Meta{..}+ -- * Scene parts  data NodeGroup@@ -223,6 +256,8 @@     pokeByteOff ptr  8 mMean     pokeByteOff ptr 12 mStd +instance CBOR.Serialise Measurements+ {-# INLINEABLE middleAa #-} middleAa :: AxisAligned Measurements -> AxisAligned Float middleAa = fmap middle@@ -303,3 +338,45 @@   adjustRange tn@TexturedNode{tnNode} newFirstIndex = tn     { tnNode = adjustRange tnNode newFirstIndex     }++-- | CBOR.encode helper for storable types (vectors, etc.)+{-# NOINLINE encodeStorable #-}+encodeStorable :: forall a . Storable a => a -> CBOR.Encoding+encodeStorable x = unsafePerformIO do+  ptr <- Foreign.malloc @a+  poke ptr x+  buf <- BS.unsafePackMallocCStringLen+    ( Foreign.castPtr ptr+    , Foreign.sizeOf (undefined :: a)+    )+  pure $ CBOR.encode buf++-- | CBOR.decode helper for storable types (vectors, etc.)+decodeStorable :: forall a s . (Storable a, Typeable a) => CBOR.Decoder s a+decodeStorable = do+  buf <- CBOR.decodeBytes+  case fromBuf buf of+    Left err ->+      fail err+    Right !res ->+      pure res+  where+    expected =+      Foreign.sizeOf (undefined :: a)++    {-# NOINLINE fromBuf #-}+    fromBuf :: ByteString -> Either String a+    fromBuf buf =+      unsafePerformIO $+        BS.unsafeUseAsCStringLen buf \(ptr, len) ->+          if len == expected then do+            !res <- Foreign.peek (Foreign.castPtr ptr)+            pure $ Right res+          else+            pure . Left $ mconcat+              [ "Storable size mismatch for "+              , show (typeRepTyCon . typeRep $ Proxy @a)+              , " (expected: ", show expected+              , ", got: ", show len+              , ")"+              ]
src/Resource/Model.hs view
@@ -2,6 +2,7 @@  import RIO +import Codec.Serialise qualified as CBOR import Data.List qualified as List import Data.Vector.Storable qualified as Storable import Foreign (Storable(..))@@ -65,7 +66,9 @@   { irFirstIndex :: Word32   , irIndexCount :: Word32   }-  deriving (Eq, Ord, Show)+  deriving (Eq, Ord, Show, Generic)++instance CBOR.Serialise IndexRange  instance Storable IndexRange where   alignment ~_ = 4
src/Resource/Texture.hs view
@@ -40,7 +40,7 @@  import Engine.Vulkan.Types (HasVulkan(getDevice), MonadVulkan, Queues) import Resource.Collection qualified as Collection-import Resource.Image (AllocatedImage(..), destroyAllocatedImage, subresource)+import Resource.Image (AllocatedImage(..)) import Resource.Image qualified as Image  data TextureError@@ -128,7 +128,7 @@  destroy :: (MonadIO io, HasVulkan context) => context -> Texture a -> io () destroy context Texture{tAllocatedImage} =-  destroyAllocatedImage context tAllocatedImage+  Image.destroy context tAllocatedImage  createImageView   :: (MonadIO io, HasVulkan context)@@ -156,7 +156,7 @@         Vk.IMAGE_VIEW_TYPE_2D      colorRange =-      subresource Vk.IMAGE_ASPECT_COLOR_BIT mipLevels arrayLayers+      Image.subresource Vk.IMAGE_ASPECT_COLOR_BIT mipLevels arrayLayers  imageCI :: Vk.Format -> Vk.Extent3D -> Word32 -> Word32 -> Vk.ImageCreateInfo '[] imageCI format extent mipLevels arrayLayers = zero
src/Resource/Texture/Ktx1.hs view
@@ -16,8 +16,7 @@ import Vulkan.Utils.FromGL qualified as FromGL import VulkanMemoryAllocator qualified as VMA -import Engine.Types (StageRIO)-import Engine.Vulkan.Types (HasVulkan(..), Queues)+import Engine.Vulkan.Types (HasVulkan(..), MonadVulkan, Queues) import Resource.Image (AllocatedImage(..)) import Resource.Image qualified as Image import Resource.Source (Source(..))@@ -26,19 +25,29 @@ import Resource.Texture qualified as Texture  load-  :: (TextureLayers a, Typeable a, HasCallStack)+  :: ( TextureLayers a+     , MonadVulkan env m+     , MonadThrow m+     , HasLogFunc env+     , Typeable a+     , HasCallStack+     )   => Queues Vk.CommandPool   -> Source-  -> StageRIO st (Texture a)+  -> m (Texture a) load pool source =   withFrozenCallStack $     Source.load (loadBytes pool) source  loadBytes-  :: TextureLayers a+  :: ( TextureLayers a+     , MonadVulkan env m+     , MonadThrow m+     , HasLogFunc env+     )   => Queues Vk.CommandPool   -> ByteString-  -> StageRIO st (Texture a)+  -> m (Texture a) loadBytes pool bytes = do   case Ktx1.fromByteString bytes of     Left (offset, err) -> do@@ -48,11 +57,15 @@       loadKtx1 pool ktx1  loadKtx1-  :: forall a st-  .  (TextureLayers a)+  :: forall a m env+  .  ( TextureLayers a+     , MonadVulkan env m+     , MonadThrow m+     , HasLogFunc env+     )   => Queues Vk.CommandPool   -> Ktx1.Ktx-  -> StageRIO st (Texture a)+  -> m (Texture a) loadKtx1 pool Ktx1.Ktx{header, images=ktxImages} = do   context <- ask   let vma = getAllocator context@@ -122,7 +135,7 @@     (Texture.imageCI format extent mipLevels numLayers)     Texture.imageAllocationCI -  Image.transitionImageLayout+  Image.transitionLayout     context     pool     image@@ -185,7 +198,7 @@    -- XXX: staging buffer is gone -  Image.transitionImageLayout+  Image.transitionLayout     context     pool     image@@ -205,8 +218,10 @@   let     allocatedImage = AllocatedImage       { aiAllocation = allocation+      , aiFormat     = format       , aiImage      = image       , aiImageView  = imageView+      , aiImageRange = Image.subresource Vk.IMAGE_ASPECT_COLOR_BIT mipLevels numLayers       }   pure Texture     { tFormat         = format