Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3.x] Window transparency support on Android #51935

Merged
merged 1 commit into from
Sep 15, 2021

Conversation

pouleyKetchoupp
Copy link
Contributor

Implements per-pixel transparency feature on Android.
Allows plugins to do specific rendering and render godot UI on top (useful for camera support with drawing on top).

In order to do that, the plugin itself needs to move its own frame behind the main godot frame with something like that in java:

ViewGroup parentView = (ViewGroup)pluginFrame.getParent();
parentView.removeView(pluginFrame);
parentView.addView(pluginFrame, 0);

3.x only for now, because I haven't looked into how to do it with Vulkan yet.

@@ -139,6 +140,7 @@ private void init(XRMode xrMode, boolean translucent, int depth, int stencil) {
* is interpreted as any 32-bit surface with alpha by SurfaceFlinger.
*/
if (translucent) {
this.setZOrderOnTop(true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pouleyKetchoupp Can you clarify what this does? According to the documentation:

By setting this, you cause it to be placed above the window. This means that none of the contents of the window this SurfaceView is in will be visible on top of its surface.

Which would mean that it would prevent additional component from rendering on top of the surface; is that the desired behavior?

Could you also walk me through how per-pixel transparency is achieved; it may help me understand better the logic in the PR.
Thanks!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which would mean that it would prevent additional component from rendering on top of the surface; is that the desired behavior?

Yes, this allows Godot rendering to be displayed on top, so a plugin can render behind the transparent surface, rather than always in front (like it does by default). As a side effect, plugins can't render on top of the surface when this is enabled, as you mentioned, but it's the only way I've found to achieve transparent rendering.

Could you also walk me through how per-pixel transparency is achieved; it may help me understand better the logic in the PR.

Transparent rendering is achieved this way:

  1. per_pixel_transparency/allowed & per_pixel_transparency/enabled are both set to true in Project Settings
  2. get_tree().get_root().transparent_bg = true; is called at runtime to make the viewport transparent
  3. At Android export time, --translucent is added to the command line (this step is needed on 3.x because Main::setup() is called after the surface is initialized, it won't be needed on master).
  4. At Android runtime, --translucent command line is checked to create a surface with transparent capabilities (on master it will just check the project settings directly)
  5. When the plugin is initialized, its own rendering frame is placed on top of the main frame by default, so this can be used to move it behind:
ViewGroup parentView = (ViewGroup)pluginFrame.getParent();
parentView.removeView(pluginFrame);
parentView.addView(pluginFrame, 0);

(from what I've seen, both this and setZOrderOnTop(true) are needed for transparency to work correctly)

The result is the plugin does its custom rendering (for example camera preview) and Godot draws on top of it with transparent background (for example to add UI elements).

Let me know if there's more I can clarify!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remember that transparent viewports aren't supported yet with the Vulkan renderer: #40651

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pouleyKetchoupp Sorry for the delay, I was out for the past week.

Regarding ordering of the surface and of the plugins' views, we control that logic here and here, so couldn't just have a flag that tells us if translucent is enabled, and changed the order of the views as shown in your sample code?
With that approach, would we still need to set the setZOrderOnTop(...) flag?

Also, when you say the surface is transparent, does that mean that any view(s) behind it will be visible? I'm trying to clarify here since making the background transparent in Godot may not mean the same as making the surface view background transparent (which would allow to see underlying views).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No worries!

Regarding ordering of the surface and of the plugins' views, we control that logic here and here, so couldn't just have a flag that tells us if translucent is enabled, and changed the order of the views as shown in your sample code?
With that approach, would we still need to set the setZOrderOnTop(...) flag?

I'm not sure why (it could be the way GL surfaces work), but both are needed for the system to work correctly.
I've tested again since last time and here's how it goes:
-setZOrderOnTop is needed for transparent rendering, without it the Godot surface renders with black background and any view behind it is not visible.
-Changing the order of views is not strictly needed if setZOrderOnTop is set, but it helps with inputs as it allows Godot inputs to be taken over views placed behind.

Also, when you say the surface is transparent, does that mean that any view(s) behind it will be visible? I'm trying to clarify here since making the background transparent in Godot may not mean the same as making the surface view background transparent (which would allow to see underlying views).

Yes, it allows other views to be visible behind it.

Using this feature, you can also make a transparent app and see the device desktop in the background. But that requires to also change the style of the main Godot app to this:

<style name="GodotAppMainTheme" parent="@android:style/Theme.Black.NoTitleBar.Fullscreen">
     <item name="android:windowIsTranslucent">true</item>
     <item name="android:windowBackground">@android:color/transparent</item>
</style>

So far I haven't found a nice way to enable it in the export process or by code, but it would be a nice addition to this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

About setting the transparent theme in the app:
From what I've just tested, changing the theme to a new one with windowIsTranslucent attribute even in the app onCreate function is not enough to make it work. It can modify the color but not the translucent flag.

I guess the best way to handle it would be to change the theme in the manifest file during export, but do you know how to handle it with custom builds? It looks like the export process fixes the manifest file only for standard builds.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pouleyKetchoupp Do you have an example for the logic you use to update the theme?

In general, the theme needs to be set before even calling setContentView(...).
So the logic should look something like:

public void onCreate(Bundle savedInstanceState) {
		if (hasTranslucentFlag())
                    setTheme(R.style.GodotAppTranslucentTheme);
                else
                    setTheme(R.style.GodotAppMainTheme);
		super.onCreate(savedInstanceState);
}

where GodotAppTranslucentTheme would be as you described above:

<style name="GodotAppTranslucentTheme" parent="@android:style/Theme.Black.NoTitleBar.Fullscreen">
     <item name="android:windowIsTranslucent">true</item>
     <item name="android:windowBackground">@android:color/transparent</item>
</style>

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on your example, even just setting the transparent theme without checking any flag like this:

public void onCreate(Bundle savedInstanceState) {
                setTheme(R.style.GodotAppTranslucentTheme);
		super.onCreate(savedInstanceState);
}

doesn't seem to work, unless windowIsTranslucent is set to true in the default theme as well.

It looks like this flag can't be changed once the app is being created, and it works by adding it in the default theme only because the splash theme inherits from the default theme (unless I'm missing something).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this flag can't be changed once the app is being created, and it works by adding it in the default theme only because the splash theme inherits from the default theme (unless I'm missing something).

@pouleyKetchoupp I thought you were still looking into this. If not, I'm good with approving and unblocking this PR, and attempting to update the app theme for translucency in another PR. For that approach, can you create an issue that documents what you already tried; I can then take a look and see if anything is missing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the confusion! Yes let me finalize this PR and open an issue to solve the app theme thing separately.

@m4gr3d
Copy link
Contributor

m4gr3d commented Sep 1, 2021

@pouleyKetchoupp For this and #52320, do we want to cherry-pick them into the 3.3.x branch?
I thought only bug fixes were supposed to go in that branch, while this would fall under the new feature category.

@pouleyKetchoupp
Copy link
Contributor Author

@m4gr3d I was thinking it's small enough not to causes regressions, but you're right, since it should be just bug fixing these changes should go on 3.x only. I'll remove the tags.

Copy link
Member

@akien-mga akien-mga left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems good to me, but I'll let @m4gr3d give the final seal of approval/merge.

@akien-mga
Copy link
Member

Might be worth updating the doc since it's not longer limited to "desktop window"s:

                <member name="display/window/per_pixel_transparency/allowed" type="bool" setter="" getter="" default="false">
                        If [code]true[/code], allows per-pixel transparency in a desktop window. This affects performance, so leave it on [code]false[/code] unless you need it.
                </member>
                <member name="display/window/per_pixel_transparency/enabled" type="bool" setter="" getter="" default="false">
                        Sets the window background to transparent when it starts.
                </member>

Might be worth specifying which platforms this is implemented for now that it's not just desktop platforms.

doc/classes/OS.xml Outdated Show resolved Hide resolved
Implements per-pixel transparency feature on Android.
Allows plugins to do specific rendering and render godot UI on top
(useful for camera support with drawing on top).
@pouleyKetchoupp
Copy link
Contributor Author

I've made the last documentation updates and opened an issue for the app theme: #52725

Copy link
Contributor

@m4gr3d m4gr3d left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!

@akien-mga akien-mga merged commit 25cbb85 into godotengine:3.x Sep 15, 2021
@akien-mga
Copy link
Member

Thanks!

@pouleyKetchoupp pouleyKetchoupp deleted the android-transparency-3.x branch September 15, 2021 23:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants