-
-
Notifications
You must be signed in to change notification settings - Fork 21.4k
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
Vulkan rendering support on Android #36919
Vulkan rendering support on Android #36919
Conversation
7354153
to
9596089
Compare
platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularContextFactory.java
Outdated
Show resolved
Hide resolved
9596089
to
15d968c
Compare
@akien-mga Thanks for the review! I've just pushed some changes based on your comments. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Buildsystem and C++ changes look good. I'll let @m4gr3d review the Java/Kotlin parts more in-depth.
I think this is OK, although it may need to be refactored to use the DisplayServer code that will be merged in a week or two. |
15d968c
to
15110dd
Compare
layout.addView(mView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); | ||
edittext.setView(mView); | ||
io.setEdit(edittext); | ||
GodotLib.setup(command_line); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has to be invoked on the rendering thread; I imagine you moved it here in order to be able to access the value for the video driver.
Instead, in export.cpp
, can you push the driver type as a command line argument the same way we do it for the xrMode
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes that's the reason.
I can do it by command line instead as you suggested, but in this case we'll have less options when we want to handle the fallback to OpenGL when Vulkan is not supported on the device (at this point I'm not sure if we'll do that in c++ or java).
This part of the code is supposed to handle the case where Main::setup
is called on the UI thread and it seems to work fine so far:
godot/platform/android/java_godot_lib_jni.cpp
Lines 197 to 199 in 989a223
// Since Godot is initialized on the UI thread, _main_thread_id was set to that thread's id, | |
// but for Godot purposes, the main thread is the one running the game loop | |
Main::setup2(Thread::get_caller_id()); |
What part of the setup do you think is going to cause problems when not run on the render thread?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My main worry is regarding thread safety since setup
initializes some data structures that setup2
and the rest of the logic access at runtime.
With your changes, setup
concludes before the render thread is started, so data access should be safe, especially given that Godot's data structures should be thread-safe.
@akien-mga @reduz It would be good to have someone familiar with Godot's C/C++ thread safety validates these changes as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there should not be a problem calling setup2() on a different thread as setup(), if setup2() will later become the rendering thread. That said, eventually we need to look at some point at optionally supporting a dedicated rendering thread on Android, given this works on Godot already in the rest of the platforms.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah nevermind, this will probably need to be changed and probably done again in setup2.
https://github.com/godotengine/godot/blob/master/main/main.cpp#L404
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah nevermind, this will probably need to be changed and probably done again in setup2.
https://github.com/godotengine/godot/blob/master/main/main.cpp#L404
This is already done in https://github.com/godotengine/godot/blob/master/platform/android/java_godot_lib_jni.cpp#L201.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there should not be a problem calling setup2() on a different thread as setup(), if setup2() will later become the rendering thread. That said, eventually we need to look at some point at optionally supporting a dedicated rendering thread on Android, given this works on Godot already in the rest of the platforms.
@reduz The thread that setup2()
is invoked on is the dedicated rendering thread. Or were you referring to something else?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh no, it's the main thread for Godot. On android there is no render thread support, so you should assume both render and main should be the same for now.
I'm sure it could eventually be done, but I would get everything to work first.
@@ -157,7 +157,7 @@ private void setButtonPausedState(boolean paused) { | |||
private String[] command_line; | |||
private boolean use_apk_expansion; | |||
|
|||
public GodotView mView; | |||
public GodotViewInterface mViewInterface; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rename the interface to GodotRenderView
or RenderView
for short, since it's not typical to have the type as a suffix to the name.
@@ -1,5 +1,5 @@ | |||
/*************************************************************************/ | |||
/* GodotView.java */ | |||
/* GodotViewGL.java */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be consistent with Android naming pattern, can this instead be GodotGLView
or GodotGLRenderView
to match the interface name.
abstract public void onPauseActivity(); | ||
abstract public void onResumeActivity(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be consistent, these should be onActivityPaused
and onActivityResumed
.
import org.godotengine.godot.vulkan.VkRenderer; | ||
import org.godotengine.godot.vulkan.VkSurfaceView; | ||
|
||
public class GodotViewVulkan extends VkSurfaceView implements GodotViewInterface { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be consistent with Android naming pattern, can this be renamed to GodotVulkanView
or GodotVulkanRenderView
to match the interface name.
/** | ||
* Invoked once per frame on the Vulkan thread after the frame is drawn. | ||
*/ | ||
public void onVkDrawFrame() {} | ||
|
||
/** | ||
* Called on the Vulkan thread after the surface is created and whenever the surface size | ||
* changes. | ||
*/ | ||
public void onVkSurfaceChanged(Surface surface, int width, int height) {} | ||
|
||
/** | ||
* Called on the Vulkan thread when the surface is created or recreated. | ||
*/ | ||
public void onVkSurfaceCreated(Surface surface) {} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd merge these with the GL*
callbacks by renaming them to make them generic. For example, onRenderDrawFrame
, onRenderSurfaceChanged
, onRenderSurfaceCreated
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean that the common functions would take both a Surface
and a GL10
as arguments, and one of them would be null depending on the renderer used?
Or maybe we could simply pass a SurfaceView
argument instead, but I don't have an example of these callbacks being used so I'm not sure what the actual use cases are.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On second though let's keep them separate since passing null
values seems like a hack as well.
Given that GLESv2
will remain, this might be useful for plugins that'd like to support one but not the other.
There are a couple other methods that use the onGL
prefix that should be updated though:
onGLRegisterPluginWithGodotNative
onGLGodotMainLoopStarted
I'll provide a PR to update the naming for these since I'd like these changes to be in as soon as possible so as to be stable enough for plugin writers to start using them.
@@ -48,52 +53,56 @@ import android.view.Surface | |||
*/ | |||
internal class VkRenderer { | |||
|
|||
private var pluginRegistry: GodotPluginRegistry = GodotPluginRegistry.getPluginRegistry() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use val
here since the variable is final
.
platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt
Show resolved
Hide resolved
* Invoked on the Vulkan thread when the underlying Android surface is created. | ||
* @param p_surface | ||
*/ | ||
public static native void surfacecreated(Surface p_surface); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method name should be updated to be camelCase (surfaceCreated
).
In the native layer, the logic for this method is almost identical to newcontext
. The two should be merged into one.
@@ -1,5 +1,5 @@ | |||
/*************************************************************************/ | |||
/* vk_renderer_jni.cpp */ | |||
/* vulkan_context_android.h */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's likely that more vulkan logic will be added inside and outside of this class. Can you restore the vulkan
directory and keep all android platform vulkan specific logic (including this class) in there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pouleyKetchoupp Nice job! I've added some feedback to be addressed.
15110dd
to
1a80e20
Compare
Thanks @m4gr3d! I've just pushed some changes that should address your previous comments. So here are the remaining things to do before this PR can be merged:
|
1a80e20
to
69123f4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great!
One feedback to address, alongside your comment.
} | ||
|
||
/** | ||
* Called after the surface is created and whenever its size changes. | ||
*/ | ||
fun onVkSurfaceChanged(surface: Surface, width: Int, height: Int) { | ||
nativeOnVkSurfaceChanged(surface, width, height) | ||
GodotLib.resize(width, height) | ||
GodotLib.newcontext(surface, false) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
newcontext
must only be called once in onVkSurfaceCreated
(see GodotRenderer
for reference).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes, good point! Right now, I'm not handling the surface re-creation properly, so the workaround is to reset the app by calling newcontext
. Instead it should update the surface in the native code, but I haven't looked into that yet.
The consequence is the app gets restarted when paused and resumed again.
It's important to fix and I'll see if I have time for it in the coming days, but I don't think it's a blocker to merge this PR. In the meantime I'll add a comment in the code and in the PR description.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good.
For the moment, you could then remove the call to newcontext
from onVkSurfaceCreated
since it's always followed by onVkSurfaceChanged
and leave a comment like you mentioned to restore the proper logic flow when surface recreation is properly handled.
69123f4
to
644812f
Compare
VkImageFormatListCreateInfoKHR format_list_create_info; | ||
Vector<VkFormat> allowed_formats; | ||
|
||
// TODO: vkCreateImage fails with format list on Android (VK_ERROR_OUT_OF_HOST_MEMORY) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose we should be able to check for support via RenderingDevice. Can you add a #warning there ? I am sure many mobile GPUs support it and we should be able to take advantage of it.
layout.addView(mView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); | ||
edittext.setView(mView); | ||
io.setEdit(edittext); | ||
GodotLib.setup(command_line); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there should not be a problem calling setup2() on a different thread as setup(), if setup2() will later become the rendering thread. That said, eventually we need to look at some point at optionally supporting a dedicated rendering thread on Android, given this works on Godot already in the rest of the platforms.
//case FEATURE_MOUSE_WARP: | ||
//case FEATURE_NATIVE_DIALOG: | ||
//case FEATURE_NATIVE_ICON: | ||
//case FEATURE_NATIVE_VIDEO: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Android does support native video, you should be able to port it from 3.x branch
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@reduz The 3.x native video implementation is brittle, lack flexibility and possibly not working. The thinking for 4.0 (and 3.2.2) is to provide video support via the revamped Android plugin system instead.
layout.addView(mView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); | ||
edittext.setView(mView); | ||
io.setEdit(edittext); | ||
GodotLib.setup(command_line); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah nevermind, this will probably need to be changed and probably done again in setup2.
https://github.com/godotengine/godot/blob/master/main/main.cpp#L404
This is already done in https://github.com/godotengine/godot/blob/master/platform/android/java_godot_lib_jni.cpp#L201.
layout.addView(mView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); | ||
edittext.setView(mView); | ||
io.setEdit(edittext); | ||
GodotLib.setup(command_line); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there should not be a problem calling setup2() on a different thread as setup(), if setup2() will later become the rendering thread. That said, eventually we need to look at some point at optionally supporting a dedicated rendering thread on Android, given this works on Godot already in the rest of the platforms.
@reduz The thread that setup2()
is invoked on is the dedicated rendering thread. Or were you referring to something else?
91891e2
to
2aa9849
Compare
Thanks for the review @reduz! I've just added the #warning you asked for. If everything's good around the display server code, this PR should be ready to merge. |
after trying to rebase android editor onto this branch, I found some small things that I think should be refactored... I can open a quick PR to this branch if you're interesed |
@thebestnom Ok, that makes sense. Thanks for the feedback! I can change it some time this week, but if you decide to do it in the meantime, please let me know and feel free to open a PR :) |
@pouleyKetchoupp working on it :) |
@pouleyKetchoupp hey, I commited everything but it doesn't let me open PR into https://github.com/nekomatata/godot/tree/android-vulkan-rendering |
@thebestnom It looks like you would have to actually fork from my repo to be able to do that :/ |
I'm not sure, I can open PR to different forks, I think the problem is that it's team only repo or somthing like that, but it doesn't matter that much... I wanted to make some comment on the code in the PR... so Ill just say it here, |
2aa9849
to
e167af3
Compare
Ok! I've seen the change with key events and it makes sense too. I've just pushed a new version that includes your changes. |
Some comments from IRC:
Let's merge as is and refine in future PRs :) |
Thanks! |
Tested a local build, it's not finding |
It works after upgrading the NDK from |
@akien-mga @pouleyKetchoupp We can specify an NDK version in the |
@m4gr3d In order to be more flexible when GLES2 is supported again, we could detect the NDK version during the build, and when the requirement is not met, show a warning and disable Vulkan support. |
@pouleyKetchoupp that sounds overly complex and I'm not sure what the benefit would be done resolving the correct ndk version is a fairly simple task. |
can't you just use |
Can someone make a tracker issue for all the issues mentioned in first comment? |
@Anutrix Ok, I can make one. |
This change enables Vulkan rendering for Android, based on the SurfaceView implementation from PR #36603. There are still some issues to solve, but as a first step Android is now functional again on the 4.0 branch.
Only Vulkan rendering is supported for now, but I've kept the old GL implementation along with this changes, so it should be easy to enable GLES2 again when it's ready.
CC @RandomShaper @m4gr3d @akien-mga @reduz
Remaining issues:
Shaders currently fail because of descriptor set limits (tested on Galaxy Note 5):
Example with
TextureRect
:Vulkan validation layers are not working (fails on
vkGetInstanceProcAddr
)It's disabled on Android for now to avoid crashes.
Errors when using format list in
RenderingDeviceVulkan::texture_create
vmaCreateImage
fails withVK_IMAGE_CREATE_MUTABLE_FORMAT_BIT
(returnsVK_ERROR_OUT_OF_HOST_MEMORY
)It's disabled on Android for now to avoid crashes.
Errors in
RenderingDeviceVulkan::compute_pipeline_create
vkCreateComputePipelines
returnsVK_ERROR_INITIALIZATION_FAILED
in some casesMissing VR support
There's specific initialization for GL, based on
xrMode
, it's not handled for Vulkan.Missing fallback to GLES2
Building templates with Android API 18 to 23
Vulkan support should be disabled in this case.
Properly handleonVkSurfaceChanged
event to update the rendering systemRight now, pausing and resuming the app causes it to restart completely instead.edit: addressed in Proper surface reset when resuming app on Android #39004
Implementation Notes:
New Android SDK requirements for building templates (Vulkan headers and libs):
NDK version: 20.0.5594570
Min Android API: 24
The Vulkan loader is taken from the Android NDK, because the one we're using for other platforms doesn't support Android platform.
[Android] wrong signatures for terminator_CreateAndroidSurfaceKHR() and vkCreateAndroidSurfaceKHR() KhronosGroup/Vulkan-Loader#96
VkRenderer
calls GodotLib directly, so I got rid ofvk_renderer_jni
. It seems easier to have one common place for calls from GL or Vulkan implementations.Fix for
GodotInstrumentation
inAndroidManifest.xml
This is used to force the app to restart.
I've kept the same hack that was used for GL to restart the whole app after paused, but we might want to improve it later by restarting only the rendering system.
Plugins:
Added vulkan specific callbacks in addition to the GL ones, but maybe the interface could be harmonized between the two to make it easier for the user.
Bugsquad edit: Fixes #36919.