dotnet / maui

.NET MAUI is the .NET Multi-platform App UI, a framework for building native device applications spanning mobile, tablet, and desktop.
https://dot.net/maui
MIT License
22.21k stars 1.75k forks source link

Border with Shadow triggering "[View] ContentViewGroup not displayed because it is too large to fit into a software layer (or drawing cache)" in Android #17881

Open jonmdev opened 1 year ago

jonmdev commented 1 year ago

Description

When a Border is created and then a shadow is applied, this seems to use a massive amount of resources in Android to the extent that if you make the Border object even slightly larger than the screen, it triggers an error:

[View] ContentViewGroup not displayed because it is too large to fit into a software layer (or drawing cache), needs 10501248 bytes, only 10108800 available

This is when using default Google Pixel 5 Android Emulator.

This does not happen for the same project in Windows/iOS (no errors).

Is there any way to fix this? Is there some problem with how the shadows are rendering?

The bug output seems to suggest it is trying to use 10.5 MB to render the shadow. It claims to only have 10.1 MB available.

I think I understand where the numbers are coming from. The Google Pixel 5 resolution is 1080 x 2340, which at 32 bit depth = 10.1 MB as per calculator: https://www.omnicalculator.com/other/image-file-size

Setting the Border a bit bigger than screen resolution creates a 10.5 MB need which apparently is greater than the device capacity.

So it seems the system is claiming it cannot draw a Shadow on any view larger than the device's native resolution. But does this make sense? It doesn't seem to.

It can clearly draw the Border at a size bigger than the screen resolution, so it is capable of rendering objects bigger than that. If you apply a shadow to a View, is it creating a bitmap object essentially the size of the entire object? Ie. In this case something 10.5 MB big? And if so, is there anyway to fix this? iOS and Windows don't give any error or such limitation.

How does it render the Border itself with no issues but the Shadow has problems?

Is there expected to be a known limitation on Android's Shadow function that all Shadow elements must be kept small or they will use massive resources? It seems if one adds many Shadow elements the resource utilization will be rapidly astronomical. Unless there is an issue that can be fixed.

Does the Android shadow function work somehow different than iOS or Windows?

Thanks for any thoughts.

Steps to Reproduce

  1. Create New MAUI project by File > New called "ContentViewGroup Too Big Bug".
  2. Copy and paste to replace all of App.xaml.cs with:

    namespace ContentViewGroup_Too_Big_Bug {
    public partial class App : Application {
    
        public event Action screenSizeChanged = null;
        public double screenWidth = 0;
        public double screenHeight = 0;
        ContentPage mainPage;
        public App() {
    
            InitializeComponent();
    
            //=========
            //LAYOUT
            //=========
            mainPage = new();
            mainPage.Background = Colors.GreenYellow;
            MainPage = mainPage;
            mainPage.SizeChanged += delegate {
                invokeScreenSizeChangeEvent();
            };
    
            VerticalStackLayout vert = new();
            mainPage.Content = vert;
            vert.HorizontalOptions = LayoutOptions.Center;
    
            Border border1 = new Border();
            border1.BackgroundColor = Colors.Azure;
            border1.Shadow = new Shadow() { Offset = new Point(0, 30), Radius = 40, Brush = Colors.Red }; //causes too large to fit in cache when border 1 is larger than the screen
            vert.Children.Add(border1);
    
            //==================
            //RESIZE FUNCTION
            //==================
            screenSizeChanged += delegate {
    
                vert.HeightRequest = screenHeight;
                vert.WidthRequest = screenWidth * 1;
    
                border1.HeightRequest = screenHeight;
                border1.WidthRequest = screenWidth * 1.1; //causes too large to fit in cacche error when >1x width with a shadow enabled
    
            };
    
            //===================
            //ANIMATION TIMER
            //===================
            var timer = Application.Current.Dispatcher.CreateTimer();
            DateTime dateTime = DateTime.Now;
            double deltaTime = 0;
            double time = 0;
            timer.Tick += delegate {
                deltaTime = (DateTime.Now - dateTime).TotalSeconds;
                time += deltaTime;
                //border1.TranslationX = Math.Sin(time) * 200;
                border1.TranslationY = Math.Sin(time * 1.2) * screenHeight * 0.1;
                dateTime = DateTime.Now;
            };
            timer.Start();
    
        }
        private void invokeScreenSizeChangeEvent() {
            if (mainPage.Width > 0 && mainPage.Height > 0) {
                screenWidth = mainPage.Width;
                screenHeight = mainPage.Height;
                screenSizeChanged?.Invoke();
                //Debug.WriteLine("main page size changed | width: " + screenWidth + " height: " + screenHeight);
            }
        }
    }
    }
  3. Play in Android Pixel 5 emulator and note the error displayed. [View] ContentViewGroup not displayed because it is too large to fit into a software layer (or drawing cache), needs 10501248 bytes, only 10108800 available
  4. Reducing the screenWidth multiplier of the Border1 object or removing the shadow stops the error.

Link to public reproduction project repository

https://github.com/jonmdev/ContentViewGroup-Too-Big-Bug

Version with bug

7.0.92

Is this a regression from previous behavior?

No, this is something new

Last version that worked well

Unknown/Other

Affected platforms

Android

Affected platform versions

Android API 33, .NET 7.0

Did you find any workaround?

If this is an intrinsic limitation of Android's Shadow function we must be very careful in how it is used.

However, I suspect it is more of a MAUI problem because when I search "ContentViewGroup not displayed because it is too large to fit into a software layer" I only find 3rd party systems triggering the problem eg. React Native, which to me suggests it is not necessarily a directly Android issue but rather an implementation issue.

Relevant log output

[View] ContentViewGroup not displayed because it is too large to fit into a software layer (or drawing cache), needs 10501248 bytes, only 10108800 available
jonmdev commented 1 year ago

To add to this report, this is not the only abnormal shadow related behavior I am observing in Android. I am also experiencing an issue where some words just simply don't get rendered at all when a Shadow is applied to them.

I posted a separate bug report for that problem here: https://github.com/dotnet/maui/issues/17884

I am not sure if these are related or unrelated.

ghost commented 1 year ago

We've added this issue to our backlog, and we will work to address it as time and resources allow. If you have any additional information or questions about this issue, please leave a comment. For additional info about issue management, please read our Triage Process.

XamlTest commented 1 year ago

Verified this on Visual Studio Enterprise 17.8.0 Preview 2.0(8.0.0-rc.1.9171). Repro on Android 13.0-API33 with below Project: ContentViewGroup Too Big Bug.zip

era-maui commented 8 months ago

I have encountered a scenario where viewWidth passed to DrawShadow is equal to 1073741794, which in exchange crashes the app at Bitmap.CreateBitmap. Seems that previously closed PR could fix it, but I can see that it was never merged

PR : https://github.com/dotnet/maui/pull/8817