Esempio n. 1
0
        protected override void InitializeCore()
        {
            base.InitializeCore();

            shadowMapRenderer = Context.RenderSystem.RenderFeatures
                                .OfType <MeshRenderFeature>().FirstOrDefault()?.RenderFeatures
                                .OfType <ForwardLightingRenderFeature>().FirstOrDefault()?.ShadowMapRenderer;

            if (MSAALevel != MultisampleCount.None)
            {
                actualMultisampleCount = (MultisampleCount)Math.Min((int)MSAALevel, (int)GraphicsDevice.Features[PixelFormat.R16G16B16A16_Float].MultisampleCountMax);
                actualMultisampleCount = (MultisampleCount)Math.Min((int)actualMultisampleCount, (int)GraphicsDevice.Features[DepthBufferFormat].MultisampleCountMax);

                // TODO: We cannot support MSAA on DX10 now
                //   Direct3D has MSAA support starting from version 11 because it requires multisample depth buffers as shader resource views.
                //   Therefore we force-disable MSAA on any platform that doesn't support MSAA.

                if (actualMultisampleCount != MSAALevel)
                {
                    logger.Warning($"Multisample count of {(int) MSAALevel} samples is not supported. Falling back to highest supported sample count of {(int) actualMultisampleCount} samples.");
                }
            }

            var camera = Context.GetCurrentCamera();
        }
Esempio n. 2
0
        protected override void InitializeCore()
        {
            ShadowMapRenderer_notPrivate =
                Context.RenderSystem.RenderFeatures
                .OfType <MeshRenderFeature>()
                .FirstOrDefault()?.RenderFeatures
                .OfType <ForwardLightingRenderFeature>()
                .FirstOrDefault()?.ShadowMapRenderer;

            base.InitializeCore();
        }
Esempio n. 3
0
        protected override void InitializeCore()
        {
            base.InitializeCore();

            // Light accumulation shader
            lightShaftsEffectShader = ToLoadAndUnload(new ImageEffectShader("LightShaftsEffect"));

            // Additive blending shader
            applyLightEffectShader            = ToLoadAndUnload(new ImageEffectShader("AdditiveLightEffect"));
            applyLightEffectShader.BlendState = new BlendStateDescription(Blend.One, Blend.One);

            minmaxVolumeEffectShader = new DynamicEffectInstance("VolumeMinMaxShader");
            minmaxVolumeEffectShader.Initialize(Context.Services);

            blur = ToLoadAndUnload(new GaussianBlur());

            // Need the shadow map renderer in order to render light shafts
            var meshRenderFeature = Context.RenderSystem.RenderFeatures.OfType <MeshRenderFeature>().FirstOrDefault();

            if (meshRenderFeature == null)
            {
                throw new InvalidOperationException("Missing mesh render feature");
            }

            var forwardLightingFeature = meshRenderFeature.RenderFeatures.OfType <ForwardLightingRenderFeature>().FirstOrDefault();

            if (forwardLightingFeature == null)
            {
                throw new InvalidOperationException("Missing forward lighting render feature");
            }

            shadowMapRenderer = forwardLightingFeature.ShadowMapRenderer;

            for (int i = 0; i < 2; ++i)
            {
                var minmaxPipelineState = new MutablePipelineState(Context.GraphicsDevice);
                minmaxPipelineState.State.SetDefaults();

                minmaxPipelineState.State.BlendState.RenderTarget0.BlendEnable           = true;
                minmaxPipelineState.State.BlendState.RenderTarget0.ColorSourceBlend      = Blend.One;
                minmaxPipelineState.State.BlendState.RenderTarget0.ColorDestinationBlend = Blend.One;
                minmaxPipelineState.State.BlendState.RenderTarget0.ColorBlendFunction    = i == 0 ? BlendFunction.Min : BlendFunction.Max;
                minmaxPipelineState.State.BlendState.RenderTarget0.ColorWriteChannels    = i == 0 ? ColorWriteChannels.Red : ColorWriteChannels.Green;

                minmaxPipelineState.State.DepthStencilState.DepthBufferEnable      = false;
                minmaxPipelineState.State.DepthStencilState.DepthBufferWriteEnable = false;
                minmaxPipelineState.State.RasterizerState.DepthClipEnable          = true;
                minmaxPipelineState.State.RasterizerState.CullMode = i == 0 ? CullMode.Back : CullMode.Front;

                minmaxPipelineStates[i] = minmaxPipelineState;
            }
        }
        private void PrepareLightGroups(RenderDrawContext context, FastList <RenderView> renderViews, RenderView renderView, RenderViewLightData renderViewData, IShadowMapRenderer shadowMapRenderer, RenderGroup group)
        {
            var viewIndex = renderViews.IndexOf(renderView);

            foreach (var activeRenderer in renderViewData.ActiveRenderers)
            {
                // Find lights
                var lightCollection = activeRenderer.LightGroup.FindLightCollectionByGroup(group);

                // Light collections aren't cleared (see ClearCache). Can be null after switching to empty scenes.
                if (lightCollection is null)
                {
                    continue;
                }

                // Indices of lights in lightCollection that need processing
                lightIndicesToProcess.Clear();
                for (int i = 0; i < lightCollection.Count; i++)
                {
                    lightIndicesToProcess.Add(i);
                }

                // Loop over all the renderers in order
                int rendererIndex = 0;
                foreach (var renderer in activeRenderer.Renderers)
                {
                    var processLightsParameters = new LightGroupRendererBase.ProcessLightsParameters
                    {
                        Context                   = context,
                        ViewIndex                 = viewIndex,
                        View                      = renderView,
                        Views                     = renderViews,
                        Renderers                 = activeRenderer.Renderers,
                        RendererIndex             = rendererIndex++,
                        LightCollection           = lightCollection,
                        LightIndices              = lightIndicesToProcess,
                        LightType                 = activeRenderer.LightGroup.LightType,
                        ShadowMapRenderer         = shadowMapRenderer,
                        ShadowMapTexturesPerLight = renderViewData.RenderLightsWithShadows,
                    };
                    renderer.ProcessLights(processLightsParameters);
                }
            }
        }
        public virtual void Draw(RenderDrawContext drawContext, IShadowMapRenderer ShadowMapRenderer)
        {
            if (renderVoxelVolumes == null)
            {
                return;
            }
            var context      = drawContext;
            var renderSystem = drawContext.RenderContext.RenderSystem;

            using (drawContext.PushRenderTargetsAndRestore())
            {
                // Draw all shadow views generated for the current view
                foreach (var renderView in renderSystem.Views)
                {
                    VoxelVolumeComponent component = null;

                    if (reflectiveVoxelViews.TryGetValue(renderView, out component))
                    {
                        RenderView voxelizeRenderView = renderView;
                        var        data = renderVoxelVolumeData[component];

                        if (data.ClipMapResolution.X < 16 || data.ClipMapResolution.Y < 16 || data.ClipMapResolution.Z < 16)
                        {
                            continue;
                        }

                        //Render Shadow Maps
                        RenderView oldView = drawContext.RenderContext.RenderView;

                        drawContext.RenderContext.RenderView = voxelizeRenderView;
                        ShadowMapRenderer.Draw(drawContext);

                        drawContext.RenderContext.RenderView = oldView;

                        //Render/Collect voxel fragments
                        using (drawContext.QueryManager.BeginProfile(Color.Black, FragmentVoxelizationProfilingKey))
                        {
                            drawContext.CommandList.ResetTargets();
                            drawContext.CommandList.SetRenderTarget(null, MSAARenderTarget);
                            float maxResolution = Math.Max(Math.Max(data.ClipMapResolution.X, data.ClipMapResolution.Y), data.ClipMapResolution.Z);
                            drawContext.CommandList.SetViewport(new Viewport(0, 0, (int)maxResolution, (int)maxResolution));
                            renderSystem.Draw(drawContext, voxelizeRenderView, renderSystem.RenderStages[voxelizeRenderView.RenderStages[0].Index]);
                        }

                        //Fill and write to voxel volume
                        using (drawContext.QueryManager.BeginProfile(Color.Black, ArrangementVoxelizationProfilingKey))
                        {
                            context.CommandList.ClearReadWrite(data.ClipMaps, new Vector4(0.0f));

                            ArrangeFragments.ThreadGroupCounts = new Int3(16, 16, 1);
                            ArrangeFragments.ThreadNumbers     = new Int3((int)data.ClipMapResolution.X / ArrangeFragments.ThreadGroupCounts.X, (int)data.ClipMapResolution.Y / ArrangeFragments.ThreadGroupCounts.Y, data.ClipMapCount / ArrangeFragments.ThreadGroupCounts.Z);
                            ArrangeFragments.Parameters.Set(ArrangeFragmentsKeys.VoxelFragmentsCounts, FragmentsCounter);
                            ArrangeFragments.Parameters.Set(ArrangeFragmentsKeys.VoxelFragments, Fragments);
                            ArrangeFragments.Parameters.Set(ArrangeFragmentsKeys.VoxelVolumeW0, data.ClipMaps);
                            ArrangeFragments.Parameters.Set(ArrangeFragmentsKeys.clipMapResolution, data.ClipMapResolution);
                            ((RendererBase)ArrangeFragments).Draw(context);


                            context.CommandList.ClearReadWrite(FragmentsCounter, new Vector4(0));
                        }

                        //Mipmap
                        using (drawContext.QueryManager.BeginProfile(Color.Black, MipmappingVoxelizationProfilingKey))
                        {
                            ClearBuffer.ThreadGroupCounts = new Int3(64, 1, 1);
                            ClearBuffer.ThreadNumbers     = new Int3(FragmentsCounter.ElementCount / ClearBuffer.ThreadGroupCounts.X, 1 / ClearBuffer.ThreadGroupCounts.Y, 1 / ClearBuffer.ThreadGroupCounts.Z);
                            ClearBuffer.Parameters.Set(ClearBufferKeys.buffer, FragmentsCounter);
                            ((RendererBase)ClearBuffer).Draw(context);

                            //Mipmap
                            Vector3 resolution        = data.ClipMapResolution;
                            Int3    threadGroupCounts = new Int3(8, 8, 8);
                            for (int i = 0; i < TempMipMaps.Length - 1; i++)
                            {
                                resolution /= 2;
                                if (resolution.X < threadGroupCounts.X || resolution.Y < threadGroupCounts.Y || resolution.Z < threadGroupCounts.Z)
                                {
                                    threadGroupCounts /= 4;
                                    if (threadGroupCounts.X < 1)
                                    {
                                        threadGroupCounts.X = 1;
                                    }
                                    if (threadGroupCounts.Y < 1)
                                    {
                                        threadGroupCounts.Y = 1;
                                    }
                                    if (threadGroupCounts.Z < 1)
                                    {
                                        threadGroupCounts.Z = 1;
                                    }
                                }
                                Generate3DMipmaps.ThreadGroupCounts = threadGroupCounts;
                                Generate3DMipmaps.ThreadNumbers     = new Int3((int)resolution.X / threadGroupCounts.X, (int)resolution.Y / threadGroupCounts.Y, (int)resolution.Z / threadGroupCounts.Z);

                                if (i == 0)
                                {
                                    Generate3DMipmaps.Parameters.Set(Generate3DMipmapsKeys.VoxelsTexR, data.ClipMaps);
                                }
                                else
                                {
                                    Generate3DMipmaps.Parameters.Set(Generate3DMipmapsKeys.VoxelsTexR, TempMipMaps[i - 1]);
                                }
                                Generate3DMipmaps.Parameters.Set(Generate3DMipmapsKeys.VoxelsTexW, TempMipMaps[i]);
                                ((RendererBase)Generate3DMipmaps).Draw(context);
                                //Don't seem to be able to read and write to the same texture, even if the views
                                //point to different mipmaps.
                                context.CommandList.CopyRegion(TempMipMaps[i], 0, null, data.MipMaps, i);
                            }
                        }
                    }
                }
            }
        }
        public virtual void Collect(RenderContext Context, IShadowMapRenderer ShadowMapRenderer)
        {
            renderVoxelVolumes = Context.VisibilityGroup.Tags.Get(CurrentRenderVoxelVolumes);

            if (renderVoxelVolumes == null)
            {
                return;
            }

            Vector3 resolutionMax     = new Vector3(0, 0, 0);
            int     fragmentsMax      = 0;
            int     fragmentsCountMax = 0;

            //Create per-volume textures
            foreach (var pair in renderVoxelVolumes)
            {
                var volume = pair.Value;
                var bounds = volume.ClipMapMatrix.ScaleVector;

                var resolution = bounds / volume.AproxVoxelSize;

                //Calculate closest power of 2 on each axis
                resolution.X = (float)Math.Pow(2, Math.Round(Math.Log(resolution.X, 2)));
                resolution.Y = (float)Math.Pow(2, Math.Round(Math.Log(resolution.Y, 2)));
                resolution.Z = (float)Math.Pow(2, Math.Round(Math.Log(resolution.Z, 2)));

                resolution = new Vector3(resolution.X, resolution.Z, resolution.Y);//Temporary

                Vector3 ClipMapResolution = new Vector3(resolution.X, resolution.Y, resolution.Z * volume.ClipMapCount);
                Vector3 MipMapResolution  = resolution / 2;

                RenderVoxelVolumeData data;
                if (!renderVoxelVolumeData.TryGetValue(pair.Key, out data))
                {
                    renderVoxelVolumeData.Add(pair.Key, data = new RenderVoxelVolumeData());
                }

                data.ClipMapResolution = resolution;
                if (NeedToRecreateTexture(data.ClipMaps, ClipMapResolution))
                {
                    data.ClipMaps = Xenko.Graphics.Texture.New3D(Context.GraphicsDevice, (int)ClipMapResolution.X, (int)ClipMapResolution.Y, (int)ClipMapResolution.Z, new MipMapCount(false), Xenko.Graphics.PixelFormat.R16G16B16A16_Float, TextureFlags.ShaderResource | TextureFlags.UnorderedAccess);
                }
                if (NeedToRecreateTexture(data.MipMaps, resolution))
                {
                    data.MipMaps = Xenko.Graphics.Texture.New3D(Context.GraphicsDevice, (int)MipMapResolution.X, (int)MipMapResolution.Y, (int)MipMapResolution.Z, new MipMapCount(true), Xenko.Graphics.PixelFormat.R16G16B16A16_Float, TextureFlags.ShaderResource | TextureFlags.UnorderedAccess);
                }


                resolutionMax     = Vector3.Min(new Vector3(256), Vector3.Max(resolutionMax, resolution));
                fragmentsMax      = Math.Max(fragmentsMax, (int)(ClipMapResolution.X * ClipMapResolution.Y * ClipMapResolution.Z));
                fragmentsCountMax = Math.Max(fragmentsCountMax, (int)(ClipMapResolution.X * ClipMapResolution.Y) * volume.ClipMapCount);
            }

            //Create re-usable textures

            float resolutionMaxSide = Math.Max(Math.Max(resolutionMax.X, resolutionMax.Y), resolutionMax.Z);

            if (NeedToRecreateTexture(MSAARenderTarget, new Vector3(resolutionMaxSide, resolutionMaxSide, 1)))
            {
                MSAARenderTarget = Texture.New(Context.GraphicsDevice, TextureDescription.New2D((int)resolutionMaxSide, (int)resolutionMaxSide, new MipMapCount(false), PixelFormat.R8G8B8A8_UNorm, TextureFlags.RenderTarget, 1, GraphicsResourceUsage.Default, MultisampleCount.X8), null);
            }

            if (NeedToRecreateBuffer(Fragments, fragmentsMax))
            {
                Fragments = Xenko.Graphics.Buffer.Structured.New(Context.GraphicsDevice, fragmentsMax, 24, true);
            }
            if (NeedToRecreateBuffer(FragmentsCounter, fragmentsCountMax))
            {
                FragmentsCounter = Xenko.Graphics.Buffer.Typed.New(Context.GraphicsDevice, fragmentsCountMax, PixelFormat.R32_SInt, true);
            }

            Vector3 MipMapResolutionMax = resolutionMax / 2;

            if (TempMipMaps == null || !TextureDimensionsEqual(TempMipMaps[0], MipMapResolutionMax))
            {
                if (TempMipMaps != null)
                {
                    for (int i = 0; i < TempMipMaps.Length; i++)
                    {
                        TempMipMaps[0].Dispose();
                    }
                }

                int mipCount = 1 + (int)Math.Floor(Math.Log(Math.Max(MipMapResolutionMax.X, Math.Max(MipMapResolutionMax.Y, MipMapResolutionMax.Z)), 2));

                TempMipMaps = new Xenko.Graphics.Texture[mipCount];

                for (int i = 0; i < TempMipMaps.Length; i++)
                {
                    TempMipMaps[i] = Xenko.Graphics.Texture.New3D(Context.GraphicsDevice, (int)MipMapResolutionMax.X, (int)MipMapResolutionMax.Y, (int)MipMapResolutionMax.Z, false, Xenko.Graphics.PixelFormat.R16G16B16A16_Float, TextureFlags.ShaderResource | TextureFlags.UnorderedAccess);

                    MipMapResolutionMax /= 2;
                    MipMapResolutionMax  = Vector3.Max(new Vector3(1), MipMapResolutionMax);
                }
            }
            if (Generate3DMipmaps == null)
            {
                Generate3DMipmaps = new Xenko.Rendering.ComputeEffect.ComputeEffectShader(Context)
                {
                    ShaderSourceName = "Generate3DMipmaps"
                };
                ClearBuffer = new Xenko.Rendering.ComputeEffect.ComputeEffectShader(Context)
                {
                    ShaderSourceName = "ClearBuffer"
                };
                ArrangeFragments = new Xenko.Rendering.ComputeEffect.ComputeEffectShader(Context)
                {
                    ShaderSourceName = "ArrangeFragments"
                };
            }

            //Create all the views
            reflectiveVoxelViews.Clear();
            foreach (var pair in renderVoxelVolumes)
            {
                var volume = pair.Value;
                var data   = renderVoxelVolumeData[pair.Key];
                var bounds = volume.ClipMapMatrix.ScaleVector;

                var shadowRenderView = new RenderView();

                float nearClip = 0.1f;
                float farClip  = 999.7f;
                //Currently hard coded and kinda odd
                shadowRenderView.View       = Matrix.Translation(0.0f, 1.0f, 0.0f) * Matrix.RotationX(-3.1415f / 2.0f);
                shadowRenderView.Projection = Matrix.OrthoRH(bounds.X * (float)Math.Pow(2, volume.ClipMapCount), bounds.Z * (float)Math.Pow(2, volume.ClipMapCount), nearClip, farClip);
                Matrix.Multiply(ref shadowRenderView.View, ref shadowRenderView.Projection, out shadowRenderView.ViewProjection);
                shadowRenderView.Frustum     = new BoundingFrustum(ref shadowRenderView.ViewProjection);
                shadowRenderView.CullingMode = CameraCullingMode.Frustum;
                shadowRenderView.ViewSize    = new Vector2(1024, 1024);

                float maxRes = Math.Max(Math.Max(data.ClipMapResolution.X, data.ClipMapResolution.Y), data.ClipMapResolution.Z);

                var rotmat = new Matrix(1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1);//xzy

                var viewToTexTrans = Matrix.Translation(0.5f, 0.5f, 0.5f);
                var viewToTexScale = Matrix.Scaling(0.5f, 0.5f, 0.5f);
                var viewportAspect = Matrix.Scaling(data.ClipMapResolution / maxRes);

                // Matrix BaseVoxelMatrix = transmat * scalemat * rotmat;
                Matrix BaseVoxelMatrix = volume.ClipMapMatrix * Matrix.Identity;
                BaseVoxelMatrix.Invert();
                BaseVoxelMatrix = BaseVoxelMatrix * Matrix.Scaling(2f, 2f, 2f) * rotmat;

                data.Matrix         = BaseVoxelMatrix * viewToTexScale * viewToTexTrans;
                data.ViewportMatrix = BaseVoxelMatrix * viewportAspect;
                data.ClipMapCount   = volume.ClipMapCount;

                shadowRenderView.NearClipPlane = nearClip;
                shadowRenderView.FarClipPlane  = farClip;
                shadowRenderView.RenderStages.Add(VoxelStage);

                reflectiveVoxelViews.Add(shadowRenderView, pair.Key);

                // Add the render view for the current frame
                Context.RenderSystem.Views.Add(shadowRenderView);

                // Collect objects in shadow views
                Context.VisibilityGroup.TryCollect(shadowRenderView);

                ShadowMapRenderer?.RenderViewsWithShadows.Add(shadowRenderView);
            }
        }
Esempio n. 7
0
        protected override void InitializeCore()
        {
            base.InitializeCore();

            shadowMapRenderer = Context.RenderSystem.RenderFeatures.OfType <MeshRenderFeature>().FirstOrDefault()?.RenderFeatures.OfType <ForwardLightingRenderFeature>().FirstOrDefault()?.ShadowMapRenderer;

            if (MSAALevel != MultisampleCount.None)
            {
                actualMultisampleCount = (MultisampleCount)Math.Min((int)MSAALevel, (int)GraphicsDevice.Features[PixelFormat.R16G16B16A16_Float].MultisampleCountMax);
                actualMultisampleCount = (MultisampleCount)Math.Min((int)actualMultisampleCount, (int)GraphicsDevice.Features[DepthBufferFormat].MultisampleCountMax);

                // Note: we cannot support MSAA on DX10 now
                if (GraphicsDevice.Features.HasMultisampleDepthAsSRV == false)
                {
                    actualMultisampleCount = MultisampleCount.None;
                }
            }

            var camera = Context.GetCurrentCamera();

            vrSystem = Services.GetService <VRDeviceSystem>();
            if (vrSystem != null)
            {
                if (VRSettings.Enabled)
                {
                    var requiredDescs = VRSettings.RequiredApis.ToArray();
                    vrSystem.PreferredApis = requiredDescs.Select(x => x.Api).Distinct().ToArray();

                    // remove VR API duplicates and keep first desired config only
                    var preferredScalings = new Dictionary <VRApi, float>();
                    foreach (var desc in requiredDescs)
                    {
                        if (!preferredScalings.ContainsKey(desc.Api))
                        {
                            preferredScalings[desc.Api] = desc.ResolutionScale;
                        }
                    }
                    vrSystem.PreferredScalings = preferredScalings;

                    vrSystem.RequireMirror = VRSettings.CopyMirror;
                    vrSystem.MirrorWidth   = GraphicsDevice.Presenter.BackBuffer.Width;
                    vrSystem.MirrorHeight  = GraphicsDevice.Presenter.BackBuffer.Height;

                    vrSystem.Enabled = true; //careful this will trigger the whole chain of initialization!
                    vrSystem.Visible = true;

                    VRSettings.VRDevice = vrSystem.Device;

                    vrSystem.PreviousUseCustomProjectionMatrix = camera.UseCustomProjectionMatrix;
                    vrSystem.PreviousUseCustomViewMatrix       = camera.UseCustomViewMatrix;
                    vrSystem.PreviousCameraProjection          = camera.ProjectionMatrix;

                    if (VRSettings.VRDevice.SupportsOverlays)
                    {
                        foreach (var overlay in VRSettings.Overlays)
                        {
                            if (overlay != null && overlay.Texture != null)
                            {
                                overlay.Overlay = VRSettings.VRDevice.CreateOverlay(overlay.Texture.Width, overlay.Texture.Height, overlay.Texture.MipLevels, (int)overlay.Texture.MultisampleCount);
                            }
                        }
                    }
                }
                else
                {
                    vrSystem.Enabled = false;
                    vrSystem.Visible = false;

                    VRSettings.VRDevice = null;

                    if (vrSystem.Device != null) //we had a device before so we know we need to restore the camera
                    {
                        camera.UseCustomViewMatrix       = vrSystem.PreviousUseCustomViewMatrix;
                        camera.UseCustomProjectionMatrix = vrSystem.PreviousUseCustomProjectionMatrix;
                        camera.ProjectionMatrix          = vrSystem.PreviousCameraProjection;
                    }
                }
            }
        }
Esempio n. 8
0
        protected override void InitializeCore()
        {
            base.InitializeCore();

            shadowMapRenderer = Context.RenderSystem.RenderFeatures.OfType <MeshRenderFeature>().FirstOrDefault()?.RenderFeatures.OfType <ForwardLightingRenderFeature>().FirstOrDefault()?.ShadowMapRenderer;

            if (MSAALevel != MultisampleCount.None)
            {
                actualMultisampleCount = (MultisampleCount)Math.Min((int)MSAALevel, (int)GraphicsDevice.Features[PixelFormat.R16G16B16A16_Float].MultisampleCountMax);
                actualMultisampleCount = (MultisampleCount)Math.Min((int)actualMultisampleCount, (int)GraphicsDevice.Features[DepthBufferFormat].MultisampleCountMax);

                // Note: we cannot support MSAA on DX10 now
                if (GraphicsDevice.Features.HasMultisampleDepthAsSRV == false &&     // TODO: Try enabling MSAA on DX9!
                    GraphicsDevice.Platform != GraphicsPlatform.OpenGL &&
                    GraphicsDevice.Platform != GraphicsPlatform.OpenGLES)
                {
                    // OpenGL has MSAA support on every version.
                    // OpenGL ES has MSAA support starting from version 3.0.
                    // Direct3D has MSAA support starting from version 11 because it requires multisample depth buffers as shader resource views.
                    // Therefore we force-disable MSAA on any platform that doesn't support MSAA.

                    actualMultisampleCount = MultisampleCount.None;
                }

                if (actualMultisampleCount != MSAALevel)
                {
                    logger.Warning("Multisample count of " + (int)MSAALevel + " samples not supported. Falling back to highest supported sample count of " + (int)actualMultisampleCount + " samples.");
                }

#if XENKO_PLATFORM_IOS
                // MSAA is not supported on iOS currently because OpenTK doesn't expose "GL.BlitFramebuffer()" on iOS for some reason.
                actualMultisampleCount = MultisampleCount.None;
#endif
            }

            var camera = Context.GetCurrentCamera();

            vrSystem = Services.GetService <VRDeviceSystem>();
            if (vrSystem != null)
            {
                if (VRSettings.Enabled)
                {
                    var requiredDescs = VRSettings.RequiredApis.ToArray();
                    vrSystem.PreferredApis = requiredDescs.Select(x => x.Api).Distinct().ToArray();

                    // remove VR API duplicates and keep first desired config only
                    var preferredScalings = new Dictionary <VRApi, float>();
                    foreach (var desc in requiredDescs)
                    {
                        if (!preferredScalings.ContainsKey(desc.Api))
                        {
                            preferredScalings[desc.Api] = desc.ResolutionScale;
                        }
                    }
                    vrSystem.PreferredScalings = preferredScalings;

                    vrSystem.RequireMirror = VRSettings.CopyMirror;
                    vrSystem.MirrorWidth   = GraphicsDevice.Presenter.BackBuffer.Width;
                    vrSystem.MirrorHeight  = GraphicsDevice.Presenter.BackBuffer.Height;

                    vrSystem.Enabled = true; //careful this will trigger the whole chain of initialization!
                    vrSystem.Visible = true;

                    VRSettings.VRDevice = vrSystem.Device;

                    vrSystem.PreviousUseCustomProjectionMatrix = camera.UseCustomProjectionMatrix;
                    vrSystem.PreviousUseCustomViewMatrix       = camera.UseCustomViewMatrix;
                    vrSystem.PreviousCameraProjection          = camera.ProjectionMatrix;

                    if (VRSettings.VRDevice.SupportsOverlays)
                    {
                        foreach (var overlay in VRSettings.Overlays)
                        {
                            if (overlay != null && overlay.Texture != null)
                            {
                                overlay.Overlay = VRSettings.VRDevice.CreateOverlay(overlay.Texture.Width, overlay.Texture.Height, overlay.Texture.MipLevels, (int)overlay.Texture.MultisampleCount);
                            }
                        }
                    }
                }
                else
                {
                    vrSystem.Enabled = false;
                    vrSystem.Visible = false;

                    VRSettings.VRDevice = null;

                    if (vrSystem.Device != null) //we had a device before so we know we need to restore the camera
                    {
                        camera.UseCustomViewMatrix       = vrSystem.PreviousUseCustomViewMatrix;
                        camera.UseCustomProjectionMatrix = vrSystem.PreviousUseCustomProjectionMatrix;
                        camera.ProjectionMatrix          = vrSystem.PreviousCameraProjection;
                    }
                }
            }
        }