public override void Render(RenderContext parentRenderContext) { if (!IsVisible) return; RectangleF bounds = ActualBounds; if (bounds.Width <= 0 || bounds.Height <= 0) return; Matrix? layoutTransformMatrix = LayoutTransform == null ? new Matrix?() : LayoutTransform.GetTransform(); Matrix? renderTransformMatrix = RenderTransform == null ? new Matrix?() : RenderTransform.GetTransform(); RenderContext localRenderContext = parentRenderContext.Derive(bounds, layoutTransformMatrix, renderTransformMatrix, RenderTransformOrigin, Opacity); Matrix finalTransform = localRenderContext.Transform; if (finalTransform != _finalTransform) { _finalTransform = finalTransform; _inverseFinalTransform = Matrix.Invert(_finalTransform.Value); _renderedBoundingBox = CalculateBoundingBox(_innerRect, finalTransform); } Brushes.Brush opacityMask = OpacityMask; if (opacityMask == null && Effect == null) // Simply render without opacity mask RenderOverride(localRenderContext); else { // Control has an opacity mask or Effect // Get global render surface and render texture or create them if they doesn't exist RenderTextureAsset renderTexture = ContentManager.Instance.GetRenderTexture(GLOBAL_RENDER_TEXTURE_ASSET_KEY); // Ensure they are allocated renderTexture.AllocateRenderTarget(GraphicsDevice.Width, GraphicsDevice.Height); if (!renderTexture.IsAllocated) return; // Morpheus_xx: these are typical performance results comparing direct rendering to texture and rendering // to surfaces (for multisampling support). // Results inside GUI-Test OpacityMask screen for default skin (720p windowed / fullscreen 1080p) // Surface + full StretchRect -> Texture : 350 fps / 174 fps // Texture : 485 fps / 265 fps // Results inside GUI-Test OpacityMask screen for Reflexion skin (fullscreen 1080p) // Surface + full StretchRect -> Texture : 142 fps // Texture : 235 fps // Create a temporary render context and render the control to the render texture RenderContext tempRenderContext = new RenderContext(localRenderContext.Transform, localRenderContext.Opacity, bounds, localRenderContext.ZOrder); // An additional copy step is only required for multisampling surfaces bool isMultiSample = GraphicsDevice.Setup.IsMultiSample; if (isMultiSample) { RenderTargetAsset renderSurface = ContentManager.Instance.GetRenderTarget(GLOBAL_RENDER_SURFACE_ASSET_KEY); renderSurface.AllocateRenderTarget(GraphicsDevice.Width, GraphicsDevice.Height); if (!renderSurface.IsAllocated) return; // First render to the multisampled surface RenderToSurface(renderSurface, tempRenderContext); // Unfortunately, brushes/brush effects are based on textures and cannot work with surfaces, so we need this additional copy step GraphicsDevice.Device.StretchRectangle( renderSurface.Surface, new Rectangle(Point.Empty, renderSurface.Size), renderTexture.Surface0, new Rectangle(Point.Empty, renderTexture.Size), TextureFilter.None); } else // Directly render to texture RenderToTexture(renderTexture, tempRenderContext); // Add bounds to our calculated, occupied area. // If we don't do that, lines at the border of this element might be dimmed because of the filter (see OpacityMask test in GUITestPlugin). // The value was just found by testing. Any better solution is welcome. if (opacityMask != null) { const float OPACITY_MASK_BOUNDS = 0.9f; RectangleF occupiedTransformedBounds = tempRenderContext.OccupiedTransformedBounds; occupiedTransformedBounds.X -= OPACITY_MASK_BOUNDS; occupiedTransformedBounds.Y -= OPACITY_MASK_BOUNDS; occupiedTransformedBounds.Width += OPACITY_MASK_BOUNDS * 2; occupiedTransformedBounds.Height += OPACITY_MASK_BOUNDS * 2; // If the control bounds have changed we need to update our primitive context to make the // texture coordinates match up if (_updateOpacityMask || _opacityMaskContext == null || occupiedTransformedBounds != _lastOccupiedTransformedBounds || renderTexture.Size != _lastOpacityRenderSize) { UpdateOpacityMask(occupiedTransformedBounds, renderTexture.Width, renderTexture.Height, localRenderContext.ZOrder); _lastOccupiedTransformedBounds = occupiedTransformedBounds; _updateOpacityMask = false; _lastOpacityRenderSize = renderTexture.Size; } // Now render the opacity texture with the OpacityMask brush) if (opacityMask.BeginRenderOpacityBrush(renderTexture.Texture, new RenderContext(Matrix.Identity, bounds))) { _opacityMaskContext.Render(0); opacityMask.EndRender(); } } // Render Effect Effects.Effect effect = Effect; if (effect != null) { UpdateEffectMask(effect, tempRenderContext.OccupiedTransformedBounds, renderTexture.Width, renderTexture.Height, localRenderContext.ZOrder); if (effect.BeginRender(renderTexture.Texture, new RenderContext(Matrix.Identity, 1.0d, bounds, localRenderContext.ZOrder))) { _effectContext.Render(0); effect.EndRender(); } } } // Calculation of absolute render size (in world coordinate system) parentRenderContext.IncludeTransformedContentsBounds(localRenderContext.OccupiedTransformedBounds); _lastZIndex = localRenderContext.ZOrder; }
public override void Render(RenderContext parentRenderContext) { if (!IsVisible) return; RectangleF bounds = ActualBounds; if (bounds.Width <= 0 || bounds.Height <= 0) return; Matrix? layoutTransformMatrix = LayoutTransform == null ? new Matrix?() : LayoutTransform.GetTransform(); Matrix? renderTransformMatrix = RenderTransform == null ? new Matrix?() : RenderTransform.GetTransform(); RenderContext localRenderContext = parentRenderContext.Derive(bounds, layoutTransformMatrix, renderTransformMatrix, RenderTransformOrigin, Opacity); Matrix finalTransform = localRenderContext.Transform; if (finalTransform != _finalTransform) { _finalTransform = finalTransform; _inverseFinalTransform = Matrix.Invert(_finalTransform.Value); _renderedBoundingBox = CalculateBoundingBox(_innerRect, finalTransform); } Brushes.Brush opacityMask = OpacityMask; Effect effect = Effect; if (opacityMask == null && effect == null) // Simply render without opacity mask RenderOverride(localRenderContext); else { // Control has an opacity mask or Effect // Get global render surface and render texture or create them if they doesn't exist RenderTextureAsset renderTexture = ContentManager.Instance.GetRenderTexture(GLOBAL_RENDER_TEXTURE_ASSET_KEY); // Ensure they are allocated renderTexture.AllocateRenderTarget(GraphicsDevice.Width, GraphicsDevice.Height); if (!renderTexture.IsAllocated) return; // Morpheus_xx: these are typical performance results comparing direct rendering to texture and rendering // to surfaces (for multisampling support). // Results inside GUI-Test OpacityMask screen for default skin (720p windowed / fullscreen 1080p) // Surface + full StretchRect -> Texture : 350 fps / 174 fps // Texture : 485 fps / 265 fps // After OpacityMask fix: // Surface + full StretchRect -> Texture : 250 fps / 155 fps // Surface + Occupied Rect -> Texture : 325 fps / 204 fps // Texture : 330 fps / 213 fps // Results inside GUI-Test OpacityMask screen for Reflexion skin (fullscreen 1080p) // Surface + full StretchRect -> Texture : 142 fps // Texture : 235 fps // Create a temporary render context and render the control to the render texture RenderContext tempRenderContext = new RenderContext(localRenderContext.Transform, localRenderContext.Opacity, bounds, localRenderContext.ZOrder); // If no effect is applied, clear only the area of the control, not whole render target (performance!) tempRenderContext.ClearOccupiedAreaOnly = effect == null; // An additional copy step is only required for multisampling surfaces bool isMultiSample = GraphicsDevice.Setup.IsMultiSample; if (isMultiSample) { RenderTargetAsset renderSurface = ContentManager.Instance.GetRenderTarget(GLOBAL_RENDER_SURFACE_ASSET_KEY); renderSurface.AllocateRenderTarget(GraphicsDevice.Width, GraphicsDevice.Height); if (!renderSurface.IsAllocated) return; // First render to the multisampled surface RenderToSurface(renderSurface, tempRenderContext); // Unfortunately, brushes/brush effects are based on textures and cannot work with surfaces, so we need this additional copy step // Morpheus_xx, 03/2013: changed to copy only the occupied area of Surface, instead of complete area. This improves performance a lot. GraphicsDevice.Device.StretchRectangle( renderSurface.Surface, ToRect(tempRenderContext.OccupiedTransformedBounds, renderSurface.Size), // new Rectangle(new Point(), renderSurface.Size), renderTexture.Surface0, ToRect(tempRenderContext.OccupiedTransformedBounds, renderTexture.Size), // new Rectangle(new Point(), renderTexture.Size), TextureFilter.None); } else { // Directly render to texture RenderToTexture(renderTexture, tempRenderContext); } // Render Effect if (effect == null) { // Use a default effect to draw the render target if none is set if (_defaultEffect == null) { _defaultEffect = new SimpleShaderEffect { ShaderEffectName = "normal" }; } effect = _defaultEffect; } UpdateEffectMask(effect, tempRenderContext.OccupiedTransformedBounds, renderTexture.Width, renderTexture.Height, localRenderContext.ZOrder); if (effect.BeginRender(renderTexture.Texture, new RenderContext(Matrix.Identity, 1.0d, bounds, localRenderContext.ZOrder))) { _effectContext.Render(0); effect.EndRender(); } } // Calculation of absolute render size (in world coordinate system) parentRenderContext.IncludeTransformedContentsBounds(localRenderContext.OccupiedTransformedBounds); _lastZIndex = localRenderContext.ZOrder; }
public override void Render(RenderContext parentRenderContext) { if (!IsVisible) return; RectangleF bounds = ActualBounds; if (bounds.Width <= 0 || bounds.Height <= 0) return; Matrix? layoutTransformMatrix = LayoutTransform == null ? new Matrix?() : LayoutTransform.GetTransform(); Matrix? renderTransformMatrix = RenderTransform == null ? new Matrix?() : RenderTransform.GetTransform(); RenderContext localRenderContext = parentRenderContext.Derive(bounds, layoutTransformMatrix, renderTransformMatrix, RenderTransformOrigin, Opacity); _inverseFinalTransform = Matrix.Invert(localRenderContext.Transform); Brushes.Brush opacityMask = OpacityMask; if (opacityMask == null && Effect == null) // Simply render without opacity mask DoRender(localRenderContext); else { // Control has an opacity mask or Effect RenderTextureAsset renderTexture = ContentManager.Instance.GetRenderTexture(GLOBAL_RENDER_TEXTURE_ASSET_KEY); // Get global render surface and render texture or create them if they dosn't exist RenderTargetAsset renderSurface = ContentManager.Instance.GetRenderTarget(GLOBAL_RENDER_SURFACE_ASSET_KEY); // Ensure they are allocated renderTexture.AllocateRenderTarget(GraphicsDevice.Width, GraphicsDevice.Height); renderSurface.AllocateRenderTarget(GraphicsDevice.Width, GraphicsDevice.Height); if (!renderTexture.IsAllocated || !renderSurface.IsAllocated) return; // Create a temporary render context and render the control to the render texture RenderContext tempRenderContext = new RenderContext(localRenderContext.Transform, localRenderContext.Opacity, bounds, localRenderContext.ZOrder); RenderToSurface(renderSurface, tempRenderContext); // Unfortunately, brushes/brush effects are based on textures and cannot work with surfaces, so we need this additional copy step GraphicsDevice.Device.StretchRectangle( renderSurface.Surface, new Rectangle(Point.Empty, renderSurface.Size), renderTexture.Surface0, new Rectangle(Point.Empty, renderTexture.Size), TextureFilter.None); // Add bounds to our calculated, occupied area. // If we don't do that, lines at the border of this element might be dimmed because of the filter (see OpacityMask test in GUITestPlugin). // The value was just found by testing. Any better solution is welcome. if (opacityMask != null) { const float OPACITY_MASK_BOUNDS = 0.9f; RectangleF occupiedTransformedBounds = tempRenderContext.OccupiedTransformedBounds; occupiedTransformedBounds.X -= OPACITY_MASK_BOUNDS; occupiedTransformedBounds.Y -= OPACITY_MASK_BOUNDS; occupiedTransformedBounds.Width += OPACITY_MASK_BOUNDS * 2; occupiedTransformedBounds.Height += OPACITY_MASK_BOUNDS * 2; // If the control bounds have changed we need to update our primitive context to make the // texture coordinates match up if (_updateOpacityMask || _opacityMaskContext == null || occupiedTransformedBounds != _lastOccupiedTransformedBounds || renderSurface.Size != _lastOpacityRenderSize) { UpdateOpacityMask(occupiedTransformedBounds, renderSurface.Width, renderSurface.Height, localRenderContext.ZOrder); _lastOccupiedTransformedBounds = occupiedTransformedBounds; _updateOpacityMask = false; _lastOpacityRenderSize = renderSurface.Size; } // Now render the opacity texture with the OpacityMask brush) if (opacityMask.BeginRenderOpacityBrush(renderTexture.Texture, new RenderContext(Matrix.Identity, bounds))) { _opacityMaskContext.Render(0); opacityMask.EndRender(); } } // Render Effect Effects.Effect effect = Effect; if (effect != null) { UpdateEffectMask(tempRenderContext.OccupiedTransformedBounds, renderTexture.Width, renderTexture.Height, localRenderContext.ZOrder); if (effect.BeginRender(renderTexture.Texture, new RenderContext(Matrix.Identity, 1.0d, bounds, localRenderContext.ZOrder))) { _effectContext.Render(0); effect.EndRender(); } } } // Calculation of absolute render size (in world coordinate system) parentRenderContext.IncludeTransformedContentsBounds(localRenderContext.OccupiedTransformedBounds); _lastZIndex = localRenderContext.ZOrder; }