diff --git a/GlitchyEditor/src/EditorLayer.bf b/GlitchyEditor/src/EditorLayer.bf index 01d6518..ad41746 100644 --- a/GlitchyEditor/src/EditorLayer.bf +++ b/GlitchyEditor/src/EditorLayer.bf @@ -118,7 +118,7 @@ namespace GlitchyEditor [AllowAppend] public this(String[] args, EditorContentManager contentManager) : base("Editor") { - Application.Get().Window.IsVSync = true; + Application.Get().MainWindow.IsVSync = true; ScriptEngine.ApplicationInfo.IsEditor = true; @@ -306,7 +306,7 @@ namespace GlitchyEditor private void InitGraphics() { - _context = Application.Get().Window.Context..AddRef(); + _context = Application.Instance.ImmediateContext..AddRef(); RasterizerStateDescription rsDesc = .(.Solid, .Back, true); _rasterizerState = new RasterizerState(rsDesc); @@ -471,7 +471,7 @@ namespace GlitchyEditor RenderCommand.SetRenderTarget(null, 0, true); RenderCommand.BindRenderTargets(); - RenderCommand.SetViewport(_context.SwapChain.BackbufferViewport); + RenderCommand.SetViewport(Application.Instance.MainWindow.SwapChain.BackbufferViewport); } private void DebugDraw3D() @@ -583,7 +583,7 @@ namespace GlitchyEditor title.AppendF($" | {_editorScene.Name}"); } - Application.Instance.Window.Title = title; + Application.Instance.MainWindow.Title = title; } #region Project Management diff --git a/GlitchyEngine/src/Application.bf b/GlitchyEngine/src/Application.bf index c3b4439..eade313 100644 --- a/GlitchyEngine/src/Application.bf +++ b/GlitchyEngine/src/Application.bf @@ -14,7 +14,7 @@ namespace GlitchyEngine { static Application s_Instance = null; - private Window _window; + private Window _mainWindow; private RendererAPI _rendererApi; private bool _running = true; @@ -25,6 +25,10 @@ namespace GlitchyEngine #if IMGUI private ImGuiLayer _imGuiLayer; #endif + + private GraphicsContext _immediateGraphicsContext ~ _?.ReleaseRef(); + + public GraphicsContext ImmediateContext => _immediateGraphicsContext; private GameTime _gameTime; @@ -34,7 +38,7 @@ namespace GlitchyEngine private append Monitor _jobQueueMutex = .(); public bool IsRunning => _running; - public Window Window => _window; + public Window MainWindow => _mainWindow; public IContentManager ContentManager => _contentManager; @@ -61,9 +65,17 @@ namespace GlitchyEngine WindowDescription windowDesc = .Default; windowDesc.Icon = "Resources/Textures/GlitchyEngineIcon.ico"; + + if (GraphicsContext.Get() == null) + { + _immediateGraphicsContext = new GraphicsContext(null, true); + _immediateGraphicsContext.Init(); + } - _window = new Window(windowDesc); - _window.EventCallback = new => OnEvent; + _mainWindow = new Window(windowDesc); + _mainWindow.EventCallback = new => OnEvent; + + _immediateGraphicsContext.CurrentWindow = _mainWindow; Input.Init(); @@ -71,7 +83,7 @@ namespace GlitchyEngine // TODO: RenderAPI in RenderCommand initialisieren? _rendererApi = new RendererAPI(); - _rendererApi.Context = _window.Context; + _rendererApi.Context = _immediateGraphicsContext; SamplerStateManager.Init(); @@ -110,7 +122,7 @@ namespace GlitchyEngine delete _layerStack; delete _rendererApi; - delete _window; + delete _mainWindow; delete _gameTime; @@ -178,7 +190,7 @@ namespace GlitchyEngine layer.Update(_gameTime); } - _window.Update(); + _mainWindow.Update(); if(allowFrame) { @@ -186,7 +198,7 @@ namespace GlitchyEngine _imGuiLayer.ImGuiRender(); #endif - _window.Context.SwapChain.Present(); + _mainWindow.SwapChain.Present(); } } } diff --git a/GlitchyEngine/src/ImGui/ImGuiLayer.bf b/GlitchyEngine/src/ImGui/ImGuiLayer.bf index c736ee8..51f3b4b 100644 --- a/GlitchyEngine/src/ImGui/ImGuiLayer.bf +++ b/GlitchyEngine/src/ImGui/ImGuiLayer.bf @@ -54,7 +54,7 @@ namespace GlitchyEngine.ImGui } #if BF_PLATFORM_WINDOWS - ImGuiImplWin32.Init(Application.Get().Window.NativeWindow); + ImGuiImplWin32.Init(Application.Instance.MainWindow.NativeWindow); #endif #if GE_GRAPHICS_DX11 diff --git a/GlitchyEngine/src/OrthographicCameraController.bf b/GlitchyEngine/src/OrthographicCameraController.bf index 82a9cef..a927427 100644 --- a/GlitchyEngine/src/OrthographicCameraController.bf +++ b/GlitchyEngine/src/OrthographicCameraController.bf @@ -38,8 +38,9 @@ namespace GlitchyEngine public void Update(GameTime gameTime) { Debug.Profiler.ProfileFunction!(); - - if(Application.Get().Window.IsActive) + + // TODO: Don't check the main window instead check the editor view window + if(Application.Instance.MainWindow.IsActive) { float3 movement = .(); diff --git a/GlitchyEngine/src/PerspectiveCameraController.bf b/GlitchyEngine/src/PerspectiveCameraController.bf index 70c1e16..1715287 100644 --- a/GlitchyEngine/src/PerspectiveCameraController.bf +++ b/GlitchyEngine/src/PerspectiveCameraController.bf @@ -98,7 +98,8 @@ namespace GlitchyEngine { Debug.Profiler.ProfileFunction!(); - if(Application.Get().Window.IsActive) + // TODO: Don't check the main window instead check the editor view window + if(Application.Instance.MainWindow.IsActive) { float3 movement = .(); diff --git a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11GraphicsContext.bf b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11GraphicsContext.bf index 0775049..362de91 100644 --- a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11GraphicsContext.bf +++ b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11GraphicsContext.bf @@ -19,10 +19,6 @@ namespace GlitchyEngine.Renderer /// DirectX 11 specific implementation of the GraphicsContext extension GraphicsContext { - private SwapChain _swapChain; - - internal Windows.HWnd nativeWindowHandle; - /// Gets whether or not the DX11 device is initialized. //internal bool IsDx11Initialized => nativeDevice != null; //internal ID3D11Device* nativeDevice => NativeDevice; @@ -30,7 +26,7 @@ namespace GlitchyEngine.Renderer //internal ID3D11Debug* debugDevice => DebugDevice; - public override SwapChain SwapChain => _swapChain; + //public override SwapChain SwapChain => _mainWindow.SwapChain; private const uint32 MaxRTVCount = DirectX.D3D11.D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT; @@ -41,23 +37,10 @@ namespace GlitchyEngine.Renderer //public static override uint32 MaxRenderTargetCount() => MaxRTVCount; - public this(Windows.HWnd windowHandle) - { - Debug.Profiler.ProfileFunction!(); - - nativeWindowHandle = windowHandle; - - _swapChain = new SwapChain(this); - - s_GraphicsContext = this; - } - public ~this() { Debug.Profiler.ProfileFunction!(); - delete _swapChain; - Dx11Release(); } @@ -65,57 +48,12 @@ namespace GlitchyEngine.Renderer { Debug.Profiler.ProfileFunction!(); - Dx11Init(); - - SwapChain.Init(); - } - - /** - * Initializes the Device and ImmediateContext. - */ - /* - void InitDevice() - { - if(!IsDx11Initialized) + if (_immediateContext) { - Log.EngineLogger.Trace("Creating D3D11 Device and Context..."); - - DeviceCreationFlags deviceFlags = .None; -#if DEBUG - deviceFlags |= .Debug; -#endif - - FeatureLevel[] levels = scope .(.Level_11_0); - - FeatureLevel deviceLevel = ?; - var deviceResult = D3D11.CreateDevice(null, .Hardware, 0, deviceFlags, levels, &nativeDevice, &deviceLevel, &nativeContext); - Log.EngineLogger.Assert(deviceResult.Succeeded, scope $"Failed to create D3D11 Device. Message({(int32)deviceResult}): {deviceResult}"); - -#if DEBUG - if(nativeDevice.QueryInterface(out debugDevice).Succeeded) - { - ID3D11InfoQueue* infoQueue; - if(nativeDevice.QueryInterface(out infoQueue).Succeeded) - { - infoQueue.SetBreakOnSeverity(.Corruption, true); - infoQueue.SetBreakOnSeverity(.Error, true); - - infoQueue.Release(); - } - } -#endif - - Log.EngineLogger.Trace($"D3D11 Device and Context created (Feature level: {deviceLevel})"); - } - else - { - // We created a second GraphicsContext (for some reason?) just increment references. - nativeDevice.AddRef(); - nativeContext.AddRef(); - debugDevice.AddRef(); + s_GraphicsContext = this; + Dx11Init(); } } - */ private ID3D11DepthStencilView* _depthStencilTarget; private ID3D11RenderTargetView*[MaxRTVCount] _renderTargets; @@ -132,11 +70,11 @@ namespace GlitchyEngine.Renderer public override void SetRenderTarget(RenderTarget2D renderTarget, int slot, bool setDepthTarget) { - _renderTargets[slot] = (renderTarget ?? _swapChain.BackBuffer)._nativeRenderTargetView; + _renderTargets[slot] = (renderTarget ?? _currentWindow.SwapChain.BackBuffer)._nativeRenderTargetView; if(slot == 0 && setDepthTarget) { - SetDepthStencilTarget((renderTarget ?? _swapChain.BackBuffer).DepthStencilTarget); + SetDepthStencilTarget((renderTarget ?? _currentWindow.SwapChain.BackBuffer).DepthStencilTarget); } } @@ -168,7 +106,7 @@ namespace GlitchyEngine.Renderer { using (ContextMonitor.Enter()) { - NativeContext.ClearRenderTargetView((renderTarget ?? _swapChain.BackBuffer)._nativeRenderTargetView, color); + NativeContext.ClearRenderTargetView((renderTarget ?? _currentWindow.SwapChain.BackBuffer)._nativeRenderTargetView, color); } } diff --git a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11RenderTarget.bf b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11RenderTarget.bf index ef2be58..d42b57a 100644 --- a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11RenderTarget.bf +++ b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11RenderTarget.bf @@ -47,14 +47,12 @@ namespace GlitchyEngine.Renderer ReleaseAndNullify(); - if(_description.IsSwapchainTarget) + if(_description.SwapChain != null) { // TODO: if the engine supports multiple windows it has to support multiple swap chains. - // kinda dirty... - var context = GraphicsContext.Get(); - - context.SwapChain.GetBackbuffer(out _nativeTexture); + // Still dirty, we need to be able to pass in which swap chain to use! + _description.SwapChain.GetBackbuffer(out _nativeTexture); CreateViews(); } @@ -99,8 +97,9 @@ namespace GlitchyEngine.Renderer private void CreateViews() { Debug.Profiler.ProfileResourceFunction!(); - - if(_description.IsSwapchainTarget) + + // TODO: Why did we branch here? + /*if(_description.Swapchain != null) { var result = NativeDevice.CreateShaderResourceView(_nativeTexture, null, &_nativeResourceView); Log.EngineLogger.Assert(result.Succeeded, "Failed to create resource view"); @@ -111,7 +110,7 @@ namespace GlitchyEngine.Renderer result = NativeDevice.CreateRenderTargetView(_nativeTexture, null, &_nativeRenderTargetView); Log.EngineLogger.Assert(result.Succeeded, "Failed to create render target view"); } - else + else*/ { var result = NativeDevice.CreateShaderResourceView(_nativeTexture, null, &_nativeResourceView); Log.EngineLogger.Assert(result.Succeeded, "Failed to create resource view"); @@ -372,11 +371,9 @@ namespace GlitchyEngine.Renderer if (target.IsSwapchainTarget) { // TODO: if the engine supports multiple windows it has to support multiple swap chains. - - // kinda dirty... - var context = GraphicsContext.Get(); - - context.SwapChain.GetBackbuffer(out _nativeTextures[i]); + + // Still dirty, we need to be able to pass in which swap chain to use! + Application.Instance.MainWindow.SwapChain.GetBackbuffer(out _nativeTextures[i]); } else { diff --git a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11RendererAPI.bf b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11RendererAPI.bf index 98b1893..feac8fb 100644 --- a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11RendererAPI.bf +++ b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11RendererAPI.bf @@ -39,7 +39,7 @@ namespace GlitchyEngine.Renderer private mixin RtOrBackbuffer(RenderTarget2D renderTarget) { - renderTarget ?? _context.SwapChain.BackBuffer + renderTarget ?? _context.CurrentWindow.SwapChain.BackBuffer } public override void Clear(RenderTarget2D renderTarget, ColorRGBA color) diff --git a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11SwapChain.bf b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11SwapChain.bf index 110d624..975051b 100644 --- a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11SwapChain.bf +++ b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11SwapChain.bf @@ -10,6 +10,8 @@ using internal GlitchyEngine.Platform.DX11; namespace GlitchyEngine.Renderer { + using internal GlitchyEngine; + public extension SwapChain { internal DirectX.DXGI.IDXGIDevice* nativeDxgiDevice; @@ -34,7 +36,7 @@ namespace GlitchyEngine.Renderer void SetResolutionFromWindow() { - DirectX.Windows.Winuser.GetClientRect(_context.nativeWindowHandle, let rect); + DirectX.Windows.Winuser.GetClientRect(_window._windowHandle, let rect); Width = (.)(rect.Right - rect.Left); Height = (.)(rect.Bottom - rect.Top); @@ -68,7 +70,7 @@ namespace GlitchyEngine.Renderer uint32 backBufferCount = 2; Format backBufferFormat = .R8G8B8A8_UNorm; - Format backBufferViewFormat = .R8G8B8A8_UNorm; // _SRGB + // TODO: Allow this: Format backBufferViewFormat = .R8G8B8A8_UNorm_SRGB; if(nativeSwapChain != null) { @@ -101,7 +103,7 @@ namespace GlitchyEngine.Renderer SwapChainFullscreenDescription fsSwapChainDesc = .(); fsSwapChainDesc.Windowed = true; - var createResult = factory.CreateSwapChainForHwnd((.)NativeDevice, _context.nativeWindowHandle, ref swDesc, &fsSwapChainDesc, null, &nativeSwapChain); + var createResult = factory.CreateSwapChainForHwnd((.)NativeDevice, _window._windowHandle, ref swDesc, &fsSwapChainDesc, null, &nativeSwapChain); if(createResult.Failed) { Log.EngineLogger.Error($"Failed to create swap chain. Message({(int)createResult}):{createResult}"); @@ -112,7 +114,7 @@ namespace GlitchyEngine.Renderer RenderTarget2DDescription desc = .(backBufferFormat, _width, _height); desc.DepthStencilFormat = _depthStencilFormat; - desc.IsSwapchainTarget = true; + desc.SwapChain = this; // Todo: perhaps just update the render target _backBuffer = new RenderTarget2D(desc); @@ -132,7 +134,7 @@ namespace GlitchyEngine.Renderer { Debug.Profiler.ProfileFunction!(); - nativeSwapChain.Present(Application.Get().Window.IsVSync ? 1 : 0, .None); + nativeSwapChain.Present(_window.IsVSync ? 1 : 0, .None); } } } diff --git a/GlitchyEngine/src/Platform/Windows/WindowsInput.bf b/GlitchyEngine/src/Platform/Windows/WindowsInput.bf index fd05c55..8a76c04 100644 --- a/GlitchyEngine/src/Platform/Windows/WindowsInput.bf +++ b/GlitchyEngine/src/Platform/Windows/WindowsInput.bf @@ -194,7 +194,7 @@ namespace GlitchyEngine public override static void SetMousePosition(int2 pos) { - HWnd windowHandle = (HWnd)(int)Application.Get().Window.NativeWindow; + HWnd windowHandle = (HWnd)(int)Application.Get().MainWindow.NativeWindow; var pos; @@ -213,7 +213,7 @@ namespace GlitchyEngine public override static void Impl_NewFrame() { - Window window = Application.Get().Window; + Window window = Application.Get().MainWindow; Debug.Profiler.ProfileFunction!(); @@ -260,7 +260,7 @@ namespace GlitchyEngine public override static void Impl_EndFrame() { - Application.Get().Window.[Friend]_rawMouseMovementAccumulator = .Zero; + Application.Get().MainWindow.[Friend]_rawMouseMovementAccumulator = .Zero; } } } diff --git a/GlitchyEngine/src/Platform/Windows/WindowsWindow.bf b/GlitchyEngine/src/Platform/Windows/WindowsWindow.bf index cbe2e3a..83a1b7d 100644 --- a/GlitchyEngine/src/Platform/Windows/WindowsWindow.bf +++ b/GlitchyEngine/src/Platform/Windows/WindowsWindow.bf @@ -23,7 +23,7 @@ namespace GlitchyEngine const String WindowClassName = "GlitchyEngineWindow"; private Windows.HInstance _instanceHandle; - private Windows.HWnd _windowHandle; + internal Windows.HWnd _windowHandle; private WindowClassExW _windowClass; @@ -37,10 +37,6 @@ namespace GlitchyEngine private bool _isActive; - private GraphicsContext _graphicsContext ~ _?.ReleaseRef(); - - public override GraphicsContext Context => _graphicsContext; - public override int32 MinWidth { get => _minMaxInfo.MinimumTrackingSize.x; @@ -230,9 +226,9 @@ namespace GlitchyEngine _windowHandle = CreateWindowExW(.None, _windowClass.ClassName, desc.Title.ToScopedNativeWChar!(), .WS_OVERLAPPEDWINDOW | .WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, desc.Width, desc.Height, 0, 0, (.)_instanceHandle, null); } - - _graphicsContext = new GraphicsContext(_windowHandle); - _graphicsContext.Init(); + + _swapChain = new SwapChain(this); + _swapChain.Init(); void* myPtr = Internal.UnsafeCastToPtr(this); SetWindowLongPtrW(_windowHandle, GWL_USERDATA, (int)myPtr); @@ -300,10 +296,10 @@ namespace GlitchyEngine if(window._clientRect.Width != 0 && window._clientRect.Height != 0) { - window._graphicsContext.SwapChain.Width = (.)window._clientRect.Width; - window._graphicsContext.SwapChain.Height = (.)window._clientRect.Height; + window._swapChain.Width = (.)window._clientRect.Width; + window._swapChain.Height = (.)window._clientRect.Height; - window._graphicsContext.SwapChain.ApplyChanges(); + window._swapChain.ApplyChanges(); } WindowResizeEvent event = scope WindowResizeEvent(window._clientRect.Width, window._clientRect.Height, window._isResizingOrMoving); diff --git a/GlitchyEngine/src/Renderer/GraphicsContext.bf b/GlitchyEngine/src/Renderer/GraphicsContext.bf index 3ba694f..8650662 100644 --- a/GlitchyEngine/src/Renderer/GraphicsContext.bf +++ b/GlitchyEngine/src/Renderer/GraphicsContext.bf @@ -8,11 +8,25 @@ namespace GlitchyEngine.Renderer { private static GraphicsContext s_GraphicsContext; public static GraphicsContext Get() => s_GraphicsContext; + + private Window _currentWindow; - public extern SwapChain SwapChain {get;} + private bool _immediateContext; + + public Window CurrentWindow + { + get => _currentWindow; + set => _currentWindow = value; + } protected extern void PlatformConstruct(); + public this(Window currentWindow, bool immediateContext = false) + { + _currentWindow = currentWindow; + _immediateContext = immediateContext; + } + /** * The maximum number of simultaneous rendertargets supported. */ diff --git a/GlitchyEngine/src/Renderer/RenderTarget.bf b/GlitchyEngine/src/Renderer/RenderTarget.bf index 10d67a5..116985b 100644 --- a/GlitchyEngine/src/Renderer/RenderTarget.bf +++ b/GlitchyEngine/src/Renderer/RenderTarget.bf @@ -17,10 +17,11 @@ namespace GlitchyEngine.Renderer public uint32 SampleCount; public uint32 SampleQuality; public DepthStencilFormat DepthStencilFormat; - public bool IsSwapchainTarget; + /// If not null, the render target will represent the specified swap chains back buffer. + public SwapChain SwapChain; public this(Format pixelFormat, uint32 width, uint32 height, uint32 arraySize = 1, uint32 mipLevels = 1, - DepthStencilFormat depthStencilFormat = .Unknown, CPUAccessFlags cpuAccess = .None, uint32 sampleCount = 1, uint32 sampleQuality = 0, bool isSwapchainTarget = false) + DepthStencilFormat depthStencilFormat = .Unknown, CPUAccessFlags cpuAccess = .None, uint32 sampleCount = 1, uint32 sampleQuality = 0, SwapChain swapChain = null) { PixelFormat = pixelFormat; Width = width; @@ -33,7 +34,7 @@ namespace GlitchyEngine.Renderer SampleCount = sampleCount; SampleQuality = sampleQuality; - IsSwapchainTarget = isSwapchainTarget; + SwapChain = swapChain; } } diff --git a/GlitchyEngine/src/Renderer/RendererAPI.bf b/GlitchyEngine/src/Renderer/RendererAPI.bf index 5d60b74..0ec5b50 100644 --- a/GlitchyEngine/src/Renderer/RendererAPI.bf +++ b/GlitchyEngine/src/Renderer/RendererAPI.bf @@ -28,7 +28,7 @@ namespace GlitchyEngine.Renderer { Debug.Profiler.ProfileRendererFunction!(); - var actualRt = (renderTarget ?? GraphicsContext.Get().SwapChain.BackBuffer); + var actualRt = (renderTarget ?? GraphicsContext.Get().CurrentWindow.SwapChain.BackBuffer); if(options.HasFlag(.Color)) Clear(actualRt, color); diff --git a/GlitchyEngine/src/Renderer/SwapChain.bf b/GlitchyEngine/src/Renderer/SwapChain.bf index 3a8c486..6eed444 100644 --- a/GlitchyEngine/src/Renderer/SwapChain.bf +++ b/GlitchyEngine/src/Renderer/SwapChain.bf @@ -3,8 +3,9 @@ namespace GlitchyEngine.Renderer public class SwapChain { private GraphicsContext _context; + private Window _window; - private bool _changed; + private bool _changed = true; private uint32 _width, _height; @@ -16,6 +17,8 @@ namespace GlitchyEngine.Renderer public GraphicsContext Context => _context; + public Window Window => _window; + /** * Gets or Sets the backbuffers width. * @remarks @ApplyChanges() needs to be called in order to apply the changes. @@ -77,6 +80,11 @@ namespace GlitchyEngine.Renderer public RenderTarget2D BackBuffer => _backBuffer; + public this(Window window) + { + _window = window; + } + public extern void Init(); /** diff --git a/GlitchyEngine/src/Window.bf b/GlitchyEngine/src/Window.bf index 8e82e31..2147c50 100644 --- a/GlitchyEngine/src/Window.bf +++ b/GlitchyEngine/src/Window.bf @@ -27,10 +27,14 @@ namespace GlitchyEngine public class Window { + private SwapChain _swapChain ~ delete _; + public delegate void EventCallback(Event e); protected EventCallback _eventCallback ~ delete _; - + + public SwapChain SwapChain => _swapChain; + public extern this(WindowDescription windowDescription); /** @@ -87,11 +91,6 @@ namespace GlitchyEngine * Gets or Sets whether or not the application uses VSync */ public extern bool IsVSync {get; set;} - - /** - * Gets the windows graphics context. - */ - public extern GraphicsContext Context {get;} /** * Gets a pointer to the platform specific window representation.