packages feed

wgpu-hs 0.2.0.1 → 0.3.0.0

raw patch · 26 files changed

+2095/−647 lines, 26 filesdep +containersdep +data-hasdep +lensdep ~data-defaultdep ~textdep ~transformersnew-component:exe:triangle-glfwnew-component:exe:triangle-sdlPVP ok

version bump matches the API change (PVP)

Dependencies added: containers, data-has, lens, mtl, resourcet, safe-exceptions, sdl2, string-qq

Dependency ranges changed: data-default, text, transformers, vector, wgpu-raw-hs

API changes (from Hackage documentation)

- WGPU: createGLFWSurface :: Instance -> Window -> IO Surface
- WGPU: logLevelToText :: LogLevel -> Text
- WGPU: logStdout :: LogLevel -> Text -> IO ()
- WGPU: type LogCallback = LogLevel -> Text -> IO ()
+ WGPU: AdapterProperties :: !Word32 -> !Word32 -> !Text -> !Text -> !AdapterType -> !BackendType -> AdapterProperties
+ WGPU: AdapterTypeCPU :: AdapterType
+ WGPU: AdapterTypeDiscreteGPU :: AdapterType
+ WGPU: AdapterTypeIntegratedGPU :: AdapterType
+ WGPU: AdapterTypeUnknown :: AdapterType
+ WGPU: BackendTypeD3D11 :: BackendType
+ WGPU: BackendTypeD3D12 :: BackendType
+ WGPU: BackendTypeMetal :: BackendType
+ WGPU: BackendTypeNull :: BackendType
+ WGPU: BackendTypeOpenGL :: BackendType
+ WGPU: BackendTypeOpenGLES :: BackendType
+ WGPU: BackendTypeVulkan :: BackendType
+ WGPU: [adapterName] :: AdapterProperties -> !Text
+ WGPU: [adapterType] :: AdapterProperties -> !AdapterType
+ WGPU: [backendType] :: AdapterProperties -> !BackendType
+ WGPU: [deviceID] :: AdapterProperties -> !Word32
+ WGPU: [driverDescription] :: AdapterProperties -> !Text
+ WGPU: [vendorID] :: AdapterProperties -> !Word32
+ WGPU: adapterPropertiesToText :: AdapterProperties -> Text
+ WGPU: connectLog :: MonadIO m => Instance -> m ()
+ WGPU: data AdapterProperties
+ WGPU: data AdapterType
+ WGPU: data BackendType
+ WGPU: disconnectLog :: MonadIO m => Instance -> m ()
+ WGPU: fromSMaybe :: a -> SMaybe a -> a
+ WGPU: getAdapterProperties :: MonadIO m => Adapter -> m AdapterProperties
+ WGPU.BoneYard.SimpleSDL: AdapterRequestFailed :: AppException
+ WGPU.BoneYard.SimpleSDL: DeviceRequestFailed :: AppException
+ WGPU.BoneYard.SimpleSDL: Params :: !Text -> !SMaybe DeviceDescriptor -> Params
+ WGPU.BoneYard.SimpleSDL: Resources :: !Instance -> !Window -> !Surface -> !Adapter -> !Device -> !Queue -> Resources
+ WGPU.BoneYard.SimpleSDL: UnknownRenderPipelineName :: RenderPipelineName -> AppException
+ WGPU.BoneYard.SimpleSDL: UnknownShaderName :: ShaderName -> AppException
+ WGPU.BoneYard.SimpleSDL: [adapter] :: Resources -> !Adapter
+ WGPU.BoneYard.SimpleSDL: [device] :: Resources -> !Device
+ WGPU.BoneYard.SimpleSDL: [inst] :: Resources -> !Instance
+ WGPU.BoneYard.SimpleSDL: [mDeviceDescriptor] :: Params -> !SMaybe DeviceDescriptor
+ WGPU.BoneYard.SimpleSDL: [queue] :: Resources -> !Queue
+ WGPU.BoneYard.SimpleSDL: [surface] :: Resources -> !Surface
+ WGPU.BoneYard.SimpleSDL: [title] :: Params -> !Text
+ WGPU.BoneYard.SimpleSDL: [window] :: Resources -> !Window
+ WGPU.BoneYard.SimpleSDL: compileWGSL :: (Has Device r, Has Shaders r, MonadReader r m, MonadResource m) => ShaderName -> WGSL -> m ShaderModule
+ WGPU.BoneYard.SimpleSDL: compileWGSL_ :: (Has Device r, Has Shaders r, MonadReader r m, MonadResource m) => ShaderName -> WGSL -> m ()
+ WGPU.BoneYard.SimpleSDL: createRenderPipeline :: (MonadIO m, HasDevice r m, Has RenderPipelines r) => RenderPipelineName -> RenderPipelineDescriptor -> m RenderPipeline
+ WGPU.BoneYard.SimpleSDL: data AppException
+ WGPU.BoneYard.SimpleSDL: data Params
+ WGPU.BoneYard.SimpleSDL: data RenderPipelineName
+ WGPU.BoneYard.SimpleSDL: data RenderPipelines
+ WGPU.BoneYard.SimpleSDL: data Resources
+ WGPU.BoneYard.SimpleSDL: data ShaderName
+ WGPU.BoneYard.SimpleSDL: data Shaders
+ WGPU.BoneYard.SimpleSDL: data SwapChainState
+ WGPU.BoneYard.SimpleSDL: emptyRenderPipelines :: MonadResource m => m RenderPipelines
+ WGPU.BoneYard.SimpleSDL: emptyShaders :: MonadResource m => m Shaders
+ WGPU.BoneYard.SimpleSDL: emptySwapChainState :: MonadResource m => m SwapChainState
+ WGPU.BoneYard.SimpleSDL: getRenderPipeline :: (Has RenderPipelines r, MonadReader r m, MonadIO m, MonadThrow m) => RenderPipelineName -> m RenderPipeline
+ WGPU.BoneYard.SimpleSDL: getShader :: (Has Shaders r, MonadReader r m, MonadIO m, MonadThrow m) => ShaderName -> m ShaderModule
+ WGPU.BoneYard.SimpleSDL: instance Data.Has.Has SDL.Internal.Types.Window WGPU.BoneYard.SimpleSDL.Resources
+ WGPU.BoneYard.SimpleSDL: instance Data.Has.Has WGPU.Internal.Adapter.Adapter WGPU.BoneYard.SimpleSDL.Resources
+ WGPU.BoneYard.SimpleSDL: instance Data.Has.Has WGPU.Internal.Device.Device WGPU.BoneYard.SimpleSDL.Resources
+ WGPU.BoneYard.SimpleSDL: instance Data.Has.Has WGPU.Internal.Instance.Instance WGPU.BoneYard.SimpleSDL.Resources
+ WGPU.BoneYard.SimpleSDL: instance Data.Has.Has WGPU.Internal.Queue.Queue WGPU.BoneYard.SimpleSDL.Resources
+ WGPU.BoneYard.SimpleSDL: instance Data.Has.Has WGPU.Internal.Surface.Surface WGPU.BoneYard.SimpleSDL.Resources
+ WGPU.BoneYard.SimpleSDL: instance Data.String.IsString WGPU.BoneYard.SimpleSDL.RenderPipelineName
+ WGPU.BoneYard.SimpleSDL: instance Data.String.IsString WGPU.BoneYard.SimpleSDL.ShaderName
+ WGPU.BoneYard.SimpleSDL: instance GHC.Classes.Eq WGPU.BoneYard.SimpleSDL.RenderPipelineName
+ WGPU.BoneYard.SimpleSDL: instance GHC.Classes.Eq WGPU.BoneYard.SimpleSDL.ShaderName
+ WGPU.BoneYard.SimpleSDL: instance GHC.Classes.Ord WGPU.BoneYard.SimpleSDL.RenderPipelineName
+ WGPU.BoneYard.SimpleSDL: instance GHC.Classes.Ord WGPU.BoneYard.SimpleSDL.ShaderName
+ WGPU.BoneYard.SimpleSDL: instance GHC.Exception.Type.Exception WGPU.BoneYard.SimpleSDL.AppException
+ WGPU.BoneYard.SimpleSDL: instance GHC.Generics.Generic WGPU.BoneYard.SimpleSDL.Resources
+ WGPU.BoneYard.SimpleSDL: instance GHC.Show.Show WGPU.BoneYard.SimpleSDL.AppException
+ WGPU.BoneYard.SimpleSDL: instance GHC.Show.Show WGPU.BoneYard.SimpleSDL.RenderPipelineName
+ WGPU.BoneYard.SimpleSDL: instance GHC.Show.Show WGPU.BoneYard.SimpleSDL.ShaderName
+ WGPU.BoneYard.SimpleSDL: loadResources :: forall m. (MonadResource m, MonadThrow m) => Params -> m Resources
+ WGPU.BoneYard.SimpleSDL: withSwapChain :: forall r m a. (HasDevice r m, HasSurface r m, HasAdapter r m, Has Window r, Has SwapChainState r) => ReaderT (SwapChain, r) m a -> m a
+ WGPU.Classy: addEnv :: MonadReader r m => q -> ReaderT (q, r) m a -> m a
+ WGPU.Classy: beginRenderPass :: HasCommandEncoder r m => RenderPassDescriptor -> m RenderPassEncoder
+ WGPU.Classy: buildCommandBuffer :: forall r m. HasDevice r m => Text -> Text -> ReaderT (CommandEncoder, r) m () -> m CommandBuffer
+ WGPU.Classy: buildRenderPass :: forall r m. HasCommandEncoder r m => RenderPassDescriptor -> ReaderT (RenderPassEncoder, r) m () -> m ()
+ WGPU.Classy: commandEncoderFinish :: HasCommandEncoder r m => Text -> m CommandBuffer
+ WGPU.Classy: connectLog :: HasInstance r m => m ()
+ WGPU.Classy: createBindGroupLayout :: HasDevice r m => BindGroupLayoutDescriptor -> m BindGroupLayout
+ WGPU.Classy: createCommandEncoder :: HasDevice r m => Text -> m CommandEncoder
+ WGPU.Classy: createPipelineLayout :: HasDevice r m => PipelineLayoutDescriptor -> m PipelineLayout
+ WGPU.Classy: createRenderPipeline :: HasDevice r m => RenderPipelineDescriptor -> m RenderPipeline
+ WGPU.Classy: createShaderModule :: HasDevice r m => ShaderModuleDescriptor -> m ShaderModule
+ WGPU.Classy: createShaderModuleSPIRV :: HasDevice r m => Text -> SPIRV -> m ShaderModule
+ WGPU.Classy: createShaderModuleWGSL :: HasDevice r m => Text -> WGSL -> m ShaderModule
+ WGPU.Classy: createSwapChain :: (HasDevice r m, HasSurface r m) => SwapChainDescriptor -> m SwapChain
+ WGPU.Classy: disconnectLog :: HasInstance r m => m ()
+ WGPU.Classy: endRenderPass :: HasRenderPassEncoder r m => m ()
+ WGPU.Classy: getAdapterProperties :: HasAdapter r m => m AdapterProperties
+ WGPU.Classy: getQueue :: HasDevice r m => m Queue
+ WGPU.Classy: getSwapChainCurrentTextureView :: HasSwapChain r m => m TextureView
+ WGPU.Classy: getSwapChainPreferredFormat :: (HasSurface r m, HasAdapter r m) => m TextureFormat
+ WGPU.Classy: getVersion :: HasInstance r m => m Version
+ WGPU.Classy: queueSubmit :: HasQueue r m => Vector CommandBuffer -> m ()
+ WGPU.Classy: queueSubmit' :: HasDevice r m => Vector CommandBuffer -> m ()
+ WGPU.Classy: renderPassDraw :: HasRenderPassEncoder r m => Range Word32 -> Range Word32 -> m ()
+ WGPU.Classy: renderPassSetPipeline :: HasRenderPassEncoder r m => RenderPipeline -> m ()
+ WGPU.Classy: requestAdapter :: HasSurface r m => m (Maybe Adapter)
+ WGPU.Classy: requestDevice :: HasAdapter r m => DeviceDescriptor -> m (Maybe Device)
+ WGPU.Classy: setLogLevel :: HasInstance r m => LogLevel -> m ()
+ WGPU.Classy: swapChainPresent :: HasSwapChain r m => m ()
+ WGPU.Classy: type HasAdapter r m = (RIO r m, Has Adapter r)
+ WGPU.Classy: type HasCommandEncoder r m = (RIO r m, Has CommandEncoder r)
+ WGPU.Classy: type HasDevice r m = (RIO r m, Has Device r)
+ WGPU.Classy: type HasInstance r m = (RIO r m, Has Instance r)
+ WGPU.Classy: type HasQueue r m = (RIO r m, Has Queue r)
+ WGPU.Classy: type HasRenderPassEncoder r m = (RIO r m, Has RenderPassEncoder r)
+ WGPU.Classy: type HasSurface r m = (RIO r m, Has Surface r)
+ WGPU.Classy: type HasSwapChain r m = (RIO r m, Has SwapChain r)
+ WGPU.GLFW.Surface: createSurface :: MonadIO m => Instance -> Window -> m Surface
+ WGPU.SDL.Surface: createSurface :: MonadIO m => Instance -> Window -> m Surface
- WGPU: BindGroupLayoutDescriptor :: !Text -> Vector BindGroupLayoutEntry -> BindGroupLayoutDescriptor
+ WGPU: BindGroupLayoutDescriptor :: !Text -> !Vector BindGroupLayoutEntry -> BindGroupLayoutDescriptor
- WGPU: Color :: Double -> Double -> Double -> Double -> Color
+ WGPU: Color :: {-# UNPACK #-} !Double -> {-# UNPACK #-} !Double -> {-# UNPACK #-} !Double -> {-# UNPACK #-} !Double -> Color
- WGPU: Operations :: !LoadOp a -> StoreOp -> Operations a
+ WGPU: Operations :: !LoadOp a -> !StoreOp -> Operations a
- WGPU: VertexAttribute :: VertexFormat -> Word64 -> Word32 -> VertexAttribute
+ WGPU: VertexAttribute :: !VertexFormat -> !Word64 -> !Word32 -> VertexAttribute
- WGPU: [alpha] :: Color -> Double
+ WGPU: [alpha] :: Color -> {-# UNPACK #-} !Double
- WGPU: [blue] :: Color -> Double
+ WGPU: [blue] :: Color -> {-# UNPACK #-} !Double
- WGPU: [entries] :: BindGroupLayoutDescriptor -> Vector BindGroupLayoutEntry
+ WGPU: [entries] :: BindGroupLayoutDescriptor -> !Vector BindGroupLayoutEntry
- WGPU: [green] :: Color -> Double
+ WGPU: [green] :: Color -> {-# UNPACK #-} !Double
- WGPU: [offset] :: VertexAttribute -> Word64
+ WGPU: [offset] :: VertexAttribute -> !Word64
- WGPU: [red] :: Color -> Double
+ WGPU: [red] :: Color -> {-# UNPACK #-} !Double
- WGPU: [shaderLocation] :: VertexAttribute -> Word32
+ WGPU: [shaderLocation] :: VertexAttribute -> !Word32
- WGPU: [store] :: Operations a -> StoreOp
+ WGPU: [store] :: Operations a -> !StoreOp
- WGPU: [vertexFormat] :: VertexAttribute -> VertexFormat
+ WGPU: [vertexFormat] :: VertexAttribute -> !VertexFormat
- WGPU: beginRenderPass :: CommandEncoder -> RenderPassDescriptor -> IO RenderPassEncoder
+ WGPU: beginRenderPass :: MonadIO m => CommandEncoder -> RenderPassDescriptor -> m RenderPassEncoder
- WGPU: commandEncoderFinish :: CommandEncoder -> Text -> IO CommandBuffer
+ WGPU: commandEncoderFinish :: MonadIO m => CommandEncoder -> Text -> m CommandBuffer
- WGPU: createBindGroupLayout :: Device -> BindGroupLayoutDescriptor -> IO BindGroupLayout
+ WGPU: createBindGroupLayout :: MonadIO m => Device -> BindGroupLayoutDescriptor -> m BindGroupLayout
- WGPU: createCommandEncoder :: Device -> Text -> IO CommandEncoder
+ WGPU: createCommandEncoder :: MonadIO m => Device -> Text -> m CommandEncoder
- WGPU: createPipelineLayout :: Device -> PipelineLayoutDescriptor -> IO PipelineLayout
+ WGPU: createPipelineLayout :: MonadIO m => Device -> PipelineLayoutDescriptor -> m PipelineLayout
- WGPU: createRenderPipeline :: Device -> RenderPipelineDescriptor -> IO RenderPipeline
+ WGPU: createRenderPipeline :: MonadIO m => Device -> RenderPipelineDescriptor -> m RenderPipeline
- WGPU: createShaderModule :: Device -> ShaderModuleDescriptor -> IO ShaderModule
+ WGPU: createShaderModule :: MonadIO m => Device -> ShaderModuleDescriptor -> m ShaderModule
- WGPU: createShaderModuleSPIRV :: Device -> Text -> SPIRV -> IO ShaderModule
+ WGPU: createShaderModuleSPIRV :: MonadIO m => Device -> Text -> SPIRV -> m ShaderModule
- WGPU: createShaderModuleWGSL :: Device -> Text -> WGSL -> IO ShaderModule
+ WGPU: createShaderModuleWGSL :: MonadIO m => Device -> Text -> WGSL -> m ShaderModule
- WGPU: createSwapChain :: Device -> Surface -> SwapChainDescriptor -> IO SwapChain
+ WGPU: createSwapChain :: MonadIO m => Device -> Surface -> SwapChainDescriptor -> m SwapChain
- WGPU: endRenderPass :: RenderPassEncoder -> IO ()
+ WGPU: endRenderPass :: MonadIO m => RenderPassEncoder -> m ()
- WGPU: getQueue :: Device -> IO Queue
+ WGPU: getQueue :: MonadIO m => Device -> m Queue
- WGPU: getSwapChainCurrentTextureView :: SwapChain -> IO TextureView
+ WGPU: getSwapChainCurrentTextureView :: MonadIO m => SwapChain -> m TextureView
- WGPU: getSwapChainPreferredFormat :: Surface -> Adapter -> IO TextureFormat
+ WGPU: getSwapChainPreferredFormat :: MonadIO m => Surface -> Adapter -> m TextureFormat
- WGPU: getVersion :: Instance -> IO Version
+ WGPU: getVersion :: MonadIO m => Instance -> m Version
- WGPU: queueSubmit :: Queue -> Vector CommandBuffer -> IO ()
+ WGPU: queueSubmit :: MonadIO m => Queue -> Vector CommandBuffer -> m ()
- WGPU: renderPassDraw :: RenderPassEncoder -> Range Word32 -> Range Word32 -> IO ()
+ WGPU: renderPassDraw :: MonadIO m => RenderPassEncoder -> Range Word32 -> Range Word32 -> m ()
- WGPU: renderPassSetPipeline :: RenderPassEncoder -> RenderPipeline -> IO ()
+ WGPU: renderPassSetPipeline :: MonadIO m => RenderPassEncoder -> RenderPipeline -> m ()
- WGPU: requestAdapter :: Surface -> IO (Maybe Adapter)
+ WGPU: requestAdapter :: MonadIO m => Surface -> m (Maybe Adapter)
- WGPU: requestDevice :: Adapter -> DeviceDescriptor -> IO (Maybe Device)
+ WGPU: requestDevice :: MonadIO m => Adapter -> DeviceDescriptor -> m (Maybe Device)
- WGPU: setLogLevel :: Instance -> LogLevel -> IO ()
+ WGPU: setLogLevel :: MonadIO m => Instance -> LogLevel -> m ()
- WGPU: swapChainPresent :: SwapChain -> IO ()
+ WGPU: swapChainPresent :: MonadIO m => SwapChain -> m ()
- WGPU: withInstance :: FilePath -> Maybe LogCallback -> (Instance -> IO a) -> IO a
+ WGPU: withInstance :: MonadIO m => FilePath -> (m Instance -> (Instance -> m ()) -> r) -> r
- WGPU: withPlatformInstance :: Maybe LogCallback -> (Instance -> IO a) -> IO a
+ WGPU: withPlatformInstance :: MonadIO m => (m Instance -> (Instance -> m ()) -> r) -> r

Files

CHANGELOG.md view
@@ -1,5 +1,15 @@ # Revision history for wgpu-hs +## 0.3.0.0 -- 2021-08-30++- Add Classy interface - supply parameters from `ReaderT`.+- Add SDL surfaces.+- Add `BoneYard` package for helpful application skeleton code.+- Switch to using `MonadIO` instead of plain `IO` when possible.+- Supply bracketing functions for `withXXX`.+- Add extra `{-# INLINABLE #-}` pragmas.+- Add ability to query Adapter properties.+ ## 0.2.0.1 -- 2021-08-24  - Added Linux support for surfaces and DLL loading.
+ examples/triangle-glfw/TriangleGLFW.hs view
@@ -0,0 +1,246 @@+{-# LANGUAGE OverloadedLists #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}++-- | Let's draw a triangle!+module Main (main) where++import Control.Concurrent+  ( MVar,+    newEmptyMVar,+    putMVar,+    tryReadMVar,+    tryTakeMVar,+  )+import Control.Exception (bracket)+import Control.Monad (unless)+import Control.Monad.Trans.Class (lift)+import Control.Monad.Trans.Except (runExceptT)+import Control.Monad.Trans.Maybe (MaybeT (MaybeT), maybeToExceptT)+import Data.Default (def)+import Data.Maybe (isNothing)+import Data.Text (Text)+import qualified Data.Text as Text+import qualified Data.Text.IO as TextIO+import qualified Graphics.UI.GLFW as GLFW+import System.Exit (exitFailure)+import WGPU (SwapChain)+import qualified WGPU+import qualified WGPU.GLFW.Surface++main :: IO ()+main = do+  TextIO.putStrLn "GLFW Triangle Example"++  -- start GLFW+  initResult <- GLFW.init+  unless initResult $ do+    TextIO.putStrLn "Failed to initialize GLFW"+    exitFailure++  -- create the GLFW window without a "client API"+  GLFW.windowHint (GLFW.WindowHint'ClientAPI GLFW.ClientAPI'NoAPI)+  window <- do+    mWin <- GLFW.createWindow 640 480 "GLFW Triangle Example" Nothing Nothing+    case mWin of+      Just w -> pure w+      Nothing -> do+        TextIO.putStrLn "Failed to create GLFW window"+        exitFailure++  WGPU.withPlatformInstance bracket $ \inst -> do+    -- attach the logger and set the logging level+    WGPU.connectLog inst+    WGPU.setLogLevel inst WGPU.Warn++    -- print the version of the WGPU library+    version <- WGPU.getVersion inst+    TextIO.putStrLn $ "WGPU version: " <> WGPU.versionToText version++    -- fetch resources (surface, adpater, device)+    Resources {..} <- getResources inst window >>= getOrFail++    shader <- WGPU.createShaderModuleWGSL device "shader" shaderSrc+    swapChainFormat <- WGPU.getSwapChainPreferredFormat surface adapter+    swapChainMVar <- newEmptyMVar+    _ <- updateSwapChain device surface window swapChainFormat swapChainMVar++    pipelineLayout <-+      WGPU.createPipelineLayout+        device+        (WGPU.PipelineLayoutDescriptor "Pipeline" [])+    pipeline <-+      WGPU.createRenderPipeline+        device+        WGPU.RenderPipelineDescriptor+          { renderPipelineLabel = "Render Pipeline",+            layout = WGPU.SJust pipelineLayout,+            vertex = WGPU.VertexState shader "vs_main" [],+            primitive = def,+            depthStencil = WGPU.SNothing,+            multisample = WGPU.MultisampleState 1 0xFFFFFFFF False,+            fragment =+              WGPU.SJust $+                WGPU.FragmentState+                  shader+                  "fs_main"+                  [ WGPU.ColorTargetState+                      swapChainFormat+                      (WGPU.SJust (WGPU.BlendState def def))+                      WGPU.colorWriteMaskAll+                  ]+          }++    let doDraw =+          draw+            device+            surface+            window+            pipeline+            queue+            swapChainFormat+            swapChainMVar++    let loop = do+          doDraw++          -- handle GLFW quit event+          GLFW.pollEvents+          shouldClose <- GLFW.windowShouldClose window+          unless shouldClose loop++    loop++  -- close down GLFW+  GLFW.destroyWindow window+  GLFW.terminate++newtype Error = Error Text deriving (Eq, Show)++getOrFail :: Either Error a -> IO a+getOrFail ma =+  case ma of+    Right x -> pure x+    Left err -> failWith err++failWith :: Error -> IO a+failWith (Error err) = do+  TextIO.putStrLn err+  exitFailure++data Resources = Resources+  { surface :: WGPU.Surface,+    adapter :: WGPU.Adapter,+    device :: WGPU.Device,+    queue :: WGPU.Queue+  }++getResources :: WGPU.Instance -> GLFW.Window -> IO (Either Error Resources)+getResources inst window = runExceptT $ do+  -- fetch a surface for the window+  surface <- lift $ WGPU.GLFW.Surface.createSurface inst window+  -- fetch an adapter for the surface+  adapter <-+    maybeToExceptT+      (Error "Failed to obtain WGPU Adapter")+      (MaybeT $ WGPU.requestAdapter surface)+  -- fetch a device for the adapter+  let deviceDescriptor :: WGPU.DeviceDescriptor+      deviceDescriptor = def {WGPU.limits = def {WGPU.maxBindGroups = 1}}+  device <-+    maybeToExceptT+      (Error "Failed to obtain WGPU Device")+      (MaybeT $ WGPU.requestDevice adapter deviceDescriptor)++  queue <- lift $ WGPU.getQueue device++  pure Resources {..}++updateSwapChain ::+  WGPU.Device ->+  WGPU.Surface ->+  GLFW.Window ->+  WGPU.TextureFormat ->+  MVar ((Int, Int), WGPU.SwapChain) ->+  IO WGPU.SwapChain+updateSwapChain device surface window textureFormat swapChainMVar = do+  mOldSwapChain <- fmap snd <$> tryReadMVar swapChainMVar+  oldSz <- maybe (0, 0) fst <$> tryReadMVar swapChainMVar+  curSz <- GLFW.getWindowSize window+  case (mOldSwapChain, curSz /= oldSz) of+    x | isNothing (fst x) || snd x -> do+      swapChain <-+        WGPU.createSwapChain+          device+          surface+          WGPU.SwapChainDescriptor+            { swapChainLabel = "SwapChain",+              usage = WGPU.TextureUsageRenderAttachment,+              swapChainFormat = textureFormat,+              width = fromIntegral . fst $ curSz,+              height = fromIntegral . snd $ curSz,+              presentMode = WGPU.PresentModeFifo+            }+      _ <- tryTakeMVar swapChainMVar+      putMVar swapChainMVar (curSz, swapChain)+      pure swapChain+    (Just swapChain, False) -> pure swapChain+    _ -> error "should not reach this case"++draw ::+  WGPU.Device ->+  WGPU.Surface ->+  GLFW.Window ->+  WGPU.RenderPipeline ->+  WGPU.Queue ->+  WGPU.TextureFormat ->+  MVar ((Int, Int), WGPU.SwapChain) ->+  IO ()+draw device surface window pipeline queue swapChainFormat swapChainMVar = do+  -- update swapchain if the window size is different+  swapChain <-+    updateSwapChain device surface window swapChainFormat swapChainMVar+  -- render+  nextTexture <- WGPU.getSwapChainCurrentTextureView swapChain+  encoder <- WGPU.createCommandEncoder device "Command Encoder"+  renderPass <-+    WGPU.beginRenderPass+      encoder+      ( WGPU.RenderPassDescriptor+          { renderPassLabel = "Render Pass",+            colorAttachments =+              [ WGPU.RenderPassColorAttachment+                  nextTexture+                  WGPU.SNothing+                  ( WGPU.Operations+                      (WGPU.LoadOpClear (WGPU.Color 0 0 0 1))+                      WGPU.StoreOpStore+                  )+              ],+            depthStencilAttachment = WGPU.SNothing+          }+      )+  WGPU.renderPassSetPipeline renderPass pipeline+  WGPU.renderPassDraw renderPass (WGPU.Range 0 3) (WGPU.Range 0 1)+  WGPU.endRenderPass renderPass+  commandBuffer <- WGPU.commandEncoderFinish encoder "Command Buffer"+  WGPU.queueSubmit queue [commandBuffer]+  WGPU.swapChainPresent swapChain++shaderSrc :: WGPU.WGSL+shaderSrc =+  WGPU.WGSL $+    Text.intercalate+      "\n"+      [ "[[stage(vertex)]]",+        "fn vs_main([[builtin(vertex_index)]] in_vertex_index: u32) -> [[builtin(position)]] vec4<f32> {",+        "  let x = f32(i32(in_vertex_index) - 1);",+        "  let y = f32(i32(in_vertex_index & 1u) * 2 - 1);",+        "  return vec4<f32>(x, y, 0.0, 1.0);",+        "}",+        "",+        "[[stage(fragment)]]",+        "fn fs_main([[builtin(position)]] in: vec4<f32>) -> [[location(0)]] vec4<f32> {",+        "  return vec4<f32>(in.x/640.0, in.y/480.0, 1.0, 1.0);",+        "}"+      ]
+ examples/triangle-sdl/TriangleSDL.hs view
@@ -0,0 +1,268 @@+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedLists #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE RecordWildCards #-}++module Main (main) where++import Control.Exception.Safe (MonadThrow)+import Control.Lens (Lens', lens)+import Control.Monad (unless)+import Control.Monad.IO.Class (MonadIO, liftIO)+import Control.Monad.Reader (MonadReader, ReaderT (runReaderT))+import Control.Monad.Trans.Resource (MonadResource, ResourceT, runResourceT)+import Data.Default (def)+import Data.Has (Has, hasLens)+import Data.Maybe (fromMaybe)+import qualified Data.String.QQ as SQQ+import Data.Text (Text)+import qualified Data.Text.IO as TextIO+import qualified SDL+import qualified WGPU+import qualified WGPU.BoneYard.SimpleSDL as SimpleSDL+import qualified WGPU.Classy as C+import Prelude hiding (putStrLn)++main :: IO ()+main = runResourceT $ do+  putStrLn "Start"++  -- Parameters for the static resource initialization+  let params =+        SimpleSDL.Params+          { title = "SDL Triangle Example",+            mDeviceDescriptor =+              WGPU.SJust $+                def {WGPU.limits = def {WGPU.maxBindGroups = 1}}+          }++  -- Initialize the app static resources. This creates the SDL window, the+  -- WGPU Instance, etc.+  resources <- SimpleSDL.loadResources params++  -- Create other state holders+  shaders <- SimpleSDL.emptyShaders+  renderPipelines <- SimpleSDL.emptyRenderPipelines+  swapChainState <- SimpleSDL.emptySwapChainState++  -- Run the app+  runApp Env {..} app++-- | The core application.+app :: App ()+app = do+  putStrLn "SDL Triangle Example"++  -- Dump some debugging stuff; set the WGPU log, etc.+  C.getVersion >>= \v -> putStrLn $ "WGPU version: " <> WGPU.versionToText v+  C.getAdapterProperties >>= \p -> putStrLn $ WGPU.adapterPropertiesToText p+  C.setLogLevel WGPU.Warn+  C.connectLog++  -- Create application-specific dynamic resources: shader and pipeline.+  initApp++  -- Application loop+  let appLoop :: App ()+      appLoop = do+        render+        events <- SDL.pollEvents+        let qPressed, windowClosed, shouldClose :: Bool+            qPressed = any eventIsQPress events+            windowClosed = any eventIsWindowClose events+            shouldClose = qPressed || windowClosed+        unless shouldClose appLoop+  appLoop++-- | The rendering action.+render :: App ()+render = do+  SimpleSDL.withSwapChain $ do+    nextTexture <- C.getSwapChainCurrentTextureView+    renderPipeline <- SimpleSDL.getRenderPipeline "renderPipeline"+    commandBuffer <-+      C.buildCommandBuffer "Command Encoder" "Command Buffer" $ do+        let renderPassDescriptor =+              WGPU.RenderPassDescriptor+                { renderPassLabel = "Render Pass",+                  colorAttachments =+                    [ WGPU.RenderPassColorAttachment+                        { colorView = nextTexture,+                          resolveTarget = WGPU.SNothing,+                          operations =+                            WGPU.Operations+                              { load = WGPU.LoadOpClear (WGPU.Color 0 0 0 1),+                                store = WGPU.StoreOpStore+                              }+                        }+                    ],+                  depthStencilAttachment = WGPU.SNothing+                }+        C.buildRenderPass renderPassDescriptor $ do+          C.renderPassSetPipeline renderPipeline+          C.renderPassDraw (WGPU.Range 0 3) (WGPU.Range 0 1)+    C.queueSubmit' [commandBuffer]+    C.swapChainPresent++-- | Application initialization+initApp :: App ()+initApp = do+  shaderModule <- SimpleSDL.compileWGSL "shader" shaderSrc++  let pipelineLayoutDescriptor = WGPU.PipelineLayoutDescriptor "Pipeline" []+  pipelineLayout <- C.createPipelineLayout pipelineLayoutDescriptor++  swapChainFormat <- C.getSwapChainPreferredFormat+  let renderPipelineDescriptor =+        WGPU.RenderPipelineDescriptor+          { renderPipelineLabel = "Render Pipeline",+            layout = WGPU.SJust pipelineLayout,+            vertex = WGPU.VertexState shaderModule "vs_main" [],+            primitive = def,+            depthStencil = WGPU.SNothing,+            multisample = WGPU.MultisampleState 1 0xFFFFFFFF False,+            fragment =+              WGPU.SJust $+                WGPU.FragmentState+                  shaderModule+                  "fs_main"+                  [ WGPU.ColorTargetState+                      swapChainFormat+                      (WGPU.SJust (WGPU.BlendState def def))+                      WGPU.colorWriteMaskAll+                  ]+          }+  _ <- SimpleSDL.createRenderPipeline "renderPipeline" renderPipelineDescriptor+  pure ()++shaderSrc :: WGPU.WGSL+shaderSrc =+  WGPU.WGSL+    [SQQ.s|+[[stage(vertex)]]+fn vs_main([[builtin(vertex_index)]] in_vertex_index: u32) -> [[builtin(position)]] vec4<f32> {+  let x = f32(i32(in_vertex_index) - 1);+  let y = f32(i32(in_vertex_index & 1u) * 2 - 1);+  return vec4<f32>(x, y, 0.0, 1.0);+}++[[stage(fragment)]]+fn fs_main([[builtin(position)]] in: vec4<f32>) -> [[location(0)]] vec4<f32> {+  return vec4<f32>(in.x/640.0, in.y/480.0, 1.0, 1.0);+}+|]++-------------------------------------------------------------------------------+-- Application Monad++-- | Run the application.+runApp ::+  -- | Environment for the application. This contains both static resources and+  -- 'MVar's that contain dynamic resources.+  Env ->+  -- | Application to run.+  App a ->+  -- | Resolved 'ResourceT' action. 'ResourceT' is used here so that portions of+  -- the app can be cleaned up automatically.+  ResourceT IO a+runApp env a = runReaderT (unApp a) env++-- | Application monad data type.+newtype App a = App {unApp :: ReaderT Env (ResourceT IO) a}+  deriving newtype+    ( Functor,+      Applicative,+      Monad,+      MonadThrow,+      MonadIO,+      MonadResource,+      MonadReader Env+    )++-- | Environment for the application.+--+-- The environment stores both static and dynamic data that the application+-- uses. This type can be customized at-will to add additional state, such as+-- game state or vizualisation state.+--+-- The 'Has' type class instances below describe how to access the components+-- of the environment that are required for the various operations.+data Env = Env+  { -- | Static resources (Instance, Window, Surface, Adapter, Device, Queue).+    resources :: !SimpleSDL.Resources,+    -- | Map of shaders. Shaders can be added to this map by compiling them,+    -- and then accessed as required. (See the 'SimpleSDL' module.)+    shaders :: !SimpleSDL.Shaders,+    -- | Map of render pipelines. Different render pipelines can be added here+    -- and then accessed as required. (See the 'SimpleSDL' module.)+    renderPipelines :: !SimpleSDL.RenderPipelines,+    -- | Swap chain state. When the app window is re-sized, the swap chain must+    -- be re-created. This type contains the current swap chain state.+    swapChainState :: !SimpleSDL.SwapChainState+  }++instance Has SimpleSDL.Resources Env where+  hasLens = lens resources (\s x -> s {resources = x})++instance Has SimpleSDL.Shaders Env where+  hasLens = lens shaders (\s x -> s {shaders = x})++instance Has SimpleSDL.RenderPipelines Env where+  hasLens = lens renderPipelines (\s x -> s {renderPipelines = x})++instance Has SimpleSDL.SwapChainState Env where+  hasLens = lens swapChainState (\s x -> s {swapChainState = x})++envResourcesL :: Lens' Env SimpleSDL.Resources+envResourcesL = hasLens++instance Has WGPU.Instance Env where hasLens = envResourcesL . hasLens++instance Has SDL.Window Env where hasLens = envResourcesL . hasLens++instance Has WGPU.Surface Env where hasLens = envResourcesL . hasLens++instance Has WGPU.Adapter Env where hasLens = envResourcesL . hasLens++instance Has WGPU.Device Env where hasLens = envResourcesL . hasLens++instance Has WGPU.Queue Env where hasLens = envResourcesL . hasLens++-------------------------------------------------------------------------------+-- SDL Event Helpers++mKeyEvent :: SDL.Event -> Maybe SDL.KeyboardEventData+mKeyEvent event =+  case SDL.eventPayload event of+    SDL.KeyboardEvent keyboardEventData -> Just keyboardEventData+    _ -> Nothing++mKeyPressed :: SDL.KeyboardEventData -> Maybe SDL.Keysym+mKeyPressed ed =+  case SDL.keyboardEventKeyMotion ed of+    SDL.Pressed -> Just (SDL.keyboardEventKeysym ed)+    _ -> Nothing++eventIsQPress :: SDL.Event -> Bool+eventIsQPress event =+  fromMaybe False $+    mKeyEvent event >>= mKeyPressed >>= \keySym -> do+      if SDL.keysymKeycode keySym == SDL.KeycodeQ+        then Just True+        else Nothing++eventIsWindowClose :: SDL.Event -> Bool+eventIsWindowClose event =+  case SDL.eventPayload event of+    SDL.WindowClosedEvent _ -> True+    _ -> False++-------------------------------------------------------------------------------+-- Miscellaneous++-- | Print text to the console.+putStrLn :: MonadIO m => Text -> m ()+putStrLn = liftIO . TextIO.putStrLn
− examples/triangle/Main.hs
@@ -1,219 +0,0 @@-{-# LANGUAGE OverloadedLists #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards #-}---- | Let's draw a triangle!-module Main (main) where--import Control.Monad (unless, when)-import Control.Monad.Trans.Class (lift)-import Control.Monad.Trans.Except (runExceptT)-import Control.Monad.Trans.Maybe (MaybeT (MaybeT), maybeToExceptT)-import Data.Default (def)-import Data.IORef (IORef, newIORef, readIORef, writeIORef)-import Data.Text (Text)-import qualified Data.Text as Text-import qualified Data.Text.IO as TextIO-import qualified Graphics.UI.GLFW as GLFW-import System.Exit (exitFailure)-import qualified WGPU--main :: IO ()-main = do-  TextIO.putStrLn "Triangle Example"--  -- start GLFW-  initResult <- GLFW.init-  unless initResult $ do-    TextIO.putStrLn "Failed to initialize GLFW"-    exitFailure--  -- create the GLFW window without a "client API"-  windowSzRef <- newIORef (640, 480)-  GLFW.windowHint (GLFW.WindowHint'ClientAPI GLFW.ClientAPI'NoAPI)-  window <- do-    mWin <- GLFW.createWindow 640 480 "Triangle" Nothing Nothing-    case mWin of-      Just w -> pure w-      Nothing -> do-        TextIO.putStrLn "Failed to create GLFW window"-        exitFailure--  WGPU.withPlatformInstance (Just WGPU.logStdout) $ \inst -> do-    -- set the logging level-    WGPU.setLogLevel inst WGPU.Warn--    -- print the version of the WGPU library-    version <- WGPU.getVersion inst-    TextIO.putStrLn $ "WGPU version: " <> WGPU.versionToText version--    -- fetch resources (surface, adpater, device)-    Resources {..} <- getResources inst window >>= getOrFail--    shader <- WGPU.createShaderModuleWGSL device "shader" shaderSrc-    swapChainFormat <- WGPU.getSwapChainPreferredFormat surface adapter-    swapChain <--      WGPU.createSwapChain-        device-        surface-        WGPU.SwapChainDescriptor-          { swapChainLabel = "SwapChain",-            usage = WGPU.TextureUsageRenderAttachment,-            swapChainFormat = swapChainFormat,-            width = 640,-            height = 480,-            presentMode = WGPU.PresentModeFifo-          }-    swapChainRef <- newIORef swapChain-    pipelineLayout <--      WGPU.createPipelineLayout-        device-        (WGPU.PipelineLayoutDescriptor "Pipeline" [])-    pipeline <--      WGPU.createRenderPipeline-        device-        WGPU.RenderPipelineDescriptor-          { renderPipelineLabel = "Render Pipeline",-            layout = WGPU.SJust pipelineLayout,-            vertex = WGPU.VertexState shader "vs_main" [],-            primitive = def,-            depthStencil = WGPU.SNothing,-            multisample = WGPU.MultisampleState 1 0xFFFFFFFF False,-            fragment =-              WGPU.SJust $-                WGPU.FragmentState-                  shader-                  "fs_main"-                  [ WGPU.ColorTargetState-                      swapChainFormat-                      (WGPU.SJust (WGPU.BlendState def def))-                      WGPU.colorWriteMaskAll-                  ]-          }--    let loop = do-          -- update swapchain if the window size is different-          updateSwapChain device surface window swapChainFormat windowSzRef swapChainRef-          -- render-          nextTexture <- WGPU.getSwapChainCurrentTextureView swapChain-          encoder <- WGPU.createCommandEncoder device "Command Encoder"-          renderPass <--            WGPU.beginRenderPass-              encoder-              ( WGPU.RenderPassDescriptor-                  { renderPassLabel = "Render Pass",-                    colorAttachments =-                      [ WGPU.RenderPassColorAttachment-                          nextTexture-                          WGPU.SNothing-                          ( WGPU.Operations-                              (WGPU.LoadOpClear (WGPU.Color 0 0 0 1))-                              WGPU.StoreOpStore-                          )-                      ],-                    depthStencilAttachment = WGPU.SNothing-                  }-              )-          WGPU.renderPassSetPipeline renderPass pipeline-          WGPU.renderPassDraw renderPass (WGPU.Range 0 3) (WGPU.Range 0 1)-          WGPU.endRenderPass renderPass-          commandBuffer <- WGPU.commandEncoderFinish encoder "Command Buffer"-          WGPU.queueSubmit queue [commandBuffer]-          WGPU.swapChainPresent swapChain--          -- handle GLFW quit event-          GLFW.pollEvents-          shouldClose <- GLFW.windowShouldClose window-          unless shouldClose loop--    loop--  -- close down GLFW-  GLFW.destroyWindow window-  GLFW.terminate--newtype Error = Error Text deriving (Eq, Show)--getOrFail :: Either Error a -> IO a-getOrFail ma =-  case ma of-    Right x -> pure x-    Left err -> failWith err--failWith :: Error -> IO a-failWith (Error err) = do-  TextIO.putStrLn err-  exitFailure--data Resources = Resources-  { surface :: WGPU.Surface,-    adapter :: WGPU.Adapter,-    device :: WGPU.Device,-    queue :: WGPU.Queue-  }--getResources :: WGPU.Instance -> GLFW.Window -> IO (Either Error Resources)-getResources inst window = runExceptT $ do-  -- fetch a surface for the window-  surface <- lift $ WGPU.createGLFWSurface inst window-  -- fetch an adapter for the surface-  adapter <--    maybeToExceptT-      (Error "Failed to obtain WGPU Adapter")-      (MaybeT $ WGPU.requestAdapter surface)-  -- fetch a device for the adapter-  let deviceDescriptor :: WGPU.DeviceDescriptor-      deviceDescriptor = def {WGPU.limits = def {WGPU.maxBindGroups = 1}}-  device <--    maybeToExceptT-      (Error "Failed to obtain WGPU Device")-      (MaybeT $ WGPU.requestDevice adapter deviceDescriptor)--  queue <- lift $ WGPU.getQueue device--  pure Resources {..}--updateSwapChain ::-  WGPU.Device ->-  WGPU.Surface ->-  GLFW.Window ->-  WGPU.TextureFormat ->-  IORef (Int, Int) ->-  IORef WGPU.SwapChain ->-  IO ()-updateSwapChain device surface window textureFormat szRef swapChainRef = do-  oldSz <- readIORef szRef-  curSz <- GLFW.getWindowSize window-  when (curSz /= oldSz) $ do-    writeIORef szRef curSz-    swapChain <--      WGPU.createSwapChain-        device-        surface-        WGPU.SwapChainDescriptor-          { swapChainLabel = "SwapChain",-            usage = WGPU.TextureUsageRenderAttachment,-            swapChainFormat = textureFormat,-            width = fromIntegral . fst $ curSz,-            height = fromIntegral . snd $ curSz,-            presentMode = WGPU.PresentModeFifo-          }-    writeIORef swapChainRef swapChain--shaderSrc :: WGPU.WGSL-shaderSrc =-  WGPU.WGSL $-    Text.intercalate-      "\n"-      [ "[[stage(vertex)]]",-        "fn vs_main([[builtin(vertex_index)]] in_vertex_index: u32) -> [[builtin(position)]] vec4<f32> {",-        "  let x = f32(i32(in_vertex_index) - 1);",-        "  let y = f32(i32(in_vertex_index & 1u) * 2 - 1);",-        "  return vec4<f32>(x, y, 0.0, 1.0);",-        "}",-        "",-        "[[stage(fragment)]]",-        "fn fs_main([[builtin(position)]] in: vec4<f32>) -> [[location(0)]] vec4<f32> {",-        "  return vec4<f32>(in.x/640.0, in.y/480.0, 1.0, 1.0);",-        "}"-      ]
src-internal/WGPU/Internal/Adapter.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE ForeignFunctionInterface #-} {-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} @@ -9,21 +10,50 @@ module WGPU.Internal.Adapter   ( -- * Types     Adapter (..),+    AdapterType (..),+    BackendType (..),+    AdapterProperties (..),      -- * Functions     requestAdapter,+    getAdapterProperties,+    adapterPropertiesToText,   ) where -import Control.Concurrent (MVar, newEmptyMVar, putMVar, takeMVar)-import Control.Monad.IO.Class (liftIO)-import Control.Monad.Trans.Cont (evalContT)-import Foreign (freeHaskellFunPtr, nullPtr)+import Control.Monad.IO.Class (MonadIO, liftIO)+import Data.Text (Text)+import qualified Data.Text as Text+import Data.Word (Word32)+import Foreign (nullPtr) import Foreign.Ptr (Ptr)+import Text.Printf (printf)+import WGPU.Internal.ChainedStruct (ChainedStruct (EmptyChain)) import WGPU.Internal.Instance (Instance, wgpuHsInstance)-import WGPU.Internal.Memory (ToRaw, raw, rawPtr, showWithPtr)+import WGPU.Internal.Memory+  ( FromRaw,+    ToRaw,+    allocaC,+    evalContT,+    freeHaskellFunPtr,+    fromRaw,+    fromRawPtr,+    newEmptyMVar,+    putMVar,+    raw,+    rawPtr,+    showWithPtr,+    takeMVar,+  ) import WGPU.Internal.Surface (Surface, surfaceInst)+import WGPU.Raw.Generated.Enum.WGPUAdapterType (WGPUAdapterType)+import qualified WGPU.Raw.Generated.Enum.WGPUAdapterType as WGPUAdapterType+import WGPU.Raw.Generated.Enum.WGPUBackendType (WGPUBackendType)+import qualified WGPU.Raw.Generated.Enum.WGPUBackendType as WGPUBackendType+import qualified WGPU.Raw.Generated.Enum.WGPUNativeSType as WGPUSType import qualified WGPU.Raw.Generated.Fun as RawFun+import WGPU.Raw.Generated.Struct.WGPUAdapterProperties (WGPUAdapterProperties)+import qualified WGPU.Raw.Generated.Struct.WGPUAdapterProperties as WGPUAdapterProperties import WGPU.Raw.Generated.Struct.WGPURequestAdapterOptions   ( WGPURequestAdapterOptions,   )@@ -60,41 +90,149 @@  ------------------------------------------------------------------------------- +-- | Physical device type.+data AdapterType+  = AdapterTypeDiscreteGPU+  | AdapterTypeIntegratedGPU+  | AdapterTypeCPU+  | AdapterTypeUnknown+  deriving (Eq, Show)++instance ToRaw AdapterType WGPUAdapterType where+  raw adapterType = case adapterType of+    AdapterTypeDiscreteGPU -> pure WGPUAdapterType.DiscreteGPU+    AdapterTypeIntegratedGPU -> pure WGPUAdapterType.IntegratedGPU+    AdapterTypeCPU -> pure WGPUAdapterType.CPU+    AdapterTypeUnknown -> pure WGPUAdapterType.Unknown++instance FromRaw WGPUAdapterType AdapterType where+  fromRaw wAdapterType = pure $ case wAdapterType of+    WGPUAdapterType.DiscreteGPU -> AdapterTypeDiscreteGPU+    WGPUAdapterType.IntegratedGPU -> AdapterTypeIntegratedGPU+    WGPUAdapterType.CPU -> AdapterTypeCPU+    WGPUAdapterType.Unknown -> AdapterTypeUnknown+    _ -> AdapterTypeUnknown++-------------------------------------------------------------------------------++-- | Backends supported by WGPU.+data BackendType+  = BackendTypeNull+  | BackendTypeD3D11+  | BackendTypeD3D12+  | BackendTypeMetal+  | BackendTypeVulkan+  | BackendTypeOpenGL+  | BackendTypeOpenGLES+  deriving (Eq, Show)++instance ToRaw BackendType WGPUBackendType where+  raw backendType = case backendType of+    BackendTypeNull -> pure WGPUBackendType.Null+    BackendTypeD3D11 -> pure WGPUBackendType.D3D11+    BackendTypeD3D12 -> pure WGPUBackendType.D3D12+    BackendTypeMetal -> pure WGPUBackendType.Metal+    BackendTypeVulkan -> pure WGPUBackendType.Vulkan+    BackendTypeOpenGL -> pure WGPUBackendType.OpenGL+    BackendTypeOpenGLES -> pure WGPUBackendType.OpenGLES++instance FromRaw WGPUBackendType BackendType where+  fromRaw wBackendType = pure $ case wBackendType of+    WGPUBackendType.Null -> BackendTypeNull+    WGPUBackendType.D3D11 -> BackendTypeD3D11+    WGPUBackendType.D3D12 -> BackendTypeD3D12+    WGPUBackendType.Metal -> BackendTypeMetal+    WGPUBackendType.Vulkan -> BackendTypeVulkan+    WGPUBackendType.OpenGL -> BackendTypeOpenGL+    WGPUBackendType.OpenGLES -> BackendTypeOpenGLES+    _ -> BackendTypeNull++-------------------------------------------------------------------------------++data AdapterProperties = AdapterProperties+  { deviceID :: !Word32,+    vendorID :: !Word32,+    adapterName :: !Text,+    driverDescription :: !Text,+    adapterType :: !AdapterType,+    backendType :: !BackendType+  }+  deriving (Eq, Show)++instance ToRaw AdapterProperties WGPUAdapterProperties where+  raw AdapterProperties {..} = do+    chain_ptr <- rawPtr (EmptyChain WGPUSType.AdapterExtras)+    name_ptr <- rawPtr adapterName+    driverDescription_ptr <- rawPtr driverDescription+    n_adapterType <- raw adapterType+    n_backendType <- raw backendType+    pure+      WGPUAdapterProperties.WGPUAdapterProperties+        { nextInChain = chain_ptr,+          deviceID = deviceID,+          vendorID = vendorID,+          name = name_ptr,+          driverDescription = driverDescription_ptr,+          adapterType = n_adapterType,+          backendType = n_backendType+        }++instance FromRaw WGPUAdapterProperties AdapterProperties where+  fromRaw WGPUAdapterProperties.WGPUAdapterProperties {..} = do+    n_adapterName <- fromRaw name+    n_driverDescription <- fromRaw driverDescription+    n_adapterType <- fromRaw adapterType+    n_backendType <- fromRaw backendType+    pure+      AdapterProperties+        { deviceID = deviceID,+          vendorID = vendorID,+          adapterName = n_adapterName,+          driverDescription = n_driverDescription,+          adapterType = n_adapterType,+          backendType = n_backendType+        }++-------------------------------------------------------------------------------+ -- | Request an 'Adapter' that is compatible with a given 'Surface'. -- -- This action blocks until an available adapter is returned. requestAdapter ::+  (MonadIO m) =>   -- | Existing surface for which to request an @Adapter@.   Surface ->   -- | The returned @Adapter@, if it could be retrieved.-  IO (Maybe Adapter)-requestAdapter surface = evalContT $ do+  m (Maybe Adapter)+requestAdapter surface = liftIO . evalContT $ do   let inst = surfaceInst surface -  adapterMVar :: MVar WGPUAdapter <- liftIO newEmptyMVar--  let adapterCallback :: WGPUAdapter -> Ptr () -> IO ()-      adapterCallback adapter _ = putMVar adapterMVar adapter-  adapterCallback_c <- liftIO $ mkAdapterCallback adapterCallback+  adaptmv <- newEmptyMVar+  callback <- mkAdapterCallback (\a _ -> putMVar adaptmv a)    requestAdapterOptions_ptr <- rawPtr (RequestAdapterOptions surface)-  liftIO $-    RawFun.wgpuInstanceRequestAdapter-      (wgpuHsInstance inst)-      (WGPUInstance nullPtr)-      requestAdapterOptions_ptr-      adapterCallback_c-      nullPtr+  RawFun.wgpuInstanceRequestAdapter+    (wgpuHsInstance inst)+    (WGPUInstance nullPtr)+    requestAdapterOptions_ptr+    callback+    nullPtr -  adapter <- liftIO $ takeMVar adapterMVar-  liftIO $ freeHaskellFunPtr adapterCallback_c+  adapter <- takeMVar adaptmv+  freeHaskellFunPtr callback    pure $ case adapter of     WGPUAdapter ptr | ptr == nullPtr -> Nothing     WGPUAdapter _ -> Just (Adapter inst adapter) +mkAdapterCallback ::+  MonadIO m =>+  (WGPUAdapter -> Ptr () -> IO ()) ->+  m WGPURequestAdapterCallback+mkAdapterCallback = liftIO . mkAdapterCallbackIO+ foreign import ccall "wrapper"-  mkAdapterCallback ::+  mkAdapterCallbackIO ::     (WGPUAdapter -> Ptr () -> IO ()) -> IO WGPURequestAdapterCallback  newtype RequestAdapterOptions = RequestAdapterOptions {compatibleSurface :: Surface}@@ -107,3 +245,54 @@         { nextInChain = nullPtr,           compatibleSurface = n_surface         }++-------------------------------------------------------------------------------++-- | Get information about an adapter.+getAdapterProperties :: MonadIO m => Adapter -> m AdapterProperties+getAdapterProperties adapter = liftIO $+  evalContT $ do+    wgpuAdapterProperties_ptr <- allocaC+    RawFun.wgpuAdapterGetProperties+      (wgpuHsInstance . adapterInst $ adapter)+      (wgpuAdapter adapter)+      wgpuAdapterProperties_ptr+    fromRawPtr wgpuAdapterProperties_ptr++-- | Format adapter properties into a multi-line block of text.+--+-- This can be useful for debugging purposes.+adapterPropertiesToText :: AdapterProperties -> Text+adapterPropertiesToText AdapterProperties {..} =+  Text.unlines+    [ "Adapter Properties:",+      "  device ID   : " <> Text.pack (printf "0x%08x" deviceID),+      "  vendor ID   : " <> Text.pack (printf "0x%08x" vendorID),+      "  name        : "+        <> if Text.null adapterName+          then "(unknown)"+          else adapterName,+      "  description : "+        <> if Text.null driverDescription+          then "(unknown)"+          else driverDescription,+      "  type        : " <> adapterTypeTxt,+      "  backend     : " <> backendTypeTxt+    ]+  where+    adapterTypeTxt :: Text+    adapterTypeTxt = case adapterType of+      AdapterTypeDiscreteGPU -> "Discrete GPU"+      AdapterTypeIntegratedGPU -> "Integrated GPU"+      AdapterTypeCPU -> "CPU"+      AdapterTypeUnknown -> "(unknown)"++    backendTypeTxt :: Text+    backendTypeTxt = case backendType of+      BackendTypeNull -> "(unknown)"+      BackendTypeD3D11 -> "D3D 11"+      BackendTypeD3D12 -> "D3D 12"+      BackendTypeMetal -> "Metal"+      BackendTypeVulkan -> "Vulkan"+      BackendTypeOpenGL -> "OpenGL"+      BackendTypeOpenGLES -> "OpenGL ES"
src-internal/WGPU/Internal/Binding.hs view
@@ -26,8 +26,7 @@   ) where -import Control.Monad.IO.Class (liftIO)-import Control.Monad.Trans.Cont (evalContT)+import Control.Monad.IO.Class (MonadIO, liftIO) import Data.Bits ((.|.)) import Data.Text (Text) import Data.Vector (Vector)@@ -35,8 +34,15 @@ import Foreign (nullPtr) import Foreign.C (CBool (CBool)) import WGPU.Internal.Device (Device, deviceInst, wgpuDevice)-import WGPU.Internal.Instance (Instance, wgpuHsInstance)-import WGPU.Internal.Memory (ToRaw, raw, rawArrayPtr, rawPtr, showWithPtr)+import WGPU.Internal.Instance (wgpuHsInstance)+import WGPU.Internal.Memory+  ( ToRaw,+    evalContT,+    raw,+    rawArrayPtr,+    rawPtr,+    showWithPtr,+  ) import WGPU.Internal.SMaybe (SMaybe, fromSMaybe) import WGPU.Internal.Texture (TextureFormat, TextureViewDimension) import WGPU.Raw.Generated.Enum.WGPUBufferBindingType (WGPUBufferBindingType)@@ -92,23 +98,22 @@  -- | Creates a 'BindGroupLayout'. createBindGroupLayout ::+  MonadIO m =>   -- | The device for which the bind group layout will be created.   Device ->   -- | Description of the bind group layout.   BindGroupLayoutDescriptor ->-  -- | IO action that creates a bind group layout.-  IO BindGroupLayout-createBindGroupLayout device ld = evalContT $ do-  let inst :: Instance-      inst = deviceInst device+  -- | MonadIO action that creates a bind group layout.+  m BindGroupLayout+createBindGroupLayout device ld = liftIO . evalContT $ do+  let inst = deviceInst device    bindGroupLayoutDescriptor_ptr <- rawPtr ld   rawBindGroupLayout <--    liftIO $-      RawFun.wgpuDeviceCreateBindGroupLayout-        (wgpuHsInstance inst)-        (wgpuDevice device)-        bindGroupLayoutDescriptor_ptr+    RawFun.wgpuDeviceCreateBindGroupLayout+      (wgpuHsInstance inst)+      (wgpuDevice device)+      bindGroupLayoutDescriptor_ptr   pure (BindGroupLayout rawBindGroupLayout)  -------------------------------------------------------------------------------@@ -118,7 +123,7 @@   { -- | Debug label of the bind group layout.     bindGroupLabel :: !Text,     -- | Sequence of entries in this bind group layout.-    entries :: Vector BindGroupLayoutEntry+    entries :: !(Vector BindGroupLayoutEntry)   }   deriving (Eq, Show) 
src-internal/WGPU/Internal/ChainedStruct.hs view
@@ -13,6 +13,8 @@ import WGPU.Raw.Generated.Struct.WGPUChainedStruct (WGPUChainedStruct) import qualified WGPU.Raw.Generated.Struct.WGPUChainedStruct as WGPUChainedStruct +-------------------------------------------------------------------------------+ -- | Represents a chained structure. data ChainedStruct a   = -- | Empty chained structure, but with a type tag.
src-internal/WGPU/Internal/Color.hs view
@@ -18,12 +18,14 @@ import WGPU.Raw.Generated.Struct.WGPUColor (WGPUColor) import qualified WGPU.Raw.Generated.Struct.WGPUColor as WGPUColor +-------------------------------------------------------------------------------+ -- | RGBA double-precision color. data Color = Color-  { red :: Double,-    green :: Double,-    blue :: Double,-    alpha :: Double+  { red :: {-# UNPACK #-} !Double,+    green :: {-# UNPACK #-} !Double,+    blue :: {-# UNPACK #-} !Double,+    alpha :: {-# UNPACK #-} !Double   }   deriving (Eq, Show) 
src-internal/WGPU/Internal/CommandEncoder.hs view
@@ -14,14 +14,20 @@   ) where -import Control.Monad.IO.Class (liftIO)-import Control.Monad.Trans.Cont (ContT (ContT), evalContT)+import Control.Monad.IO.Class (MonadIO, liftIO) import Data.Text (Text)-import Foreign (nullPtr, with)+import Foreign (nullPtr) import WGPU.Internal.CommandBuffer (CommandBuffer (CommandBuffer)) import WGPU.Internal.Device (Device, deviceInst, wgpuDevice) import WGPU.Internal.Instance (Instance, wgpuHsInstance)-import WGPU.Internal.Memory (ToRaw, raw, rawPtr, showWithPtr)+import WGPU.Internal.Memory+  ( ToRaw,+    evalContT,+    raw,+    rawPtr,+    showWithPtr,+    withCZeroingAfter,+  ) import qualified WGPU.Raw.Generated.Fun as RawFun import WGPU.Raw.Generated.Struct.WGPUCommandBufferDescriptor (WGPUCommandBufferDescriptor) import qualified WGPU.Raw.Generated.Struct.WGPUCommandBufferDescriptor as WGPUCommandBufferDescriptor@@ -57,46 +63,46 @@  -- | Create an empty command encoder. createCommandEncoder ::+  MonadIO m =>   -- | Device for which to create the command encoder.   Device ->   -- | Debug label for the command encoder.   Text ->   -- | IO action that returns the command encoder.-  IO CommandEncoder-createCommandEncoder device label = evalContT $ do+  m CommandEncoder+createCommandEncoder device label = liftIO . evalContT $ do   let inst = deviceInst device   label_ptr <- rawPtr label   commandEncoderDescriptor_ptr <--    ContT . with $+    withCZeroingAfter $       WGPUCommandEncoderDescriptor.WGPUCommandEncoderDescriptor         { nextInChain = nullPtr,           label = label_ptr         }   commandEncoderRaw <--    liftIO $-      RawFun.wgpuDeviceCreateCommandEncoder-        (wgpuHsInstance inst)-        (wgpuDevice device)-        commandEncoderDescriptor_ptr+    RawFun.wgpuDeviceCreateCommandEncoder+      (wgpuHsInstance inst)+      (wgpuDevice device)+      commandEncoderDescriptor_ptr   pure (CommandEncoder inst commandEncoderRaw)  -- | Finish encoding commands, returning a command buffer. commandEncoderFinish ::+  MonadIO m =>   -- | Command encoder to finish.   CommandEncoder ->   -- | Debugging label for the command buffer.   Text ->   -- | IO action which returns the command buffer.-  IO CommandBuffer-commandEncoderFinish commandEncoder label = evalContT $ do+  m CommandBuffer+commandEncoderFinish commandEncoder label = liftIO . evalContT $ do   let inst = commandEncoderInst commandEncoder-  commandBufferDescriptor_ptr <- rawPtr $ CommandBufferDescriptor label+  commandBufferDescriptor_ptr <- rawPtr (CommandBufferDescriptor label)   commandBufferRaw <--    liftIO $-      RawFun.wgpuCommandEncoderFinish-        (wgpuHsInstance inst)-        (wgpuCommandEncoder commandEncoder)-        commandBufferDescriptor_ptr+    RawFun.wgpuCommandEncoderFinish+      (wgpuHsInstance inst)+      (wgpuCommandEncoder commandEncoder)+      commandBufferDescriptor_ptr   pure (CommandBuffer commandBufferRaw)  -------------------------------------------------------------------------------
src-internal/WGPU/Internal/Device.hs view
@@ -19,18 +19,27 @@   ) where -import Control.Concurrent (MVar, newEmptyMVar, putMVar, takeMVar)-import Control.Monad.IO.Class (liftIO)-import Control.Monad.Trans.Cont (ContT (ContT), evalContT)+import Control.Monad.IO.Class (MonadIO, liftIO) import Data.Default (Default, def) import Data.Text (Text) import qualified Data.Text as Text import Data.Word (Word32)-import Foreign (Ptr, freeHaskellFunPtr, nullPtr, with)+import Foreign (Ptr, nullPtr) import WGPU.Internal.Adapter (Adapter, adapterInst, wgpuAdapter) import WGPU.Internal.ChainedStruct (ChainedStruct (EmptyChain, PtrChain)) import WGPU.Internal.Instance (Instance, wgpuHsInstance)-import WGPU.Internal.Memory (ToRaw, raw, rawPtr, showWithPtr)+import WGPU.Internal.Memory+  ( ToRaw,+    evalContT,+    freeHaskellFunPtr,+    newEmptyMVar,+    putMVar,+    raw,+    rawPtr,+    showWithPtr,+    takeMVar,+    withCZeroingAfter,+  ) import WGPU.Raw.Generated.Enum.WGPUNativeFeature (WGPUNativeFeature) import qualified WGPU.Raw.Generated.Enum.WGPUNativeFeature as WGPUNativeFeature import qualified WGPU.Raw.Generated.Enum.WGPUNativeSType as WGPUSType@@ -178,44 +187,47 @@ -- -- This action blocks until an available device is returned. requestDevice ::+  MonadIO m =>   -- | @Adapter@ for which the device will be returned.   Adapter ->   -- | The features and limits requested for the device.   DeviceDescriptor ->   -- | The returned @Device@, if it could be retrieved.-  IO (Maybe Device)-requestDevice adapter deviceDescriptor = evalContT $ do+  m (Maybe Device)+requestDevice adapter deviceDescriptor = liftIO . evalContT $ do   let inst = adapterInst adapter -  deviceMVar :: MVar WGPUDevice <- liftIO newEmptyMVar--  let deviceCallback :: WGPUDevice -> Ptr () -> IO ()-      deviceCallback device _ = putMVar deviceMVar device-  deviceCallback_c <- liftIO $ mkDeviceCallback deviceCallback+  deviceMVar <- newEmptyMVar+  callback <- mkDeviceCallback (\d _ -> putMVar deviceMVar d)    deviceExtras_ptr <- rawPtr deviceDescriptor   nextInChain_ptr <- rawPtr (PtrChain WGPUSType.DeviceExtras deviceExtras_ptr)   deviceDescriptor_ptr <--    ContT . with $+    withCZeroingAfter $       WGPUDeviceDescriptor.WGPUDeviceDescriptor         { nextInChain = nextInChain_ptr         } -  liftIO $-    RawFun.wgpuAdapterRequestDevice-      (wgpuHsInstance inst)-      (wgpuAdapter adapter)-      deviceDescriptor_ptr-      deviceCallback_c-      nullPtr+  RawFun.wgpuAdapterRequestDevice+    (wgpuHsInstance inst)+    (wgpuAdapter adapter)+    deviceDescriptor_ptr+    callback+    nullPtr -  device <- liftIO $ takeMVar deviceMVar-  liftIO $ freeHaskellFunPtr deviceCallback_c+  device <- takeMVar deviceMVar+  freeHaskellFunPtr callback    pure $ case device of     WGPUDevice ptr | ptr == nullPtr -> Nothing     WGPUDevice _ -> Just (Device inst device) +mkDeviceCallback ::+  (MonadIO m) =>+  (WGPUDevice -> Ptr () -> IO ()) ->+  m WGPURequestDeviceCallback+mkDeviceCallback = liftIO . mkDeviceCallbackIO+ foreign import ccall "wrapper"-  mkDeviceCallback ::+  mkDeviceCallbackIO ::     (WGPUDevice -> Ptr () -> IO ()) -> IO WGPURequestDeviceCallback
+ src-internal/WGPU/Internal/GLFW/Surface.hs view
@@ -0,0 +1,33 @@+-- |+-- Module      : WGPU.Internal.GLFW.Surface+-- Description : GLFW-specific surfaces.+module WGPU.Internal.GLFW.Surface+  ( -- * Functions+    createSurface,+  )+where++import Control.Monad.IO.Class (MonadIO, liftIO)+import qualified Graphics.UI.GLFW as GLFW+import WGPU.Internal.Instance (Instance, wgpuHsInstance)+import WGPU.Internal.Surface (Surface (Surface))+import qualified WGPU.Raw.GLFWSurface++-------------------------------------------------------------------------------++-- | Create a WGPU 'Surface' for a GLFW 'GLFW.Window'.+--+-- This function is not part of the @wgpu-native@ API, but is part of the+-- Haskell API until the native WGPU API has a better story around windowing.+createSurface ::+  MonadIO m =>+  -- | API instance.+  Instance ->+  -- | GLFW window for which the surface will be created.+  GLFW.Window ->+  -- | IO action to create the surface.+  m Surface+createSurface inst window =+  liftIO $+    Surface inst+      <$> WGPU.Raw.GLFWSurface.createSurface (wgpuHsInstance inst) window
src-internal/WGPU/Internal/Instance.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}  -- | -- Module      : WGPU.Internal.Instance@@ -9,19 +10,16 @@ -- Instance of the WGPU API Haskell bindings. module WGPU.Internal.Instance   ( -- * Instance--    ---    -- $instance-    Instance (..),+    Instance,+    wgpuHsInstance,     withPlatformInstance,     withInstance,      -- * Logging-    LogCallback,     LogLevel (..),     setLogLevel,-    logLevelToText,-    logStdout,+    connectLog,+    disconnectLog,      -- * Version     Version (..),@@ -30,40 +28,19 @@   ) where +import Control.Monad.IO.Class (MonadIO) import Data.Bits (shiftR, (.&.)) import Data.Text (Text) import qualified Data.Text as Text-import qualified Data.Text.IO as TextIO import Data.Word (Word32, Word8)-import Foreign (Ptr, freeHaskellFunPtr, nullFunPtr)-import Foreign.C (CChar, peekCString) import qualified System.Info import WGPU.Internal.Memory (ToRaw, raw)-import WGPU.Raw.Dynamic (withWGPU)-import WGPU.Raw.Generated.Enum.WGPULogLevel (WGPULogLevel (WGPULogLevel))+import WGPU.Raw.Dynamic (InstanceHandle, instanceHandleInstance, withWGPU)+import WGPU.Raw.Generated.Enum.WGPULogLevel (WGPULogLevel) import qualified WGPU.Raw.Generated.Enum.WGPULogLevel as WGPULogLevel import WGPU.Raw.Generated.Fun (WGPUHsInstance) import qualified WGPU.Raw.Generated.Fun as RawFun-import WGPU.Raw.Types (WGPULogCallback)---- $instance------ The Haskell bindings to WGPU use a value of type 'Instance' as a handle to--- the rest of the API. An 'Instance' value is obtained by loading a dynamic--- library at runtime, using the 'withInstance' function. A typical invocation--- might look like this:------ @--- 'withInstance' "libwgpu_native.dylib" (Just 'logStdOut') $ \inst -> do---   -- set the logging level for the instance---   'setLogLevel' inst 'Warn'---   -- run the rest of the program ...--- @------ The dynamic library @libwgpu_native.dylib@ is obtained by compiling the--- Rust project <https://github.com/gfx-rs/wgpu-native wgpu-native>. Care--- should be take to compile a version of @libwgpu_native.dylib@ which is--- compatible with the API in these bindings.+import qualified WGPU.Raw.Log as RawLog  ------------------------------------------------------------------------------- @@ -71,8 +48,11 @@ -- -- An instance is loaded from a dynamic library using the 'withInstance' -- function.-newtype Instance = Instance {wgpuHsInstance :: WGPUHsInstance}+newtype Instance = Instance {instanceHandle :: InstanceHandle} +wgpuHsInstance :: Instance -> WGPUHsInstance+wgpuHsInstance = instanceHandleInstance . instanceHandle+ instance Show Instance where show _ = "<Instance>"  instance ToRaw Instance WGPUHsInstance where@@ -87,51 +67,33 @@ -- per-platform name for the library, based on the value returned by -- 'System.Info.os'. withPlatformInstance ::-  -- | Optional logging callback. @'Just' 'logStdout'@ can be supplied here to-  --   print log messages to @stdout@ for debugging purposes.-  Maybe LogCallback ->-  -- | The Program. A function which takes an 'Instance' and returns an IO-  --   action that uses the instance.-  (Instance -> IO a) ->-  -- | IO action which loads the WGPU 'Instance', passes it to the program, and-  --   returns the result of running the program.-  IO a+  MonadIO m =>+  -- | Bracketing function.+  -- This can (for example) be something like 'Control.Exception.Safe.bracket'.+  (m Instance -> (Instance -> m ()) -> r) ->+  -- | Usage or action component of the bracketing function.+  r withPlatformInstance = withInstance platformDylibName  -- | Load the WGPU API from a dynamic library and supply an 'Instance' to a -- program. withInstance ::+  forall m r.+  MonadIO m =>   -- | Name of the @wgpu-native@ dynamic library, or a complete path to it.   FilePath ->-  -- | Optional logging callback. @'Just' 'logStdout'@ can be supplied here to-  --   print log messages to @stdout@ for debugging purposes.-  Maybe LogCallback ->-  -- | The Program. A function which takes an 'Instance' and returns an IO-  --   action that uses the instance.-  (Instance -> IO a) ->-  -- | IO action which loads the WGPU 'Instance', passes it to the program, and-  --   returns the result of running the program.-  IO a-withInstance dylibPath mLog program =-  withWGPU dylibPath $-    \winst -> do-      -- create the logging callback if necessary-      logCallback_c <- case mLog of-        Nothing -> pure nullFunPtr-        Just logFn -> mkLogCallback . toRawLogCallback $ logFn-      RawFun.wgpuSetLogCallback winst logCallback_c--      -- run the program-      result <- program (Instance winst)--      -- free the logging callback-      case mLog of-        Nothing -> pure ()-        Just _ -> do-          RawFun.wgpuSetLogCallback winst nullFunPtr-          freeHaskellFunPtr logCallback_c--      pure result+  -- | Bracketing function.+  -- This can (for example) be something like 'Control.Exception.Safe.bracket'.+  (m Instance -> (Instance -> m ()) -> r) ->+  -- | Usage or action component of the bracketing function.+  r+withInstance dylibPath bkt = withWGPU dylibPath bkt'+  where+    bkt' :: m InstanceHandle -> (InstanceHandle -> m ()) -> r+    bkt' create release =+      bkt+        (Instance <$> create)+        (release . instanceHandle)  -- | Return the dynamic library name for a given platform. --@@ -157,39 +119,11 @@   | Error   deriving (Eq, Show) --- | Logging callback function.-type LogCallback = LogLevel -> Text -> IO ()- -- | Set the current logging level for the instance.-setLogLevel :: Instance -> LogLevel -> IO ()+setLogLevel :: MonadIO m => Instance -> LogLevel -> m () setLogLevel inst lvl =   RawFun.wgpuSetLogLevel (wgpuHsInstance inst) (logLevelToWLogLevel lvl) --- | Create a C callback from a Haskell logging function.-foreign import ccall "wrapper"-  mkLogCallback ::-    (WGPULogLevel -> Ptr CChar -> IO ()) ->-    IO WGPULogCallback---- | Convert a logging callback function to the form required by the Raw API.-toRawLogCallback :: LogCallback -> (WGPULogLevel -> Ptr CChar -> IO ())-toRawLogCallback logFn wLogLevel cMsg = do-  msg <- Text.pack <$> peekCString cMsg-  logFn (wLogLevelToLogLevel wLogLevel) msg---- | Convert a raw API logging level into a 'LogLevel'.------ Any unknown log levels become an 'Error'.-wLogLevelToLogLevel :: WGPULogLevel -> LogLevel-wLogLevelToLogLevel wLvl =-  case wLvl of-    WGPULogLevel.Trace -> Trace-    WGPULogLevel.Debug -> Debug-    WGPULogLevel.Info -> Info-    WGPULogLevel.Warn -> Warn-    WGPULogLevel.Error -> Error-    _ -> Error- -- | Convert a 'LogLevel' value into the type required by the raw API. logLevelToWLogLevel :: LogLevel -> WGPULogLevel logLevelToWLogLevel lvl =@@ -200,22 +134,13 @@     Warn -> WGPULogLevel.Warn     Error -> WGPULogLevel.Error --- | Convert a 'LogLevel' to a text string.-logLevelToText :: LogLevel -> Text-logLevelToText lvl =-  case lvl of-    Trace -> "Trace"-    Debug -> "Debug"-    Info -> "Info"-    Warn -> "Warn"-    Error -> "Error"+-- | Connect a stdout logger to the instance.+connectLog :: MonadIO m => Instance -> m ()+connectLog inst = RawLog.connectLog (wgpuHsInstance inst) --- | A logging function which prints to @stdout@.------ This logging function can be supplied to 'withInstance' to print logging--- messages to @stdout@ for debugging purposes.-logStdout :: LogLevel -> Text -> IO ()-logStdout lvl msg = TextIO.putStrLn $ "[" <> logLevelToText lvl <> "]: " <> msg+-- | Disconnect a stdout logger from the instance.+disconnectLog :: MonadIO m => Instance -> m ()+disconnectLog inst = RawLog.disconnectLog (wgpuHsInstance inst)  ------------------------------------------------------------------------------- @@ -229,7 +154,7 @@   deriving (Eq, Show)  -- | Return the exact version of the WGPU native instance.-getVersion :: Instance -> IO Version+getVersion :: MonadIO m => Instance -> m Version getVersion inst = w32ToVersion <$> RawFun.wgpuGetVersion (wgpuHsInstance inst)   where     w32ToVersion :: Word32 -> Version
src-internal/WGPU/Internal/Memory.hs view
@@ -61,17 +61,32 @@ module WGPU.Internal.Memory   ( -- * Classes     ToRaw (raw),+    FromRaw (fromRaw),     ToRawPtr (rawPtr),+    FromRawPtr (fromRawPtr),      -- * Functions++    -- ** Internal+    evalContT,+    allocaC,     rawArrayPtr,     showWithPtr,+    withCZeroingAfter,++    -- ** Lifted to MonadIO+    newEmptyMVar,+    takeMVar,+    putMVar,+    freeHaskellFunPtr,+    poke,   ) where -import Control.Monad (forM_)-import Control.Monad.IO.Class (liftIO)-import Control.Monad.Trans.Cont (ContT (ContT), evalContT)+import Control.Concurrent (MVar)+import qualified Control.Concurrent (newEmptyMVar, putMVar, takeMVar)+import Control.Monad.Cont (ContT (ContT), callCC, runContT)+import Control.Monad.IO.Class (MonadIO, liftIO) import Data.ByteString (ByteString) import Data.ByteString.Unsafe (unsafeUseAsCString) import Data.Text (Text)@@ -80,17 +95,20 @@ import qualified Data.Vector.Generic as Vector import Data.Word (Word8) import Foreign-  ( Ptr,+  ( FunPtr,+    Ptr,     Storable,     advancePtr,+    alignment,+    alloca,     allocaArray,     castPtr,-    fillBytes,-    poke,+    nullPtr,+    peek,     sizeOf,-    with,   )-import Foreign.C (CBool, CChar, withCString)+import qualified Foreign (fillBytes, freeHaskellFunPtr, poke)+import Foreign.C (CBool, CChar, peekCString, withCString)  ------------------------------------------------------------------------------- -- Type Classes@@ -104,7 +122,7 @@ class ToRaw a b | a -> b where   -- | Convert a value to a raw representation, bracketing any resource   -- management.-  raw :: a -> ContT c IO b+  raw :: a -> ContT r IO b  -- | Represents a value of type @a@ that can be stored as type @(Ptr b)@ in the -- 'ContT' monad.@@ -114,72 +132,138 @@ -- allocated for @('Ptr' b)@ must be allocated before the continuation is -- called, and freed afterward. class ToRawPtr a b where-  rawPtr :: a -> ContT c IO (Ptr b)+  rawPtr :: a -> ContT r IO (Ptr b) +-- | Represents a type @a@ that can be read from a raw value @b@.+class FromRaw b a | a -> b where+  fromRaw :: MonadIO m => b -> m a++-- | Represents a type @a@ that can be read from a raw pointer @b@.+class FromRawPtr b a where+  fromRawPtr :: MonadIO m => Ptr b -> m a+ ------------------------------------------------------------------------------- -- Derived Functionality  -- | Return a pointer to an allocated array, populated with raw values from a -- vector. rawArrayPtr ::-  forall v a b c.+  forall v r a b.   (ToRaw a b, Storable b, Vector v a) =>   -- | Vector of values to store in a C array.   v a ->   -- | Pointer to the array with raw values stored in it.-  ContT c IO (Ptr b)-rawArrayPtr xs =-  ContT $ \action -> do-    let n :: Int-        n = Vector.length xs-    allocaArray n $ \arrayPtr ->-      evalContT $ do-        forM_-          (zip (Vector.toList xs) [0 ..])-          (\(x, i) -> pokeRaw x (advancePtr arrayPtr i))-        liftIO (action arrayPtr)-  where-    pokeRaw :: a -> Ptr b -> ContT c IO ()-    pokeRaw value raw_ptr = raw value >>= liftIO . poke raw_ptr+  ContT r IO (Ptr b)+rawArrayPtr xs = callCC $ \k -> do+  let pokeRaw :: a -> Ptr b -> ContT c IO ()+      pokeRaw value raw_ptr = raw value >>= liftIO . poke raw_ptr +      n :: Int+      n = Vector.length xs+  arrayPtr <- allocaArrayC n+  Vector.iforM_ xs $ \i x -> pokeRaw x (advancePtr arrayPtr i)+  r <- k arrayPtr+  zeroMemory arrayPtr (n * alignment (undefined :: b))+  pure r+{-# INLINEABLE rawArrayPtr #-}+ ------------------------------------------------------------------------------- -- Instances  -- Allow every ToRaw instance to be a ToRawPtr instance. instance {-# OVERLAPPABLE #-} (Storable b, ToRaw a b) => ToRawPtr a b where-  rawPtr x = do-    rawX <- raw x-    ContT $ zeroingWith rawX+  rawPtr x = raw x >>= withCZeroingAfter+  {-# INLINEABLE rawPtr #-} -instance ToRaw Bool CBool where raw x = pure (if x then 1 else 0)+instance {-# OVERLAPPABLE #-} (Storable b, FromRaw b a) => FromRawPtr b a where+  fromRawPtr ptr = (liftIO . peek) ptr >>= fromRaw+  {-# INLINEABLE fromRawPtr #-} -instance ToRawPtr Text CChar where rawPtr = ContT . withCString . Text.unpack+instance ToRaw Bool CBool where+  raw x = pure (if x then 1 else 0)+  {-# INLINE raw #-} +instance ToRawPtr Text CChar where+  rawPtr = withCStringC . Text.unpack+  {-# INLINEABLE rawPtr #-}+ instance ToRawPtr ByteString Word8 where-  rawPtr byteString =-    ContT $ \action -> unsafeUseAsCString byteString (action . castPtr)+  rawPtr = fmap castPtr . unsafeUseAsCStringC+  {-# INLINEABLE rawPtr #-} +instance FromRaw (Ptr CChar) Text where+  fromRaw ptr =+    if ptr == nullPtr+      then pure Text.empty+      else (liftIO . fmap Text.pack . peekCString) ptr+  {-# INLINEABLE fromRaw #-}+ ---------------------------------------------------------------------------------- Utils+-- Continuation helpers --- | Like 'with', but zeroes memory after the action has been performed.------ Allocates memory for a value of type @a@ and fills the memory with the--- 'Foreign.Storable' representation of the @a@ value.-zeroingWith ::-  Storable a =>-  -- | Value to use.-  a ->-  -- | Action to perform with a pointer to the value.-  (Ptr a -> IO b) ->-  -- | Result of running the action.-  IO b-zeroingWith value action =-  with value $ \value_ptr -> do-    result <- action value_ptr-    fillBytes value_ptr 0x00 (sizeOf value)-    pure result+allocaC :: Storable a => ContT r IO (Ptr a)+allocaC = ContT alloca+{-# INLINEABLE allocaC #-} +allocaArrayC :: Storable a => Int -> ContT r IO (Ptr a)+allocaArrayC sz = ContT (allocaArray sz)+{-# INLINEABLE allocaArrayC #-}++withCStringC :: String -> ContT r IO (Ptr CChar)+withCStringC str = ContT (withCString str)+{-# INLINEABLE withCStringC #-}++unsafeUseAsCStringC :: ByteString -> ContT r IO (Ptr CChar)+unsafeUseAsCStringC byteString = ContT (unsafeUseAsCString byteString)+{-# INLINEABLE unsafeUseAsCStringC #-}++withCZeroingAfter :: Storable a => a -> ContT r IO (Ptr a)+withCZeroingAfter x = callCC $ \k -> do+  ptr <- allocaC+  poke ptr x+  r <- k ptr+  zeroMemory ptr (sizeOf x)+  pure r+{-# INLINEABLE withCZeroingAfter #-}++-------------------------------------------------------------------------------+-- Memory actions lifted to MonadIO++newEmptyMVar :: MonadIO m => m (MVar a)+newEmptyMVar = liftIO Control.Concurrent.newEmptyMVar+{-# INLINEABLE newEmptyMVar #-}++takeMVar :: MonadIO m => MVar a -> m a+takeMVar = liftIO . Control.Concurrent.takeMVar+{-# INLINEABLE takeMVar #-}++putMVar :: MonadIO m => MVar a -> a -> m ()+putMVar mvar x = liftIO $ Control.Concurrent.putMVar mvar x+{-# INLINEABLE putMVar #-}++poke :: (MonadIO m, Storable a) => Ptr a -> a -> m ()+poke ptr value = liftIO (Foreign.poke ptr value)+{-# INLINEABLE poke #-}++freeHaskellFunPtr :: MonadIO m => FunPtr a -> m ()+freeHaskellFunPtr = liftIO . Foreign.freeHaskellFunPtr+{-# INLINEABLE freeHaskellFunPtr #-}++fillBytes :: MonadIO m => Ptr a -> Word8 -> Int -> m ()+fillBytes ptr x sz = liftIO (Foreign.fillBytes ptr x sz)+{-# INLINEABLE fillBytes #-}++zeroMemory :: MonadIO m => Ptr a -> Int -> m ()+zeroMemory ptr = fillBytes ptr 0x00+{-# INLINEABLE zeroMemory #-}++-------------------------------------------------------------------------------++evalContT :: Monad m => ContT a m a -> m a+evalContT cont = runContT cont pure++-------------------------------------------------------------------------------+ -- | Formatter for 'Show' instances for opaque pointers. -- -- Displays a name and a corresponding opaque pointer.@@ -191,3 +275,4 @@   -- | Final show string.   String showWithPtr name ptr = "<" <> name <> ":" <> show ptr <> ">"+{-# INLINEABLE showWithPtr #-}
src-internal/WGPU/Internal/Pipeline.hs view
@@ -40,8 +40,7 @@   ) where -import Control.Monad.IO.Class (liftIO)-import Control.Monad.Trans.Cont (evalContT)+import Control.Monad.IO.Class (MonadIO, liftIO) import Data.Bits ((.|.)) import Data.Default (Default, def) import Data.Int (Int32)@@ -53,8 +52,15 @@ import Foreign.C (CFloat (CFloat)) import WGPU.Internal.Binding (BindGroupLayout) import WGPU.Internal.Device (Device, deviceInst, wgpuDevice)-import WGPU.Internal.Instance (Instance, wgpuHsInstance)-import WGPU.Internal.Memory (ToRaw, raw, rawArrayPtr, rawPtr, showWithPtr)+import WGPU.Internal.Instance (wgpuHsInstance)+import WGPU.Internal.Memory+  ( ToRaw,+    evalContT,+    raw,+    rawArrayPtr,+    rawPtr,+    showWithPtr,+  ) import WGPU.Internal.Multipurpose (CompareFunction) import WGPU.Internal.RenderPass (RenderPipeline (RenderPipeline)) import WGPU.Internal.SMaybe (SMaybe (SJust, SNothing))@@ -155,20 +161,20 @@  -- | Create a pipeline layout. createPipelineLayout ::+  MonadIO m =>   -- | The device for which the pipeline layout will be created.   Device ->   -- | Descriptor of the pipeline.   PipelineLayoutDescriptor ->-  IO PipelineLayout-createPipelineLayout device pdl = evalContT $ do+  m PipelineLayout+createPipelineLayout device pdl = liftIO . evalContT $ do   let inst = deviceInst device   pipelineLayoutDescriptor_ptr <- rawPtr pdl   rawPipelineLayout <--    liftIO $-      RawFun.wgpuDeviceCreatePipelineLayout-        (wgpuHsInstance inst)-        (wgpuDevice device)-        pipelineLayoutDescriptor_ptr+    RawFun.wgpuDeviceCreatePipelineLayout+      (wgpuHsInstance inst)+      (wgpuDevice device)+      pipelineLayoutDescriptor_ptr   pure (PipelineLayout rawPipelineLayout)  -------------------------------------------------------------------------------@@ -248,11 +254,11 @@ -- | Vertex inputs (attributes) to shaders. data VertexAttribute = VertexAttribute   { -- | Format of the input.-    vertexFormat :: VertexFormat,+    vertexFormat :: !VertexFormat,     -- | Byte offset of the start of the input.-    offset :: Word64,+    offset :: !Word64,     -- | Location for this input. Must match the location in the shader.-    shaderLocation :: Word32+    shaderLocation :: !Word32   }   deriving (Eq, Show) @@ -879,19 +885,19 @@           fragment = n_fragment         } +-------------------------------------------------------------------------------+ createRenderPipeline ::+  MonadIO m =>   Device ->   RenderPipelineDescriptor ->-  IO RenderPipeline-createRenderPipeline device rpd = evalContT $ do-  let inst :: Instance-      inst = deviceInst device-+  m RenderPipeline+createRenderPipeline device rpd = liftIO . evalContT $ do+  let inst = deviceInst device   renderPipelineDescriptor_ptr <- rawPtr rpd   renderPipelineRaw <--    liftIO $-      RawFun.wgpuDeviceCreateRenderPipeline-        (wgpuHsInstance inst)-        (wgpuDevice device)-        renderPipelineDescriptor_ptr+    RawFun.wgpuDeviceCreateRenderPipeline+      (wgpuHsInstance inst)+      (wgpuDevice device)+      renderPipelineDescriptor_ptr   pure (RenderPipeline renderPipelineRaw)
src-internal/WGPU/Internal/Queue.hs view
@@ -14,14 +14,12 @@   ) where -import Control.Monad.IO.Class (liftIO)-import Control.Monad.Trans.Cont (evalContT)+import Control.Monad.IO.Class (MonadIO, liftIO) import Data.Vector (Vector)-import Data.Word (Word32) import WGPU.Internal.CommandBuffer (CommandBuffer) import WGPU.Internal.Device (Device, deviceInst, wgpuDevice) import WGPU.Internal.Instance (Instance, wgpuHsInstance)-import WGPU.Internal.Memory (ToRaw, raw, rawArrayPtr, showWithPtr)+import WGPU.Internal.Memory (ToRaw, evalContT, raw, rawArrayPtr, showWithPtr) import qualified WGPU.Raw.Generated.Fun as RawFun import WGPU.Raw.Types (WGPUQueue (WGPUQueue)) @@ -49,26 +47,21 @@ -------------------------------------------------------------------------------  -- | Get the queue for a device.-getQueue :: Device -> IO Queue+getQueue :: MonadIO m => Device -> m Queue getQueue device = do-  let queueInst :: Instance-      queueInst = deviceInst device+  let queueInst = deviceInst device   wgpuQueue <-     RawFun.wgpuDeviceGetQueue (wgpuHsInstance queueInst) (wgpuDevice device)   pure Queue {..}  -- | Submit a list of command buffers to a device queue.-queueSubmit :: Queue -> Vector CommandBuffer -> IO ()-queueSubmit queue cbs = evalContT $ do-  let inst :: Instance-      inst = queueInst queue-  let commandCount :: Word32-      commandCount = fromIntegral . length $ cbs-+queueSubmit :: MonadIO m => Queue -> Vector CommandBuffer -> m ()+queueSubmit queue cbs = liftIO . evalContT $ do+  let inst = queueInst queue+  let commandCount = fromIntegral . length $ cbs   commandBuffer_ptr <- rawArrayPtr cbs-  liftIO $-    RawFun.wgpuQueueSubmit-      (wgpuHsInstance inst)-      (wgpuQueue queue)-      commandCount-      commandBuffer_ptr+  RawFun.wgpuQueueSubmit+    (wgpuHsInstance inst)+    (wgpuQueue queue)+    commandCount+    commandBuffer_ptr
src-internal/WGPU/Internal/RenderPass.hs view
@@ -24,17 +24,27 @@   ) where -import Control.Monad.IO.Class (liftIO)-import Control.Monad.Trans.Cont (evalContT)+import Control.Monad.IO.Class (MonadIO, liftIO) import Data.Text (Text) import Data.Vector (Vector) import Data.Word (Word32) import Foreign (nullPtr) import Foreign.C (CBool (CBool), CFloat (CFloat)) import WGPU.Internal.Color (Color, transparentBlack)-import WGPU.Internal.CommandEncoder (CommandEncoder, commandEncoderInst, wgpuCommandEncoder)+import WGPU.Internal.CommandEncoder+  ( CommandEncoder,+    commandEncoderInst,+    wgpuCommandEncoder,+  ) import WGPU.Internal.Instance (Instance, wgpuHsInstance)-import WGPU.Internal.Memory (ToRaw, raw, rawArrayPtr, rawPtr, showWithPtr)+import WGPU.Internal.Memory+  ( ToRaw,+    evalContT,+    raw,+    rawArrayPtr,+    rawPtr,+    showWithPtr,+  ) import WGPU.Internal.SMaybe (SMaybe (SJust, SNothing)) import WGPU.Internal.Texture (TextureView) import qualified WGPU.Raw.Generated.Enum.WGPULoadOp as WGPULoadOp@@ -104,6 +114,8 @@     LoadOpLoad   deriving (Eq, Show) +-------------------------------------------------------------------------------+ -- | Operation to perform to the output attachment at the end of the render -- pass. data StoreOp@@ -120,12 +132,16 @@         StoreOpStore -> WGPUStoreOp.Store         StoreOpClear -> WGPUStoreOp.Clear +-------------------------------------------------------------------------------+ data Operations a = Operations   { load :: !(LoadOp a),-    store :: StoreOp+    store :: !StoreOp   }   deriving (Eq, Show) +-------------------------------------------------------------------------------+ -- | Describes a color attachment to a render pass. data RenderPassColorAttachment = RenderPassColorAttachment   { -- | The view to use as an attachment.@@ -162,6 +178,8 @@           clearColor = n_clearColor         } +-------------------------------------------------------------------------------+ -- | Describes a depth/stencil attachment to a render pass. data RenderPassDepthStencilAttachment = RenderPassDepthStencilAttachment   { -- | The view to use as an attachment.@@ -228,6 +246,8 @@           stencilReadOnly = n_stencilReadOnly         } +-------------------------------------------------------------------------------+ -- | Describes the attachments of a render pass. data RenderPassDescriptor = RenderPassDescriptor   { -- | Debugging label for the render pass.@@ -257,6 +277,8 @@           occlusionQuerySet = WGPUQuerySet nullPtr         } +-------------------------------------------------------------------------------+ -- | Half open range. It includes the 'start' value but not the 'end' value. data Range a = Range   { rangeStart :: !a,@@ -264,41 +286,40 @@   }   deriving (Eq, Show) +-------------------------------------------------------------------------------+ -- | Begins recording of a render pass. beginRenderPass ::+  MonadIO m =>   -- | @CommandEncoder@ to contain the render pass.   CommandEncoder ->   -- | Description of the render pass.   RenderPassDescriptor ->   -- | IO action which returns the render pass encoder.-  IO RenderPassEncoder-beginRenderPass commandEncoder rpd = evalContT $ do-  let inst :: Instance-      inst = commandEncoderInst commandEncoder-+  m RenderPassEncoder+beginRenderPass commandEncoder rpd = liftIO . evalContT $ do+  let inst = commandEncoderInst commandEncoder   renderPassDescriptor_ptr <- rawPtr rpd   renderPassEncoderRaw <--    liftIO $-      RawFun.wgpuCommandEncoderBeginRenderPass-        (wgpuHsInstance inst)-        (wgpuCommandEncoder commandEncoder)-        renderPassDescriptor_ptr+    RawFun.wgpuCommandEncoderBeginRenderPass+      (wgpuHsInstance inst)+      (wgpuCommandEncoder commandEncoder)+      renderPassDescriptor_ptr   pure (RenderPassEncoder inst renderPassEncoderRaw)  -- | Sets the active render pipeline. -- -- Subsequent draw calls will exhibit the behaviour defined by the pipeline. renderPassSetPipeline ::+  MonadIO m =>   -- | Render pass encoder on which to act.   RenderPassEncoder ->   -- | Render pipeline to set active.   RenderPipeline ->   -- | IO action which sets the active render pipeline.-  IO ()+  m () renderPassSetPipeline renderPassEncoder renderPipeline = do-  let inst :: Instance-      inst = renderPassEncoderInst renderPassEncoder-+  let inst = renderPassEncoderInst renderPassEncoder   RawFun.wgpuRenderPassEncoderSetPipeline     (wgpuHsInstance inst)     (wgpuRenderPassEncoder renderPassEncoder)@@ -306,6 +327,7 @@  -- | Draws primitives from the active vertex buffers. renderPassDraw ::+  MonadIO m =>   -- | Render pass encoder on which to act.   RenderPassEncoder ->   -- | Range of vertices to draw.@@ -313,11 +335,9 @@   -- | Range of instances to draw.   Range Word32 ->   -- | IO action which stores the draw command.-  IO ()+  m () renderPassDraw renderPassEncoder vertices instances = do-  let inst :: Instance-      inst = renderPassEncoderInst renderPassEncoder-+  let inst = renderPassEncoderInst renderPassEncoder   RawFun.wgpuRenderPassEncoderDraw     (wgpuHsInstance inst)     (wgpuRenderPassEncoder renderPassEncoder)@@ -328,14 +348,13 @@  -- | Finish recording of a render pass. endRenderPass ::+  MonadIO m =>   -- | Render pass encoder on which to finish recording.   RenderPassEncoder ->   -- | IO action that finishes recording.-  IO ()+  m () endRenderPass renderPassEncoder = do-  let inst :: Instance-      inst = renderPassEncoderInst renderPassEncoder-+  let inst = renderPassEncoderInst renderPassEncoder   RawFun.wgpuRenderPassEncoderEndPass     (wgpuHsInstance inst)     (wgpuRenderPassEncoder renderPassEncoder)
+ src-internal/WGPU/Internal/SDL/Surface.hs view
@@ -0,0 +1,23 @@+-- |+-- Module      : WGPU.Internal.SDL.Surface+-- Description : SDL-specific surfaces.+module WGPU.Internal.SDL.Surface+  ( -- * Functions+    createSurface,+  )+where++import Control.Monad.IO.Class (MonadIO)+import qualified SDL (Window)+import WGPU.Internal.Instance (Instance, wgpuHsInstance)+import WGPU.Internal.Surface (Surface (Surface))+import qualified WGPU.Raw.SDLSurface (createSurface)++createSurface ::+  MonadIO m =>+  Instance ->+  SDL.Window ->+  m Surface+createSurface inst window =+  Surface inst+    <$> WGPU.Raw.SDLSurface.createSurface (wgpuHsInstance inst) window
src-internal/WGPU/Internal/Shader.hs view
@@ -21,8 +21,7 @@   ) where -import Control.Monad.IO.Class (liftIO)-import Control.Monad.Trans.Cont (evalContT)+import Control.Monad.IO.Class (MonadIO, liftIO) import Data.ByteString (ByteString) import qualified Data.ByteString as ByteString import Data.String (IsString)@@ -32,8 +31,15 @@ import Foreign.C (CChar) import WGPU.Internal.ChainedStruct (ChainedStruct (EmptyChain, PtrChain)) import WGPU.Internal.Device (Device, deviceInst, wgpuDevice)-import WGPU.Internal.Instance (Instance, wgpuHsInstance)-import WGPU.Internal.Memory (ToRaw, ToRawPtr, raw, rawPtr, showWithPtr)+import WGPU.Internal.Instance (wgpuHsInstance)+import WGPU.Internal.Memory+  ( ToRaw,+    ToRawPtr,+    evalContT,+    raw,+    rawPtr,+    showWithPtr,+  ) import qualified WGPU.Raw.Generated.Enum.WGPUSType as WGPUSType import qualified WGPU.Raw.Generated.Fun as RawFun import WGPU.Raw.Generated.Struct.WGPUShaderModuleDescriptor (WGPUShaderModuleDescriptor)@@ -67,26 +73,26 @@  -- | Create a shader module from either SPIR-V or WGSL source code. createShaderModule ::+  MonadIO m =>   -- | Device for the shader.   Device ->   -- | Descriptor of the shader module.   ShaderModuleDescriptor ->   -- | IO action producing the shader module.-  IO ShaderModule-createShaderModule device smd = evalContT $ do-  let inst :: Instance-      inst = deviceInst device+  m ShaderModule+createShaderModule device smd = liftIO . evalContT $ do+  let inst = deviceInst device   shaderModuleDescriptor_ptr <- rawPtr smd   rawShaderModule <--    liftIO $-      RawFun.wgpuDeviceCreateShaderModule-        (wgpuHsInstance inst)-        (wgpuDevice device)-        shaderModuleDescriptor_ptr+    RawFun.wgpuDeviceCreateShaderModule+      (wgpuHsInstance inst)+      (wgpuDevice device)+      shaderModuleDescriptor_ptr   pure (ShaderModule rawShaderModule)  -- | Create a shader module from SPIR-V source code. createShaderModuleSPIRV ::+  MonadIO m =>   -- | Device for which the shader should be created.   Device ->   -- | Debugging label for the shader.@@ -94,7 +100,7 @@   -- | Shader source code (SPIR-V bytestring).   SPIRV ->   -- | IO action creating the shader module.-  IO ShaderModule+  m ShaderModule createShaderModuleSPIRV device label spirv =   createShaderModule device smd   where@@ -107,6 +113,7 @@  -- | Create a shader module from WGSL source code. createShaderModuleWGSL ::+  MonadIO m =>   -- | Device for which the shader should be created.   Device ->   -- | Debugging label for the shader.@@ -114,7 +121,7 @@   -- | Shader source code (WGSL source string).   WGSL ->   -- | IO action creating the shader module.-  IO ShaderModule+  m ShaderModule createShaderModuleWGSL device label wgsl =   createShaderModule device smd   where
src-internal/WGPU/Internal/Surface.hs view
@@ -3,21 +3,14 @@ -- | -- Module      : WGPU.Internal.Surface -- Description : Platform-specific surfaces.------ Device-specific surfaces. module WGPU.Internal.Surface   ( -- * Types     Surface (..),--    -- * Functions-    createGLFWSurface,   ) where -import qualified Graphics.UI.GLFW as GLFW-import WGPU.Internal.Instance (Instance, wgpuHsInstance)+import WGPU.Internal.Instance (Instance) import WGPU.Internal.Memory (ToRaw, raw, showWithPtr)-import qualified WGPU.Raw.GLFWSurface import WGPU.Raw.Types (WGPUSurface (WGPUSurface))  -------------------------------------------------------------------------------@@ -45,20 +38,3 @@  instance ToRaw Surface WGPUSurface where   raw = pure . wgpuSurface------------------------------------------------------------------------------------- | Create a WGPU 'Surface' for a GLFW 'GLFW.Window'.------ This function is not part of the @wgpu-native@ API, but is part of the--- Haskell API until the native WGPU API has a better story around windowing.-createGLFWSurface ::-  -- | API instance.-  Instance ->-  -- | GLFW window for which the surface will be created.-  GLFW.Window ->-  -- | IO action to create the surface.-  IO Surface-createGLFWSurface inst window = do-  Surface inst-    <$> WGPU.Raw.GLFWSurface.createSurface (wgpuHsInstance inst) window
src-internal/WGPU/Internal/SwapChain.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-}+{-# OPTIONS_GHC -Wno-deferred-type-errors #-}  -- | -- Module      : WGPU.Internal.SwapChain@@ -19,16 +20,24 @@   ) where -import Control.Concurrent (MVar, newEmptyMVar, putMVar, takeMVar)-import Control.Monad.IO.Class (liftIO)-import Control.Monad.Trans.Cont (evalContT)+import Control.Monad.IO.Class (MonadIO, liftIO) import Data.Text (Text) import Data.Word (Word32)-import Foreign (Ptr, freeHaskellFunPtr, nullPtr)+import Foreign (Ptr, nullPtr) import WGPU.Internal.Adapter (Adapter, wgpuAdapter) import WGPU.Internal.Device (Device, deviceInst, wgpuDevice) import WGPU.Internal.Instance (Instance, wgpuHsInstance)-import WGPU.Internal.Memory (ToRaw, raw, rawPtr, showWithPtr)+import WGPU.Internal.Memory+  ( ToRaw,+    evalContT,+    freeHaskellFunPtr,+    newEmptyMVar,+    putMVar,+    raw,+    rawPtr,+    showWithPtr,+    takeMVar,+  ) import WGPU.Internal.Surface (Surface, surfaceInst, wgpuSurface) import WGPU.Internal.Texture (TextureFormat, TextureUsage, TextureView (TextureView), textureFormatFromRaw) import WGPU.Raw.Generated.Enum.WGPUPresentMode (WGPUPresentMode)@@ -137,37 +146,40 @@ -- | Returns an optimal texture format to use for the swapchain with this --   adapter and surface. getSwapChainPreferredFormat ::+  MonadIO m =>   -- | @Surface@ for which to obtain an optimal texture format.   Surface ->   -- | @Adapter@ for which to obtain an optimal texture format.   Adapter ->   -- | IO action which returns the optimal texture format.-  IO TextureFormat+  m TextureFormat getSwapChainPreferredFormat surface adapter = do-  let inst :: Instance-      inst = surfaceInst surface--  textureFormatMVar :: MVar WGPUTextureFormat <- newEmptyMVar--  let textureFormatCallback :: WGPUTextureFormat -> Ptr () -> IO ()-      textureFormatCallback tf _ = putMVar textureFormatMVar tf+  let inst = surfaceInst surface -  textureFormatCallback_c <--    mkSurfaceGetPreferredFormatCallback textureFormatCallback+  textureFormatMVar <- newEmptyMVar+  callback <-+    mkSurfaceGetPreferredFormatCallback (\tf _ -> putMVar textureFormatMVar tf)    RawFun.wgpuSurfaceGetPreferredFormat     (wgpuHsInstance inst)     (wgpuSurface surface)     (wgpuAdapter adapter)-    textureFormatCallback_c+    callback     nullPtr    textureFormat <- textureFormatFromRaw <$> takeMVar textureFormatMVar-  freeHaskellFunPtr textureFormatCallback_c+  freeHaskellFunPtr callback   pure textureFormat +mkSurfaceGetPreferredFormatCallback ::+  MonadIO m =>+  (WGPUTextureFormat -> Ptr () -> IO ()) ->+  m WGPUSurfaceGetPreferredFormatCallback+mkSurfaceGetPreferredFormatCallback =+  liftIO . mkSurfaceGetPreferredFormatCallbackIO+ foreign import ccall "wrapper"-  mkSurfaceGetPreferredFormatCallback ::+  mkSurfaceGetPreferredFormatCallbackIO ::     (WGPUTextureFormat -> Ptr () -> IO ()) ->     IO WGPUSurfaceGetPreferredFormatCallback @@ -178,6 +190,7 @@ -- To determine the preferred 'TextureFormat' for the 'Surface', use the -- 'getSwapChainPreferredFormat' function. createSwapChain ::+  MonadIO m =>   -- | @Device@ for which the @SwapChain@ will be created.   Device ->   -- | @Surface@ for which the @SwapChain@ will be created.@@ -185,32 +198,29 @@   -- | Description of the @SwapChain@ to be created.   SwapChainDescriptor ->   -- | IO action which creates the swap chain.-  IO SwapChain-createSwapChain device surface scd = evalContT $ do-  let inst :: Instance-      inst = deviceInst device-+  m SwapChain+createSwapChain device surface scd = liftIO . evalContT $ do+  let inst = deviceInst device   swapChainDescriptor_ptr <- rawPtr scd   rawSwapChain <--    liftIO $-      RawFun.wgpuDeviceCreateSwapChain-        (wgpuHsInstance inst)-        (wgpuDevice device)-        (wgpuSurface surface)-        swapChainDescriptor_ptr+    RawFun.wgpuDeviceCreateSwapChain+      (wgpuHsInstance inst)+      (wgpuDevice device)+      (wgpuSurface surface)+      swapChainDescriptor_ptr   pure (SwapChain inst rawSwapChain)  -------------------------------------------------------------------------------  -- | Get the 'TextureView' for the current swap chain frame. getSwapChainCurrentTextureView ::+  MonadIO m =>   -- | Swap chain from which to fetch the current texture view.   SwapChain ->   -- | IO action which returns the current swap chain texture view.-  IO TextureView+  m TextureView getSwapChainCurrentTextureView swapChain = do-  let inst :: Instance-      inst = swapChainInst swapChain+  let inst = swapChainInst swapChain   TextureView     <$> RawFun.wgpuSwapChainGetCurrentTextureView       (wgpuHsInstance inst)@@ -220,11 +230,11 @@  -- | Present the latest swap chain image. swapChainPresent ::+  MonadIO m =>   -- | Swap chain to present.   SwapChain ->   -- | IO action which presents the swap chain image.-  IO ()+  m () swapChainPresent swapChain = do-  let inst :: Instance-      inst = swapChainInst swapChain+  let inst = swapChainInst swapChain   RawFun.wgpuSwapChainPresent (wgpuHsInstance inst) (wgpuSwapChain swapChain)
src/WGPU.hs view
@@ -22,12 +22,16 @@     -- * Surface #surface#     -- $surface     Surface,-    createGLFWSurface,      -- * Adapter #adapter#     -- $adapter     Adapter,+    AdapterType (..),+    BackendType (..),+    AdapterProperties (..),     requestAdapter,+    getAdapterProperties,+    adapterPropertiesToText,      -- * Device #device#     -- $device@@ -149,10 +153,9 @@      -- * Logging     LogLevel (..),-    LogCallback,+    connectLog,+    disconnectLog,     setLogLevel,-    logStdout,-    logLevelToText,      -- * Multipurpose     CompareFunction (..),@@ -161,6 +164,7 @@      -- ** Strict Maybe     SMaybe (..),+    fromSMaybe,   ) where @@ -193,14 +197,11 @@ -- there are also early attempts to create a native binding (ie. a C language -- binding). Two implementations of the native binding are: -----   * <https://github.com/gfx-rs/wgpu-native wgpu-native>: a Mozilla-backed---     WebGPU native implementation, written in Rust, used in the Firefox web---     browser. This is the binding to which this Haskell library is currently---     tied.+--   * <https://github.com/gfx-rs/wgpu-native wgpu-native>: a Rust+--     implementation used in the Firefox web browser. -----   * <https://dawn.googlesource.com/dawn dawn>: a Google-backed WebGPU native---     implementation, written in C++, used in the Chrome web browser. In the---     future, we hope to support this backend too.+--   * <https://dawn.googlesource.com/dawn dawn>: a C++ implementation,used in+--     the Chrome web browser. -- -- The native bindings to WebGPU have the potential to become a portable, -- next-generation GPU API which is easy to use. Vulkan is also currently@@ -214,11 +215,11 @@ -- -- Currently, macOS (Metal), Windows and Linux are supported. ----- === Dependence on GLFW-b+-- === Windowing System Support ----- This package currently uses only--- <https://hackage.haskell.org/package/GLFW-b GLFW-b>--- for windowing and event processing.+-- The bindings support both GLFW-b and SDL as windowing systems on macOS,+-- Windows and Linux. The windowing system bindings are somewhat hacky (due to+-- the early stage of WebGPU Native), but they work. -- -- === Structure of Bindings --@@ -262,11 +263,13 @@ -- <https://github.com/gfx-rs/wgpu-native wgpu-native> is supported. -- -- To load the dynamic library and obtain an instance, use the--- 'withPlatformInstance' or 'withInstance' bracketing functions:+-- 'withPlatformInstance' or 'withInstance' bracketing functions. These+-- functions take a function that performs bracketing. -- -- @--- 'withPlatformInstance' (Just 'logStdout') $ \inst -> do---   -- set the logging level (optional)+-- 'withPlatformInstance' 'Control.Exception.Safe.bracket' $ \inst -> do+--   -- attach the logger and set the logging level (optional)+--   'connectLog' inst --   'setLogLevel' inst 'Warn' --   -- run the rest of the program... -- @@@ -279,9 +282,8 @@ -- $surface -- -- A 'Surface' is a handle to a platform-specific presentable surface, like a--- window. Currently, only GLFW windows are supported for surface creation.--- Once you have a GLFW window, you may create a 'Surface' for it using the--- 'createGLFWSurface' function.+-- window. First, create either a GLFW or SDL window, and then create a surface+-- using either 'createGLFWSurface' or 'createSDLSurface'. -- -- Once you have a 'Surface', the next step is usually to -- <WGPU.html#g:adapter request an adapter> that is compatible with it.
+ src/WGPU/BoneYard/SimpleSDL.hs view
@@ -0,0 +1,407 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}++-- |+-- Module      : WGPU.BoneYard.SimpleSDL+-- Description : Template for a simple SDL app.+--+-- This is a kind of skeleton for a very simple SDL app. It is intended for+-- bootstrapping development. A common use case is when you want a window to+-- draw in with everything configured. This provides a version of that+-- functionality that can later be replaced or refined (easily) by the app+-- developer if necessary.+module WGPU.BoneYard.SimpleSDL+  ( -- * Swap Chain++    -- ** Types+    SwapChainState,++    -- ** Functions+    emptySwapChainState,+    withSwapChain,++    -- * Render Pipelines++    -- ** Types+    RenderPipelineName,+    RenderPipelines,++    -- ** Functions+    emptyRenderPipelines,+    createRenderPipeline,+    getRenderPipeline,++    -- * Shaders++    -- ** Types+    ShaderName,+    Shaders,++    -- ** Functions+    emptyShaders,+    compileWGSL,+    compileWGSL_,+    getShader,++    -- * Resources++    -- ** Types+    Params (..),+    Resources (..),++    -- ** Functions+    loadResources,++    -- * Exceptions+    AppException (..),+  )+where++import Control.Concurrent (MVar, modifyMVar, modifyMVar_, newMVar, withMVar)+import Control.Exception.Safe (Exception, MonadThrow, throwM)+import Control.Lens (lens)+import Control.Monad.IO.Class (MonadIO, liftIO)+import Control.Monad.Reader (MonadReader, ReaderT, ask, asks, runReaderT)+import Control.Monad.Trans.Resource (MonadResource, allocate)+import Data.Default (def)+import Data.Has (Has, getter, hasLens)+import Data.Map.Strict (Map)+import qualified Data.Map.Strict as Map+import Data.String (IsString)+import Data.Text (Text)+import Data.Word (Word32)+import GHC.Generics (Generic)+import SDL (Window)+import qualified SDL+import WGPU+  ( Adapter,+    Device,+    DeviceDescriptor,+    Instance,+    Queue,+    RenderPipeline,+    RenderPipelineDescriptor,+    SMaybe,+    ShaderModule,+    Surface,+    SwapChain,+    TextureFormat,+    WGSL,+  )+import qualified WGPU+import qualified WGPU.Classy as C+import qualified WGPU.SDL.Surface++-------------------------------------------------------------------------------+-- SwapChain Management++-- | Contains mutable state to manage the swap chain.+newtype SwapChainState = SwapChainState+  {unSwapChainState :: MVar (Maybe SwapChainDetails)}++data SwapChainDetails = SwapChainDetails+  { scdSize :: !(Word32, Word32),+    scdSwapChain :: !SwapChain+  }++-- | Initialize a new 'SwapChainState'.+emptySwapChainState :: MonadResource m => m SwapChainState+emptySwapChainState = SwapChainState <$> liftIO (newMVar Nothing)++-- | Provide a 'ReaderT' with a properly-configured 'SwapChain'.+withSwapChain ::+  forall r m a.+  ( C.HasDevice r m,+    C.HasSurface r m,+    C.HasAdapter r m,+    Has Window r,+    Has SwapChainState r+  ) =>+  ReaderT (SwapChain, r) m a ->+  m a+withSwapChain action = do+  env <- ask+  swapChain <- getSwapChain+  runReaderT action (swapChain, env)+  where+    windowSize :: m (Word32, Word32)+    windowSize = do+      SDL.V2 w h <- asks getter >>= SDL.glGetDrawableSize+      pure (fromIntegral w, fromIntegral h)++    getSwapChain :: m SwapChain+    getSwapChain = do+      device <- asks getter+      surface <- asks getter+      windowSz <- windowSize+      textureFormat <- C.getSwapChainPreferredFormat+      mVarMaybe <- asks getter+      liftIO $+        modifyMVar (unSwapChainState mVarMaybe) $ \case+          Nothing -> do+            newSwapChain device surface windowSz textureFormat+          Just scd@SwapChainDetails {..} -> do+            if scdSize == windowSz+              then pure (Just scd, scdSwapChain)+              else newSwapChain device surface windowSz textureFormat++    newSwapChain ::+      Device ->+      Surface ->+      (Word32, Word32) ->+      TextureFormat ->+      IO (Maybe SwapChainDetails, SwapChain)+    newSwapChain device surface (w, h) textureFormat = do+      swapChain <-+        WGPU.createSwapChain+          device+          surface+          WGPU.SwapChainDescriptor+            { swapChainLabel = "SwapChain",+              usage = WGPU.TextureUsageRenderAttachment,+              swapChainFormat = textureFormat,+              width = w,+              height = h,+              presentMode = WGPU.PresentModeFifo+            }+      pure (Just (SwapChainDetails (w, h) swapChain), swapChain)++-------------------------------------------------------------------------------+-- Render Pipeline Collection++-- | Name of a render pipeline.+newtype RenderPipelineName = RenderPipelineName {unRenderPipelineName :: Text}+  deriving (Eq, Ord, IsString, Show)++-- | Container for mutable state that contains a map of render pipelines.+newtype RenderPipelines = RenderPipelines+  {unRenderPipelines :: MVarMap RenderPipelineName RenderPipeline}++-- | Create an empty 'RenderPipelines'.+emptyRenderPipelines :: MonadResource m => m RenderPipelines+emptyRenderPipelines = RenderPipelines <$> emptyMVarMap++-- | Create a 'RenderPipeline', storing it in the 'RenderPipelines' map.+--+-- A 'RenderPipeline' created this way can be fetched using+-- 'getRenderPipeline'. This calls 'C.createRenderPipeline' under the hood.+createRenderPipeline ::+  ( MonadIO m,+    C.HasDevice r m,+    Has RenderPipelines r+  ) =>+  -- | Name of the render pipeline.+  RenderPipelineName ->+  -- | Descriptor of the render pipeline.+  RenderPipelineDescriptor ->+  -- | The created render pipeline.+  m RenderPipeline+createRenderPipeline name renderPipelineDescriptor = do+  renderPipeline <- C.createRenderPipeline renderPipelineDescriptor+  asks getter >>= insertMVarMap name renderPipeline . unRenderPipelines+  pure renderPipeline++-- | Fetch a render pipeline that was previously created using+-- 'createRenderPipeline'.+--+-- If the render pipeline is not available, this function throws an exception+-- of type 'AppException'.+getRenderPipeline ::+  (Has RenderPipelines r, MonadReader r m, MonadIO m, MonadThrow m) =>+  -- | Name of the render pipeline to fetch.+  RenderPipelineName ->+  -- | The render pipeline.+  m RenderPipeline+getRenderPipeline name = do+  mRenderPipeline <- asks getter >>= lookupMVarMap name . unRenderPipelines+  case mRenderPipeline of+    Just renderPipeline -> pure renderPipeline+    Nothing -> throwM (UnknownRenderPipelineName name)++-------------------------------------------------------------------------------+-- Shader Collection++-- | Name of a shader.+newtype ShaderName = ShaderName {unShaderName :: Text}+  deriving (Eq, Ord, IsString, Show)++-- | Container for mutable state that contains a map of shaders.+newtype Shaders = Shaders {unShaders :: MVarMap ShaderName ShaderModule}++-- | Create an empty 'Shaders'.+emptyShaders :: MonadResource m => m Shaders+emptyShaders = Shaders <$> emptyMVarMap++-- | Compile a WGSL shader, adding it to the 'Shaders' map.+compileWGSL_ ::+  (Has Device r, Has Shaders r, MonadReader r m, MonadResource m) =>+  -- | Name of the shader.+  ShaderName ->+  -- | Shader source code.+  WGSL ->+  -- | Action that compiles the shader and adds it to the 'Shaders' map.+  m ()+compileWGSL_ shaderName wgsl = compileWGSL shaderName wgsl >> pure ()++-- | Compile a WGSL shader, adding it to the 'Shaders' map, and returning the+-- compiled 'ShaderModule'.+compileWGSL ::+  (Has Device r, Has Shaders r, MonadReader r m, MonadResource m) =>+  -- | Name of the shader.+  ShaderName ->+  -- | Shader source code.+  WGSL ->+  -- | Action that returns the compiled shader module, after adding it to the+  -- 'Shaders' map.+  m ShaderModule+compileWGSL shaderName wgsl = do+  shaderModule <- C.createShaderModuleWGSL (unShaderName shaderName) wgsl+  asks getter >>= insertMVarMap shaderName shaderModule . unShaders+  pure shaderModule++-- | Fetch a shader that was previously compiled.+--+-- If the shader is not available, this function throws an exception of type+-- 'AppException'.+getShader ::+  (Has Shaders r, MonadReader r m, MonadIO m, MonadThrow m) =>+  -- | Name of the shader to fetch.+  ShaderName ->+  -- | The shader module.+  m ShaderModule+getShader shaderName = do+  mShaderModule <- asks getter >>= lookupMVarMap shaderName . unShaders+  case mShaderModule of+    Just shaderModule -> pure shaderModule+    Nothing -> throwM (UnknownShaderName shaderName)++-------------------------------------------------------------------------------+-- Application Static Resources++-- | Parameters for initialization.+data Params = Params+  { -- | Title of the window.+    title :: !Text,+    -- | Optional device descriptor.+    mDeviceDescriptor :: !(SMaybe DeviceDescriptor)+  }++-- | Load the resources for an application.+--+-- This creates:+--   - 'Instance',+--   - SDL 'Window' (which is shown)+--   - 'Surface' for the SDL window+--   - 'Adapter'+--   - 'Device'+--   - 'Queue'+loadResources ::+  forall m.+  (MonadResource m, MonadThrow m) =>+  -- | Initialization parameters.+  Params ->+  -- | Created application resources.+  m Resources+loadResources Params {..} = do+  inst <- createInstance+  window <- createWindow+  surface <- WGPU.SDL.Surface.createSurface inst window+  adapter <- requestAdapter surface+  device <- requestDevice adapter+  queue <- WGPU.getQueue device+  pure Resources {..}+  where+    createInstance :: m Instance+    createInstance = snd <$> WGPU.withPlatformInstance allocate++    createWindow :: m Window+    createWindow = do+      SDL.initializeAll+      let windowConfig = SDL.defaultWindow {SDL.windowResizable = True}+      snd+        <$> allocate+          (SDL.createWindow title windowConfig)+          SDL.destroyWindow++    requestAdapter :: Surface -> m Adapter+    requestAdapter surface =+      WGPU.requestAdapter surface >>= \case+        Nothing -> throwM AdapterRequestFailed+        Just adapter -> pure adapter++    requestDevice :: Adapter -> m Device+    requestDevice adapter = do+      let deviceDescriptor = WGPU.fromSMaybe def mDeviceDescriptor+      WGPU.requestDevice adapter deviceDescriptor >>= \case+        Nothing -> throwM DeviceRequestFailed+        Just device -> pure device++-- | Resources for the app.+data Resources = Resources+  { inst :: !Instance,+    window :: !Window,+    surface :: !Surface,+    adapter :: !Adapter,+    device :: !Device,+    queue :: !Queue+  }+  deriving (Generic)++instance Has Instance Resources where+  hasLens = lens inst (\s x -> s {inst = x})++instance Has Window Resources where+  hasLens = lens window (\s x -> s {window = x})++instance Has Surface Resources where+  hasLens = lens surface (\s x -> s {surface = x})++instance Has Adapter Resources where+  hasLens = lens adapter (\s x -> s {adapter = x})++instance Has Device Resources where+  hasLens = lens device (\s x -> s {device = x})++instance Has Queue Resources where+  hasLens = lens queue (\s x -> s {queue = x})++-------------------------------------------------------------------------------+-- Map inside an MVar++newtype MVarMap k v = MVarMap {unMVarMap :: MVar (Map k v)}++emptyMVarMap :: MonadIO m => m (MVarMap k v)+emptyMVarMap = MVarMap <$> liftIO (newMVar Map.empty)++insertMVarMap :: (Ord k, MonadIO m) => k -> v -> MVarMap k v -> m ()+insertMVarMap key value mVarMap =+  liftIO $ modifyMVar_ (unMVarMap mVarMap) (pure . Map.insert key value)++lookupMVarMap :: (Ord k, MonadIO m) => k -> MVarMap k v -> m (Maybe v)+lookupMVarMap key mVarMap =+  liftIO $ withMVar (unMVarMap mVarMap) (pure . Map.lookup key)++-------------------------------------------------------------------------------+-- Exceptions++-- | Exceptions from SimpleSDL.+data AppException+  = -- | Requesting an adapter failed.+    AdapterRequestFailed+  | -- | Requesting a device failed.+    DeviceRequestFailed+  | -- | Requesting a shader failed.+    UnknownShaderName ShaderName+  | -- | Requesting a render pipeline failed.+    UnknownRenderPipelineName RenderPipelineName+  deriving (Show)++instance Exception AppException
+ src/WGPU/Classy.hs view
@@ -0,0 +1,362 @@+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE ScopedTypeVariables #-}++-- |+-- Module      : WGPU.Classy+-- Description : Get parameters from readers.+--+-- WGPU commands commonly take parameters such as the 'Instance', 'Device', etc,+-- which are relatively unchanged across multiple commands. This module provides+-- a way to supply those parameters from a 'MonadReader'. Useful for the truly+-- lazy among us.+module WGPU.Classy+  ( -- * Constraints+    HasInstance,+    HasSurface,+    HasAdapter,+    HasDevice,+    HasSwapChain,+    HasCommandEncoder,+    HasRenderPassEncoder,+    HasQueue,++    -- * Classes++    -- * Lifted Functions++    -- ** Adapter+    requestAdapter,+    getAdapterProperties,++    -- ** Device+    requestDevice,++    -- ** Swapchain+    getSwapChainPreferredFormat,+    createSwapChain,+    getSwapChainCurrentTextureView,+    swapChainPresent,++    -- ** Resource Binding+    createBindGroupLayout,++    -- ** Shader Modules+    createShaderModule,+    createShaderModuleSPIRV,+    createShaderModuleWGSL,++    -- ** Pipelines++    -- *** Render+    createPipelineLayout,+    createRenderPipeline,++    -- ** Command Encoding+    createCommandEncoder,+    commandEncoderFinish,+    beginRenderPass,+    renderPassSetPipeline,+    renderPassDraw,+    endRenderPass,++    -- ** Queue+    getQueue,+    queueSubmit,+    queueSubmit',++    -- ** Version+    getVersion,++    -- ** Logging+    connectLog,+    disconnectLog,+    setLogLevel,++    -- * Reader Contexts+    addEnv,++    -- * Building++    -- ** Command Encoding+    buildCommandBuffer,+    buildRenderPass,+  )+where++import Control.Monad.IO.Class (MonadIO)+import Control.Monad.Reader (MonadReader, ReaderT, ask, asks, runReaderT)+import Data.Has (Has, getter)+import Data.Text (Text)+import Data.Vector (Vector)+import Data.Word (Word32)+import WGPU+  ( Adapter,+    AdapterProperties,+    BindGroupLayout,+    BindGroupLayoutDescriptor,+    CommandBuffer,+    CommandEncoder,+    Device,+    DeviceDescriptor,+    Instance,+    LogLevel,+    PipelineLayout,+    PipelineLayoutDescriptor,+    Queue,+    Range,+    RenderPassDescriptor,+    RenderPassEncoder,+    RenderPipeline,+    RenderPipelineDescriptor,+    SPIRV,+    ShaderModule,+    ShaderModuleDescriptor,+    Surface,+    SwapChain,+    SwapChainDescriptor,+    TextureFormat,+    TextureView,+    Version,+    WGSL,+  )+import qualified WGPU++-------------------------------------------------------------------------------+-- Constraint Synonyms++type RIO r m = (MonadIO m, MonadReader r m)++type HasInstance r m = (RIO r m, Has Instance r)++type HasSurface r m = (RIO r m, Has Surface r)++type HasAdapter r m = (RIO r m, Has Adapter r)++type HasDevice r m = (RIO r m, Has Device r)++type HasSwapChain r m = (RIO r m, Has SwapChain r)++type HasCommandEncoder r m = (RIO r m, Has CommandEncoder r)++type HasRenderPassEncoder r m = (RIO r m, Has RenderPassEncoder r)++type HasQueue r m = (RIO r m, Has Queue r)++access :: (Has q r, MonadReader r m) => (q -> m a) -> m a+access action = asks getter >>= action+{-# INLINEABLE access #-}++access2 :: (Has q r, MonadReader r m) => (q -> b -> m a) -> b -> m a+access2 action y = asks getter >>= \x -> action x y+{-# INLINEABLE access2 #-}++access3 :: (Has q r, MonadReader r m) => (q -> b -> c -> m a) -> b -> c -> m a+access3 action y z = asks getter >>= \x -> action x y z+{-# INLINEABLE access3 #-}++-------------------------------------------------------------------------------+-- Adapter++requestAdapter :: HasSurface r m => m (Maybe Adapter)+requestAdapter = access WGPU.requestAdapter+{-# INLINEABLE requestAdapter #-}++getAdapterProperties :: HasAdapter r m => m AdapterProperties+getAdapterProperties = access WGPU.getAdapterProperties+{-# INLINEABLE getAdapterProperties #-}++-------------------------------------------------------------------------------+-- Device++requestDevice :: HasAdapter r m => DeviceDescriptor -> m (Maybe Device)+requestDevice = access2 WGPU.requestDevice+{-# INLINEABLE requestDevice #-}++-------------------------------------------------------------------------------+-- Swapchain++getSwapChainPreferredFormat ::+  (HasSurface r m, HasAdapter r m) =>+  m TextureFormat+getSwapChainPreferredFormat =+  access . access2 $ WGPU.getSwapChainPreferredFormat+{-# INLINEABLE getSwapChainPreferredFormat #-}++createSwapChain ::+  (HasDevice r m, HasSurface r m) =>+  SwapChainDescriptor ->+  m SwapChain+createSwapChain = access2 . access3 $ WGPU.createSwapChain+{-# INLINEABLE createSwapChain #-}++getSwapChainCurrentTextureView :: HasSwapChain r m => m TextureView+getSwapChainCurrentTextureView = access WGPU.getSwapChainCurrentTextureView+{-# INLINEABLE getSwapChainCurrentTextureView #-}++swapChainPresent :: HasSwapChain r m => m ()+swapChainPresent = access WGPU.swapChainPresent+{-# INLINEABLE swapChainPresent #-}++-------------------------------------------------------------------------------+-- Resource Binding++createBindGroupLayout ::+  HasDevice r m =>+  BindGroupLayoutDescriptor ->+  m BindGroupLayout+createBindGroupLayout = access2 WGPU.createBindGroupLayout+{-# INLINEABLE createBindGroupLayout #-}++-------------------------------------------------------------------------------+-- Shader Modules++createShaderModule :: HasDevice r m => ShaderModuleDescriptor -> m ShaderModule+createShaderModule = access2 WGPU.createShaderModule+{-# INLINEABLE createShaderModule #-}++createShaderModuleSPIRV :: HasDevice r m => Text -> SPIRV -> m ShaderModule+createShaderModuleSPIRV = access3 WGPU.createShaderModuleSPIRV+{-# INLINEABLE createShaderModuleSPIRV #-}++createShaderModuleWGSL :: HasDevice r m => Text -> WGSL -> m ShaderModule+createShaderModuleWGSL = access3 WGPU.createShaderModuleWGSL+{-# INLINEABLE createShaderModuleWGSL #-}++-------------------------------------------------------------------------------+-- Render Pipelines++createPipelineLayout ::+  HasDevice r m =>+  PipelineLayoutDescriptor ->+  m PipelineLayout+createPipelineLayout = access2 WGPU.createPipelineLayout+{-# INLINEABLE createPipelineLayout #-}++createRenderPipeline ::+  HasDevice r m =>+  RenderPipelineDescriptor ->+  m RenderPipeline+createRenderPipeline = access2 WGPU.createRenderPipeline+{-# INLINEABLE createRenderPipeline #-}++-------------------------------------------------------------------------------+-- Command Encoding (Lifted)++createCommandEncoder :: HasDevice r m => Text -> m CommandEncoder+createCommandEncoder = access2 WGPU.createCommandEncoder+{-# INLINEABLE createCommandEncoder #-}++commandEncoderFinish :: HasCommandEncoder r m => Text -> m CommandBuffer+commandEncoderFinish = access2 WGPU.commandEncoderFinish+{-# INLINEABLE commandEncoderFinish #-}++beginRenderPass ::+  HasCommandEncoder r m =>+  RenderPassDescriptor ->+  m RenderPassEncoder+beginRenderPass = access2 WGPU.beginRenderPass+{-# INLINEABLE beginRenderPass #-}++renderPassSetPipeline :: HasRenderPassEncoder r m => RenderPipeline -> m ()+renderPassSetPipeline = access2 WGPU.renderPassSetPipeline+{-# INLINEABLE renderPassSetPipeline #-}++renderPassDraw ::+  HasRenderPassEncoder r m =>+  Range Word32 ->+  Range Word32 ->+  m ()+renderPassDraw = access3 WGPU.renderPassDraw+{-# INLINEABLE renderPassDraw #-}++endRenderPass :: HasRenderPassEncoder r m => m ()+endRenderPass = access WGPU.endRenderPass+{-# INLINEABLE endRenderPass #-}++-------------------------------------------------------------------------------+-- Queue++getQueue :: HasDevice r m => m Queue+getQueue = access WGPU.getQueue+{-# INLINEABLE getQueue #-}++queueSubmit :: HasQueue r m => Vector CommandBuffer -> m ()+queueSubmit = access2 WGPU.queueSubmit+{-# INLINEABLE queueSubmit #-}++-- | Fetch the queue from a device and submit command buffers to it.+queueSubmit' :: HasDevice r m => Vector CommandBuffer -> m ()+queueSubmit' = buildQueue . queueSubmit+{-# INLINEABLE queueSubmit' #-}++-------------------------------------------------------------------------------+-- Version++getVersion :: HasInstance r m => m Version+getVersion = access WGPU.getVersion++-------------------------------------------------------------------------------+-- Logging++connectLog :: HasInstance r m => m ()+connectLog = access WGPU.connectLog+{-# INLINEABLE connectLog #-}++disconnectLog :: HasInstance r m => m ()+disconnectLog = access WGPU.disconnectLog+{-# INLINEABLE disconnectLog #-}++setLogLevel :: HasInstance r m => LogLevel -> m ()+setLogLevel = access2 WGPU.setLogLevel+{-# INLINEABLE setLogLevel #-}++-------------------------------------------------------------------------------+-- Reader Contexts++-- | Add 'q' into the reader environment.+addEnv :: MonadReader r m => q -> ReaderT (q, r) m a -> m a+addEnv x action = ask >>= \env -> runReaderT action (x, env)+{-# INLINEABLE addEnv #-}++-------------------------------------------------------------------------------+-- Building++-- | Build a 'CommandBuffer' by running actions in an environment that has+-- access to a 'CommandEncoder'.+buildCommandBuffer ::+  forall r m.+  HasDevice r m =>+  -- | Debugging label for the command encoder.+  Text ->+  -- | Debugging label for the command buffer.+  Text ->+  -- | Action to configure the 'CommandEncoder'.+  ReaderT (CommandEncoder, r) m () ->+  -- | Completed 'CommandBuffer'.+  m CommandBuffer+buildCommandBuffer commandEncoderLabel commandBufferLabel build = do+  commandEncoder <- createCommandEncoder commandEncoderLabel+  addEnv commandEncoder (build >> commandEncoderFinish commandBufferLabel)+{-# INLINEABLE buildCommandBuffer #-}++-- | Build a render pass by running actions in an environment that has access+-- to a 'RenderPassEncoder'.+buildRenderPass ::+  forall r m.+  HasCommandEncoder r m =>+  RenderPassDescriptor ->+  ReaderT (RenderPassEncoder, r) m () ->+  m ()+buildRenderPass renderPassDescriptor build = do+  renderPassEncoder <- beginRenderPass renderPassDescriptor+  addEnv renderPassEncoder (build >> endRenderPass)+{-# INLINEABLE buildRenderPass #-}++-- | Build a queue by running actions in an environment that has access to a+-- `Queue`.+buildQueue :: HasDevice r m => ReaderT (Queue, r) m () -> m ()+buildQueue action = getQueue >>= \queue -> addEnv queue action+{-# INLINEABLE buildQueue #-}
+ src/WGPU/GLFW/Surface.hs view
@@ -0,0 +1,10 @@+-- |+-- Module      : WGPU.GLFW.Surface+-- Description : GLFW-specific surfaces.+module WGPU.GLFW.Surface+  ( -- * Functions+    createSurface,+  )+where++import WGPU.Internal.GLFW.Surface (createSurface)
+ src/WGPU/SDL/Surface.hs view
@@ -0,0 +1,10 @@+-- |+-- Module      : WGPU.SDL.Surface+-- Description : SDL-specific surfaces.+module WGPU.SDL.Surface+  ( -- * Functions+    createSurface,+  )+where++import WGPU.Internal.SDL.Surface (createSurface)
wgpu-hs.cabal view
@@ -1,6 +1,6 @@ cabal-version:      3.0 name:               wgpu-hs-version:            0.2.0.1+version:            0.3.0.0 synopsis:           WGPU description:        A high-level binding to WGPU. bug-reports:        https://github.com/lancelet/wgpu-hs/issues@@ -12,11 +12,21 @@ category:           Graphics extra-source-files: CHANGELOG.md +flag glfw+  description: Enable GLFW integration+  default:     True+  manual:      True+ flag examples   description: Build the examples   default:     True   manual:      True +flag sdl2+  description: Enable SDL2 integration+  default:     True+  manual:      True+ common base   default-language: Haskell2010   build-depends:    base ^>=4.14.0.0@@ -50,12 +60,22 @@     WGPU.Internal.SwapChain     WGPU.Internal.Texture +  if flag(glfw)+    build-depends:   GLFW-b+    exposed-modules: WGPU.Internal.GLFW.Surface++  if flag(sdl2)+    build-depends:   sdl2+    exposed-modules: WGPU.Internal.SDL.Surface+   build-depends:     , bytestring     , data-default-    , GLFW-b+    , data-has+    , lens+    , mtl+    , resourcet     , text-    , transformers     , vector     , wgpu-raw-hs @@ -64,21 +84,60 @@   hs-source-dirs:  src   build-depends:     , bytestring        ^>=0.10.12+    , containers        ^>=0.6.5.1     , data-default      ^>=0.7.1.1-    , GLFW-b            ^>=3.3.0+    , data-has          ^>=0.4.0.0+    , lens              ^>=5.0.1+    , mtl               ^>=2.2.2+    , resourcet         ^>=1.2.4.3+    , safe-exceptions   ^>=0.1.7.2     , text              ^>=1.2.4-    , transformers      ^>=0.5.6     , vector            ^>=0.12.3     , wgpu-hs-internal-    , wgpu-raw-hs       ==0.2.0.1+    , wgpu-raw-hs       ==0.3.0.0 -  exposed-modules: WGPU+  exposed-modules:+    WGPU+    WGPU.Classy -executable triangle+  if flag(glfw)+    build-depends:   GLFW-b ^>=3.3.0+    exposed-modules: WGPU.GLFW.Surface++  if flag(sdl2)+    build-depends:   sdl2 ^>=2.5.3.0+    exposed-modules:+      WGPU.BoneYard.SimpleSDL+      WGPU.SDL.Surface++executable triangle-sdl   import:         base, ghc-options -  if flag(examples)+  if (flag(examples) && flag(sdl2))     build-depends:+      , data-default     ^>=0.7.1.1+      , data-has         ^>=0.4.0.0+      , lens             ^>=5.0.1+      , mtl              ^>=2.2.2+      , resourcet        ^>=1.2.4.3+      , safe-exceptions  ^>=0.1.7.2+      , sdl2             ^>=2.5.3.0+      , string-qq        ^>=0.0.4+      , text             ^>=1.2.4+      , vector           ^>=0.12.3+      , wgpu-hs++  else+    buildable: False++  hs-source-dirs: examples/triangle-sdl+  main-is:        TriangleSDL.hs++executable triangle-glfw+  import:         base, ghc-options++  if (flag(examples) && flag(glfw))+    build-depends:       , data-default  ^>=0.7.1.1       , GLFW-b        ^>=3.3.0       , text          ^>=1.2.4@@ -88,5 +147,5 @@   else     buildable: False -  hs-source-dirs: examples/triangle-  main-is:        Main.hs+  hs-source-dirs: examples/triangle-glfw+  main-is:        TriangleGLFW.hs