mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2025-06-30 15:06:26 +02:00
Move solution and projects to src
This commit is contained in:
parent
cd124bda58
commit
cee7121058
3466 changed files with 55 additions and 55 deletions
603
src/Ryujinx.Graphics.Vulkan/Window.cs
Normal file
603
src/Ryujinx.Graphics.Vulkan/Window.cs
Normal file
|
@ -0,0 +1,603 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Vulkan.Effects;
|
||||
using Silk.NET.Vulkan;
|
||||
using Silk.NET.Vulkan.Extensions.KHR;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using VkFormat = Silk.NET.Vulkan.Format;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
class Window : WindowBase, IDisposable
|
||||
{
|
||||
private const int SurfaceWidth = 1280;
|
||||
private const int SurfaceHeight = 720;
|
||||
|
||||
private readonly VulkanRenderer _gd;
|
||||
private readonly SurfaceKHR _surface;
|
||||
private readonly PhysicalDevice _physicalDevice;
|
||||
private readonly Device _device;
|
||||
private SwapchainKHR _swapchain;
|
||||
|
||||
private Image[] _swapchainImages;
|
||||
private Auto<DisposableImageView>[] _swapchainImageViews;
|
||||
|
||||
private Semaphore _imageAvailableSemaphore;
|
||||
private Semaphore _renderFinishedSemaphore;
|
||||
|
||||
private int _width;
|
||||
private int _height;
|
||||
private bool _vsyncEnabled;
|
||||
private bool _vsyncModeChanged;
|
||||
private VkFormat _format;
|
||||
private AntiAliasing _currentAntiAliasing;
|
||||
private bool _updateEffect;
|
||||
private IPostProcessingEffect _effect;
|
||||
private IScalingFilter _scalingFilter;
|
||||
private bool _isLinear;
|
||||
private float _scalingFilterLevel;
|
||||
private bool _updateScalingFilter;
|
||||
private ScalingFilter _currentScalingFilter;
|
||||
|
||||
public unsafe Window(VulkanRenderer gd, SurfaceKHR surface, PhysicalDevice physicalDevice, Device device)
|
||||
{
|
||||
_gd = gd;
|
||||
_physicalDevice = physicalDevice;
|
||||
_device = device;
|
||||
_surface = surface;
|
||||
|
||||
CreateSwapchain();
|
||||
|
||||
var semaphoreCreateInfo = new SemaphoreCreateInfo()
|
||||
{
|
||||
SType = StructureType.SemaphoreCreateInfo
|
||||
};
|
||||
|
||||
gd.Api.CreateSemaphore(device, semaphoreCreateInfo, null, out _imageAvailableSemaphore).ThrowOnError();
|
||||
gd.Api.CreateSemaphore(device, semaphoreCreateInfo, null, out _renderFinishedSemaphore).ThrowOnError();
|
||||
}
|
||||
|
||||
private void RecreateSwapchain()
|
||||
{
|
||||
var oldSwapchain = _swapchain;
|
||||
_vsyncModeChanged = false;
|
||||
|
||||
for (int i = 0; i < _swapchainImageViews.Length; i++)
|
||||
{
|
||||
_swapchainImageViews[i].Dispose();
|
||||
}
|
||||
|
||||
// Destroy old Swapchain.
|
||||
_gd.Api.DeviceWaitIdle(_device);
|
||||
_gd.SwapchainApi.DestroySwapchain(_device, oldSwapchain, Span<AllocationCallbacks>.Empty);
|
||||
|
||||
CreateSwapchain();
|
||||
}
|
||||
|
||||
private unsafe void CreateSwapchain()
|
||||
{
|
||||
_gd.SurfaceApi.GetPhysicalDeviceSurfaceCapabilities(_physicalDevice, _surface, out var capabilities);
|
||||
|
||||
uint surfaceFormatsCount;
|
||||
|
||||
_gd.SurfaceApi.GetPhysicalDeviceSurfaceFormats(_physicalDevice, _surface, &surfaceFormatsCount, null);
|
||||
|
||||
var surfaceFormats = new SurfaceFormatKHR[surfaceFormatsCount];
|
||||
|
||||
fixed (SurfaceFormatKHR* pSurfaceFormats = surfaceFormats)
|
||||
{
|
||||
_gd.SurfaceApi.GetPhysicalDeviceSurfaceFormats(_physicalDevice, _surface, &surfaceFormatsCount, pSurfaceFormats);
|
||||
}
|
||||
|
||||
uint presentModesCount;
|
||||
|
||||
_gd.SurfaceApi.GetPhysicalDeviceSurfacePresentModes(_physicalDevice, _surface, &presentModesCount, null);
|
||||
|
||||
var presentModes = new PresentModeKHR[presentModesCount];
|
||||
|
||||
fixed (PresentModeKHR* pPresentModes = presentModes)
|
||||
{
|
||||
_gd.SurfaceApi.GetPhysicalDeviceSurfacePresentModes(_physicalDevice, _surface, &presentModesCount, pPresentModes);
|
||||
}
|
||||
|
||||
uint imageCount = capabilities.MinImageCount + 1;
|
||||
if (capabilities.MaxImageCount > 0 && imageCount > capabilities.MaxImageCount)
|
||||
{
|
||||
imageCount = capabilities.MaxImageCount;
|
||||
}
|
||||
|
||||
var surfaceFormat = ChooseSwapSurfaceFormat(surfaceFormats);
|
||||
|
||||
var extent = ChooseSwapExtent(capabilities);
|
||||
|
||||
_width = (int)extent.Width;
|
||||
_height = (int)extent.Height;
|
||||
_format = surfaceFormat.Format;
|
||||
|
||||
var oldSwapchain = _swapchain;
|
||||
|
||||
var swapchainCreateInfo = new SwapchainCreateInfoKHR()
|
||||
{
|
||||
SType = StructureType.SwapchainCreateInfoKhr,
|
||||
Surface = _surface,
|
||||
MinImageCount = imageCount,
|
||||
ImageFormat = surfaceFormat.Format,
|
||||
ImageColorSpace = surfaceFormat.ColorSpace,
|
||||
ImageExtent = extent,
|
||||
ImageUsage = ImageUsageFlags.ColorAttachmentBit | ImageUsageFlags.TransferDstBit | ImageUsageFlags.StorageBit,
|
||||
ImageSharingMode = SharingMode.Exclusive,
|
||||
ImageArrayLayers = 1,
|
||||
PreTransform = capabilities.CurrentTransform,
|
||||
CompositeAlpha = ChooseCompositeAlpha(capabilities.SupportedCompositeAlpha),
|
||||
PresentMode = ChooseSwapPresentMode(presentModes, _vsyncEnabled),
|
||||
Clipped = true
|
||||
};
|
||||
|
||||
_gd.SwapchainApi.CreateSwapchain(_device, swapchainCreateInfo, null, out _swapchain).ThrowOnError();
|
||||
|
||||
_gd.SwapchainApi.GetSwapchainImages(_device, _swapchain, &imageCount, null);
|
||||
|
||||
_swapchainImages = new Image[imageCount];
|
||||
|
||||
fixed (Image* pSwapchainImages = _swapchainImages)
|
||||
{
|
||||
_gd.SwapchainApi.GetSwapchainImages(_device, _swapchain, &imageCount, pSwapchainImages);
|
||||
}
|
||||
|
||||
_swapchainImageViews = new Auto<DisposableImageView>[imageCount];
|
||||
|
||||
for (int i = 0; i < _swapchainImageViews.Length; i++)
|
||||
{
|
||||
_swapchainImageViews[i] = CreateSwapchainImageView(_swapchainImages[i], surfaceFormat.Format);
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe Auto<DisposableImageView> CreateSwapchainImageView(Image swapchainImage, VkFormat format)
|
||||
{
|
||||
var componentMapping = new ComponentMapping(
|
||||
ComponentSwizzle.R,
|
||||
ComponentSwizzle.G,
|
||||
ComponentSwizzle.B,
|
||||
ComponentSwizzle.A);
|
||||
|
||||
var aspectFlags = ImageAspectFlags.ColorBit;
|
||||
|
||||
var subresourceRange = new ImageSubresourceRange(aspectFlags, 0, 1, 0, 1);
|
||||
|
||||
var imageCreateInfo = new ImageViewCreateInfo()
|
||||
{
|
||||
SType = StructureType.ImageViewCreateInfo,
|
||||
Image = swapchainImage,
|
||||
ViewType = ImageViewType.Type2D,
|
||||
Format = format,
|
||||
Components = componentMapping,
|
||||
SubresourceRange = subresourceRange
|
||||
};
|
||||
|
||||
_gd.Api.CreateImageView(_device, imageCreateInfo, null, out var imageView).ThrowOnError();
|
||||
return new Auto<DisposableImageView>(new DisposableImageView(_gd.Api, _device, imageView));
|
||||
}
|
||||
|
||||
private static SurfaceFormatKHR ChooseSwapSurfaceFormat(SurfaceFormatKHR[] availableFormats)
|
||||
{
|
||||
if (availableFormats.Length == 1 && availableFormats[0].Format == VkFormat.Undefined)
|
||||
{
|
||||
return new SurfaceFormatKHR(VkFormat.B8G8R8A8Unorm, ColorSpaceKHR.PaceSrgbNonlinearKhr);
|
||||
}
|
||||
|
||||
foreach (var format in availableFormats)
|
||||
{
|
||||
if (format.Format == VkFormat.B8G8R8A8Unorm && format.ColorSpace == ColorSpaceKHR.PaceSrgbNonlinearKhr)
|
||||
{
|
||||
return format;
|
||||
}
|
||||
}
|
||||
|
||||
return availableFormats[0];
|
||||
}
|
||||
|
||||
private static CompositeAlphaFlagsKHR ChooseCompositeAlpha(CompositeAlphaFlagsKHR supportedFlags)
|
||||
{
|
||||
if (supportedFlags.HasFlag(CompositeAlphaFlagsKHR.OpaqueBitKhr))
|
||||
{
|
||||
return CompositeAlphaFlagsKHR.OpaqueBitKhr;
|
||||
}
|
||||
else if (supportedFlags.HasFlag(CompositeAlphaFlagsKHR.PreMultipliedBitKhr))
|
||||
{
|
||||
return CompositeAlphaFlagsKHR.PreMultipliedBitKhr;
|
||||
}
|
||||
else
|
||||
{
|
||||
return CompositeAlphaFlagsKHR.InheritBitKhr;
|
||||
}
|
||||
}
|
||||
|
||||
private static PresentModeKHR ChooseSwapPresentMode(PresentModeKHR[] availablePresentModes, bool vsyncEnabled)
|
||||
{
|
||||
if (!vsyncEnabled && availablePresentModes.Contains(PresentModeKHR.ImmediateKhr))
|
||||
{
|
||||
return PresentModeKHR.ImmediateKhr;
|
||||
}
|
||||
else if (availablePresentModes.Contains(PresentModeKHR.MailboxKhr))
|
||||
{
|
||||
return PresentModeKHR.MailboxKhr;
|
||||
}
|
||||
else
|
||||
{
|
||||
return PresentModeKHR.FifoKhr;
|
||||
}
|
||||
}
|
||||
|
||||
public static Extent2D ChooseSwapExtent(SurfaceCapabilitiesKHR capabilities)
|
||||
{
|
||||
if (capabilities.CurrentExtent.Width != uint.MaxValue)
|
||||
{
|
||||
return capabilities.CurrentExtent;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint width = Math.Max(capabilities.MinImageExtent.Width, Math.Min(capabilities.MaxImageExtent.Width, SurfaceWidth));
|
||||
uint height = Math.Max(capabilities.MinImageExtent.Height, Math.Min(capabilities.MaxImageExtent.Height, SurfaceHeight));
|
||||
|
||||
return new Extent2D(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe override void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback)
|
||||
{
|
||||
_gd.PipelineInternal.AutoFlush.Present();
|
||||
|
||||
uint nextImage = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
var acquireResult = _gd.SwapchainApi.AcquireNextImage(
|
||||
_device,
|
||||
_swapchain,
|
||||
ulong.MaxValue,
|
||||
_imageAvailableSemaphore,
|
||||
new Fence(),
|
||||
ref nextImage);
|
||||
|
||||
if (acquireResult == Result.ErrorOutOfDateKhr ||
|
||||
acquireResult == Result.SuboptimalKhr ||
|
||||
_vsyncModeChanged)
|
||||
{
|
||||
RecreateSwapchain();
|
||||
}
|
||||
else
|
||||
{
|
||||
acquireResult.ThrowOnError();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var swapchainImage = _swapchainImages[nextImage];
|
||||
|
||||
_gd.FlushAllCommands();
|
||||
|
||||
var cbs = _gd.CommandBufferPool.Rent();
|
||||
|
||||
Transition(
|
||||
cbs.CommandBuffer,
|
||||
swapchainImage,
|
||||
0,
|
||||
AccessFlags.TransferWriteBit,
|
||||
ImageLayout.Undefined,
|
||||
ImageLayout.General);
|
||||
|
||||
var view = (TextureView)texture;
|
||||
|
||||
UpdateEffect();
|
||||
|
||||
if (_effect != null)
|
||||
{
|
||||
view = _effect.Run(view, cbs, _width, _height);
|
||||
}
|
||||
|
||||
int srcX0, srcX1, srcY0, srcY1;
|
||||
float scale = view.ScaleFactor;
|
||||
|
||||
if (crop.Left == 0 && crop.Right == 0)
|
||||
{
|
||||
srcX0 = 0;
|
||||
srcX1 = (int)(view.Width / scale);
|
||||
}
|
||||
else
|
||||
{
|
||||
srcX0 = crop.Left;
|
||||
srcX1 = crop.Right;
|
||||
}
|
||||
|
||||
if (crop.Top == 0 && crop.Bottom == 0)
|
||||
{
|
||||
srcY0 = 0;
|
||||
srcY1 = (int)(view.Height / scale);
|
||||
}
|
||||
else
|
||||
{
|
||||
srcY0 = crop.Top;
|
||||
srcY1 = crop.Bottom;
|
||||
}
|
||||
|
||||
if (scale != 1f)
|
||||
{
|
||||
srcX0 = (int)(srcX0 * scale);
|
||||
srcY0 = (int)(srcY0 * scale);
|
||||
srcX1 = (int)Math.Ceiling(srcX1 * scale);
|
||||
srcY1 = (int)Math.Ceiling(srcY1 * scale);
|
||||
}
|
||||
|
||||
if (ScreenCaptureRequested)
|
||||
{
|
||||
if (_effect != null)
|
||||
{
|
||||
_gd.CommandBufferPool.Return(
|
||||
cbs,
|
||||
null,
|
||||
stackalloc[] { PipelineStageFlags.ColorAttachmentOutputBit },
|
||||
null);
|
||||
_gd.FlushAllCommands();
|
||||
cbs.GetFence().Wait();
|
||||
cbs = _gd.CommandBufferPool.Rent();
|
||||
}
|
||||
|
||||
CaptureFrame(view, srcX0, srcY0, srcX1 - srcX0, srcY1 - srcY0, view.Info.Format.IsBgr(), crop.FlipX, crop.FlipY);
|
||||
|
||||
ScreenCaptureRequested = false;
|
||||
}
|
||||
|
||||
float ratioX = crop.IsStretched ? 1.0f : MathF.Min(1.0f, _height * crop.AspectRatioX / (_width * crop.AspectRatioY));
|
||||
float ratioY = crop.IsStretched ? 1.0f : MathF.Min(1.0f, _width * crop.AspectRatioY / (_height * crop.AspectRatioX));
|
||||
|
||||
int dstWidth = (int)(_width * ratioX);
|
||||
int dstHeight = (int)(_height * ratioY);
|
||||
|
||||
int dstPaddingX = (_width - dstWidth) / 2;
|
||||
int dstPaddingY = (_height - dstHeight) / 2;
|
||||
|
||||
int dstX0 = crop.FlipX ? _width - dstPaddingX : dstPaddingX;
|
||||
int dstX1 = crop.FlipX ? dstPaddingX : _width - dstPaddingX;
|
||||
|
||||
int dstY0 = crop.FlipY ? dstPaddingY : _height - dstPaddingY;
|
||||
int dstY1 = crop.FlipY ? _height - dstPaddingY : dstPaddingY;
|
||||
|
||||
if (_scalingFilter != null)
|
||||
{
|
||||
_scalingFilter.Run(
|
||||
view,
|
||||
cbs,
|
||||
_swapchainImageViews[nextImage],
|
||||
_format,
|
||||
_width,
|
||||
_height,
|
||||
new Extents2D(srcX0, srcY0, srcX1, srcY1),
|
||||
new Extents2D(dstX0, dstY0, dstX1, dstY1)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
_gd.HelperShader.BlitColor(
|
||||
_gd,
|
||||
cbs,
|
||||
view,
|
||||
_swapchainImageViews[nextImage],
|
||||
_width,
|
||||
_height,
|
||||
1,
|
||||
_format,
|
||||
false,
|
||||
new Extents2D(srcX0, srcY0, srcX1, srcY1),
|
||||
new Extents2D(dstX0, dstY1, dstX1, dstY0),
|
||||
_isLinear,
|
||||
true);
|
||||
}
|
||||
|
||||
Transition(
|
||||
cbs.CommandBuffer,
|
||||
swapchainImage,
|
||||
0,
|
||||
0,
|
||||
ImageLayout.General,
|
||||
ImageLayout.PresentSrcKhr);
|
||||
|
||||
_gd.CommandBufferPool.Return(
|
||||
cbs,
|
||||
stackalloc[] { _imageAvailableSemaphore },
|
||||
stackalloc[] { PipelineStageFlags.ColorAttachmentOutputBit },
|
||||
stackalloc[] { _renderFinishedSemaphore });
|
||||
|
||||
// TODO: Present queue.
|
||||
var semaphore = _renderFinishedSemaphore;
|
||||
var swapchain = _swapchain;
|
||||
|
||||
Result result;
|
||||
|
||||
var presentInfo = new PresentInfoKHR()
|
||||
{
|
||||
SType = StructureType.PresentInfoKhr,
|
||||
WaitSemaphoreCount = 1,
|
||||
PWaitSemaphores = &semaphore,
|
||||
SwapchainCount = 1,
|
||||
PSwapchains = &swapchain,
|
||||
PImageIndices = &nextImage,
|
||||
PResults = &result
|
||||
};
|
||||
|
||||
lock (_gd.QueueLock)
|
||||
{
|
||||
_gd.SwapchainApi.QueuePresent(_gd.Queue, presentInfo);
|
||||
}
|
||||
}
|
||||
|
||||
public override void SetAntiAliasing(AntiAliasing effect)
|
||||
{
|
||||
if (_currentAntiAliasing == effect && _effect != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_currentAntiAliasing = effect;
|
||||
|
||||
_updateEffect = true;
|
||||
}
|
||||
|
||||
public override void SetScalingFilter(ScalingFilter type)
|
||||
{
|
||||
if (_currentScalingFilter == type && _effect != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_currentScalingFilter = type;
|
||||
|
||||
_updateScalingFilter = true;
|
||||
}
|
||||
|
||||
private void UpdateEffect()
|
||||
{
|
||||
if (_updateEffect)
|
||||
{
|
||||
_updateEffect = false;
|
||||
|
||||
switch (_currentAntiAliasing)
|
||||
{
|
||||
case AntiAliasing.Fxaa:
|
||||
_effect?.Dispose();
|
||||
_effect = new FxaaPostProcessingEffect(_gd, _device);
|
||||
break;
|
||||
case AntiAliasing.None:
|
||||
_effect?.Dispose();
|
||||
_effect = null;
|
||||
break;
|
||||
case AntiAliasing.SmaaLow:
|
||||
case AntiAliasing.SmaaMedium:
|
||||
case AntiAliasing.SmaaHigh:
|
||||
case AntiAliasing.SmaaUltra:
|
||||
var quality = _currentAntiAliasing - AntiAliasing.SmaaLow;
|
||||
if (_effect is SmaaPostProcessingEffect smaa)
|
||||
{
|
||||
smaa.Quality = quality;
|
||||
}
|
||||
else
|
||||
{
|
||||
_effect?.Dispose();
|
||||
_effect = new SmaaPostProcessingEffect(_gd, _device, quality);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_updateScalingFilter)
|
||||
{
|
||||
_updateScalingFilter = false;
|
||||
|
||||
switch (_currentScalingFilter)
|
||||
{
|
||||
case ScalingFilter.Bilinear:
|
||||
case ScalingFilter.Nearest:
|
||||
_scalingFilter?.Dispose();
|
||||
_scalingFilter = null;
|
||||
_isLinear = _currentScalingFilter == ScalingFilter.Bilinear;
|
||||
break;
|
||||
case ScalingFilter.Fsr:
|
||||
if (_scalingFilter is not FsrScalingFilter)
|
||||
{
|
||||
_scalingFilter?.Dispose();
|
||||
_scalingFilter = new FsrScalingFilter(_gd, _device);
|
||||
}
|
||||
|
||||
_scalingFilter.Level = _scalingFilterLevel;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void SetScalingFilterLevel(float level)
|
||||
{
|
||||
_scalingFilterLevel = level;
|
||||
_updateScalingFilter = true;
|
||||
}
|
||||
|
||||
private unsafe void Transition(
|
||||
CommandBuffer commandBuffer,
|
||||
Image image,
|
||||
AccessFlags srcAccess,
|
||||
AccessFlags dstAccess,
|
||||
ImageLayout srcLayout,
|
||||
ImageLayout dstLayout)
|
||||
{
|
||||
var subresourceRange = new ImageSubresourceRange(ImageAspectFlags.ColorBit, 0, 1, 0, 1);
|
||||
|
||||
var barrier = new ImageMemoryBarrier()
|
||||
{
|
||||
SType = StructureType.ImageMemoryBarrier,
|
||||
SrcAccessMask = srcAccess,
|
||||
DstAccessMask = dstAccess,
|
||||
OldLayout = srcLayout,
|
||||
NewLayout = dstLayout,
|
||||
SrcQueueFamilyIndex = Vk.QueueFamilyIgnored,
|
||||
DstQueueFamilyIndex = Vk.QueueFamilyIgnored,
|
||||
Image = image,
|
||||
SubresourceRange = subresourceRange
|
||||
};
|
||||
|
||||
_gd.Api.CmdPipelineBarrier(
|
||||
commandBuffer,
|
||||
PipelineStageFlags.TopOfPipeBit,
|
||||
PipelineStageFlags.AllCommandsBit,
|
||||
0,
|
||||
0,
|
||||
null,
|
||||
0,
|
||||
null,
|
||||
1,
|
||||
barrier);
|
||||
}
|
||||
|
||||
private void CaptureFrame(TextureView texture, int x, int y, int width, int height, bool isBgra, bool flipX, bool flipY)
|
||||
{
|
||||
byte[] bitmap = texture.GetData(x, y, width, height);
|
||||
|
||||
_gd.OnScreenCaptured(new ScreenCaptureImageInfo(width, height, isBgra, bitmap, flipX, flipY));
|
||||
}
|
||||
|
||||
public override void SetSize(int width, int height)
|
||||
{
|
||||
// Not needed as we can get the size from the surface.
|
||||
}
|
||||
|
||||
public override void ChangeVSyncMode(bool vsyncEnabled)
|
||||
{
|
||||
_vsyncEnabled = vsyncEnabled;
|
||||
_vsyncModeChanged = true;
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
_gd.Api.DestroySemaphore(_device, _renderFinishedSemaphore, null);
|
||||
_gd.Api.DestroySemaphore(_device, _imageAvailableSemaphore, null);
|
||||
|
||||
for (int i = 0; i < _swapchainImageViews.Length; i++)
|
||||
{
|
||||
_swapchainImageViews[i].Dispose();
|
||||
}
|
||||
|
||||
_gd.SwapchainApi.DestroySwapchain(_device, _swapchain, null);
|
||||
}
|
||||
|
||||
_effect?.Dispose();
|
||||
_scalingFilter?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue