Skip to content

Commit 694ebbb

Browse files
committed
Support multi-window replay on Android
Introduce JAVA layer into gfxreconstruct to enable replay multi-window trace files on Android. The command line changes to am start -n "com.lunarg.gfxreconstruct.replay/.ReplayActivity"
1 parent 4428ab3 commit 694ebbb

30 files changed

+217
-7
lines changed

android/framework/application/CMakeLists.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
add_library(gfxrecon_application STATIC "")
22

33
target_sources(gfxrecon_application
4-
PRIVATE
4+
PUBLIC
55
${GFXRECON_SOURCE_DIR}/framework/application/android_context.h
66
${GFXRECON_SOURCE_DIR}/framework/application/android_context.cpp
77
${GFXRECON_SOURCE_DIR}/framework/application/android_window.h
@@ -10,6 +10,7 @@ target_sources(gfxrecon_application
1010
${GFXRECON_SOURCE_DIR}/framework/application/application.cpp
1111
${GFXRECON_SOURCE_DIR}/framework/application/wsi_context.h
1212
${GFXRECON_SOURCE_DIR}/framework/application/wsi_context.cpp
13+
${GFXRECON_SOURCE_DIR}/framework/application/android_jni.cpp
1314
)
1415

1516
target_include_directories(gfxrecon_application

android/scripts/gfxrecon.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141

4242
# Application info
4343
app_name = 'com.lunarg.gfxreconstruct.replay'
44-
app_activity = '"com.lunarg.gfxreconstruct.replay/android.app.NativeActivity"'
44+
app_activity = '"com.lunarg.gfxreconstruct.replay/.ReplayActivity"'
4545
app_action = 'android.intent.action.MAIN'
4646
app_category = 'android.intent.category.LAUNCHER'
4747

android/tools/replay/src/main/AndroidManifest.xml android/tools/replay/AndroidManifest.xml

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
android:theme="@style/AppTheme"
1515
android:supportsRtl="true"
1616
android:extractNativeLibs="true"
17-
android:hasCode="false">
18-
<activity android:name="android.app.NativeActivity"
17+
android:hasCode="true">
18+
<activity android:name=".ReplayActivity"
1919
android:exported="true"
2020
android:configChanges="orientation|screenSize|keyboard|keyboardHidden|screenLayout"
2121
android:screenOrientation="unspecified">

android/tools/replay/build.gradle

+7
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@ android {
3636
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
3737
}
3838
}
39+
sourceSets {
40+
main {
41+
manifest.srcFile 'AndroidManifest.xml'
42+
java.srcDirs = ['src']
43+
res.srcDirs = ['res']
44+
}
45+
}
3946
externalNativeBuild {
4047
cmake {
4148
path "CMakeLists.txt"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package com.lunarg.gfxreconstruct.replay;
2+
import java.util.List;
3+
import java.util.ArrayList;
4+
import android.app.NativeActivity;
5+
import android.os.Bundle;
6+
import android.widget.FrameLayout;
7+
import android.view.SurfaceView;
8+
import android.view.SurfaceHolder;
9+
import android.view.ViewGroup.LayoutParams;
10+
import android.view.Surface;
11+
import android.util.Log;
12+
import android.content.Context;
13+
import android.view.View;
14+
15+
public class ReplayActivity extends NativeActivity
16+
{
17+
private FrameLayout frameLayout;
18+
private static final String TAG = "gfxrecon: ReplayActivity";
19+
private Surface mSurface;
20+
public native void setSurface(Surface surface);
21+
private List<VKSurfaceView> mSurfaceviewList = new ArrayList<VKSurfaceView>();
22+
23+
@Override protected void onCreate(Bundle savedInstanceState)
24+
{
25+
super.onCreate(savedInstanceState);
26+
27+
// Create a FrameLayout to hold SurfaceView instances
28+
frameLayout = new FrameLayout(this);
29+
setContentView(frameLayout);
30+
31+
System.loadLibrary("gfxrecon-replay");
32+
}
33+
34+
private class VKSurfaceView extends SurfaceView implements SurfaceHolder.Callback
35+
{
36+
private int width;
37+
private int height;
38+
39+
public VKSurfaceView(Context context, int w, int h)
40+
{
41+
super(context);
42+
43+
width = w;
44+
height = h;
45+
46+
SurfaceHolder holder = getHolder();
47+
holder.addCallback(this);
48+
}
49+
50+
@Override public void surfaceCreated(SurfaceHolder holder)
51+
{
52+
mSurface = holder.getSurface();
53+
Log.i(TAG, "SurfaceHolder.Callback: surfaceCreated:" + mSurface);
54+
}
55+
56+
@Override public void surfaceDestroyed(SurfaceHolder holder)
57+
{
58+
mSurface = holder.getSurface();
59+
Log.i(TAG, "SurfaceHolder.Callback: surfaceDestroyed:" + mSurface);
60+
}
61+
62+
@Override public void surfaceChanged(SurfaceHolder holder, int format, int w, int h)
63+
{
64+
mSurface = holder.getSurface();
65+
Log.i(TAG, "SurfaceHolder.Callback: surfaceChanged:" + mSurface + " " + w + " " + h);
66+
}
67+
}
68+
69+
public void addNewView(int width, int height)
70+
{
71+
// Create a new SurfaceView
72+
final Context context = this;
73+
final int wid = width;
74+
final int hei = height;
75+
mSurface = null;
76+
runOnUiThread(new Runnable() {
77+
@Override public void run()
78+
{
79+
System.loadLibrary("gfxrecon-replay");
80+
VKSurfaceView newSurfaceView = new VKSurfaceView(context, wid, hei);
81+
frameLayout.addView(newSurfaceView,
82+
new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
83+
Log.i(TAG,
84+
"Create a new surface view:"
85+
+ " width:" + wid + " height:" + hei);
86+
mSurfaceviewList.add(newSurfaceView);
87+
}
88+
});
89+
while (mSurface == null)
90+
{
91+
try
92+
{
93+
Thread.sleep(100);
94+
}
95+
catch (Exception e)
96+
{
97+
Log.w(TAG, "Create new surface failed");
98+
e.printStackTrace();
99+
}
100+
}
101+
setSurface(mSurface);
102+
}
103+
104+
private void removeOneView(int surface_idx)
105+
{
106+
final int sur_idx = surface_idx;
107+
runOnUiThread(new Runnable() {
108+
@Override public void run()
109+
{
110+
if (frameLayout != null)
111+
{
112+
Log.i(TAG, "Remove one view");
113+
frameLayout.removeView(mSurfaceviewList.get(sur_idx));
114+
}
115+
else
116+
{
117+
Log.w(TAG, "View container has been destroyed!");
118+
}
119+
}
120+
});
121+
}
122+
}

framework/application/android_context.cpp

+38
Original file line numberDiff line numberDiff line change
@@ -108,5 +108,43 @@ void AndroidContext::SetOrientation(ScreenOrientation orientation)
108108
}
109109
}
110110

111+
void AndroidContext::requestNativeWindow(int width, int height)
112+
{
113+
JavaVM* jni_vm = nullptr;
114+
jobject jni_activity = nullptr;
115+
JNIEnv* env = nullptr;
116+
if ((android_app_ != nullptr) && (android_app_->activity != nullptr))
117+
{
118+
jni_vm = android_app_->activity->vm;
119+
jni_activity = android_app_->activity->clazz;
120+
}
121+
if ((jni_vm != nullptr) && (jni_activity != 0) && (jni_vm->AttachCurrentThread(&env, nullptr) == JNI_OK))
122+
{
123+
jclass object_class = env->GetObjectClass(jni_activity);
124+
jmethodID createsufaceview = env->GetMethodID(object_class, "addNewView", "(II)V");
125+
env->CallVoidMethod(jni_activity, createsufaceview, width, height);
126+
jni_vm->DetachCurrentThread();
127+
}
128+
}
129+
130+
void AndroidContext::destroyNativeWindow(int window_index)
131+
{
132+
JavaVM* jni_vm = nullptr;
133+
jobject jni_activity = nullptr;
134+
JNIEnv* env = nullptr;
135+
if ((android_app_ != nullptr) && (android_app_->activity != nullptr))
136+
{
137+
jni_vm = android_app_->activity->vm;
138+
jni_activity = android_app_->activity->clazz;
139+
}
140+
if ((jni_vm != nullptr) && (jni_activity != 0) && (jni_vm->AttachCurrentThread(&env, nullptr) == JNI_OK))
141+
{
142+
jclass object_class = env->GetObjectClass(jni_activity);
143+
jmethodID removesurfaceview = env->GetMethodID(object_class, "removeOneView", "(I)V");
144+
env->CallVoidMethod(jni_activity, removesurfaceview, window_index);
145+
jni_vm->DetachCurrentThread();
146+
}
147+
}
148+
111149
GFXRECON_END_NAMESPACE(application)
112150
GFXRECON_END_NAMESPACE(gfxrecon)

framework/application/android_context.h

+4
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ class AndroidContext : public WsiContext
5959

6060
void SetOrientation(ScreenOrientation orientation);
6161

62+
void requestNativeWindow(int width, int height);
63+
64+
void destroyNativeWindow(int window_index);
65+
6266
private:
6367
std::unique_ptr<AndroidWindow> window_{};
6468
struct android_app* android_app_{};

framework/application/android_jni.cpp

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#include <jni.h>
2+
#include <android/log.h>
3+
#include "application/android_window.h"
4+
#include <android/native_window_jni.h>
5+
6+
// Define a log tag for convenience
7+
#define LOG_TAG "gfxrecon: ReplayActivity"
8+
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
9+
10+
extern "C" JNIEXPORT void JNICALL Java_com_lunarg_gfxreconstruct_replay_ReplayActivity_setSurface(JNIEnv* env,
11+
jobject obj,
12+
jobject surface)
13+
{
14+
gfxrecon::application::tmp_window = ANativeWindow_fromSurface(env, surface);
15+
LOGI("Created new window %p\n", gfxrecon::application::tmp_window);
16+
}

framework/application/android_window.cpp

+16-3
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535

3636
GFXRECON_BEGIN_NAMESPACE(gfxrecon)
3737
GFXRECON_BEGIN_NAMESPACE(application)
38+
ANativeWindow* tmp_window = nullptr;
3839

3940
AndroidWindow::AndroidWindow(AndroidContext* android_context, ANativeWindow* window) :
4041
android_context_(android_context), window_(window), width_(0), height_(0), pre_transform_(0)
@@ -158,13 +159,25 @@ decode::Window* AndroidWindowFactory::Create(
158159
GFXRECON_UNREFERENCED_PARAMETER(height);
159160
GFXRECON_UNREFERENCED_PARAMETER(force_windowed);
160161

161-
return android_context_->GetWindow();
162+
tmp_window = nullptr;
163+
android_context_->requestNativeWindow(width, height);
164+
AndroidWindow* tmpwin = nullptr;
165+
if (tmp_window != nullptr)
166+
{
167+
tmpwin = new AndroidWindow(android_context_, tmp_window);
168+
GFXRECON_LOG_INFO("Got android window %p\n", tmp_window);
169+
}
170+
else
171+
{
172+
GFXRECON_LOG_WARNING("Get android window failed");
173+
}
174+
return tmpwin;
162175
}
163176

164177
void AndroidWindowFactory::Destroy(decode::Window* window)
165178
{
166-
// Android currently has a single window whose lifetime is managed by AndroidContext.
167-
GFXRECON_UNREFERENCED_PARAMETER(window);
179+
int32_t windowidx = created_window_.at(window) - 1;
180+
android_context_->destroyNativeWindow(windowidx);
168181
}
169182

170183
VkBool32 AndroidWindowFactory::GetPhysicalDevicePresentationSupport(const encode::VulkanInstanceTable* table,

framework/application/android_window.h

+2
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,11 @@
3131
#include "util/platform.h"
3232

3333
#include <android_native_app_glue.h>
34+
#include <jni.h>
3435

3536
GFXRECON_BEGIN_NAMESPACE(gfxrecon)
3637
GFXRECON_BEGIN_NAMESPACE(application)
38+
extern ANativeWindow* tmp_window;
3739

3840
class AndroidWindow : public decode::Window
3941
{

framework/decode/vulkan_swapchain.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ VkResult VulkanSwapchain::CreateSurface(VkResult orig
9696
return VK_ERROR_UNKNOWN;
9797
}
9898

99+
++created_window_count_;
100+
window_factory->created_window_.emplace(window, created_window_count_);
101+
99102
result = window->CreateSurface(instance_table_, instance, flags, replay_surface);
100103

101104
if ((result == VK_SUCCESS) && (replay_surface != nullptr))

framework/decode/vulkan_swapchain.h

+1
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ class VulkanSwapchain
182182
application::Application* application_{ nullptr };
183183
ActiveWindows active_windows_;
184184
int32_t create_surface_count_{ 0 };
185+
int32_t created_window_count_{ 0 };
185186
VulkanSwapchainOptions swapchain_options_;
186187
};
187188

framework/decode/window.h

+3
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "vulkan/vulkan.h"
3232

3333
#include <string>
34+
#include <unordered_map>
3435

3536
GFXRECON_BEGIN_NAMESPACE(gfxrecon)
3637
GFXRECON_BEGIN_NAMESPACE(decode)
@@ -108,6 +109,8 @@ class WindowFactory
108109
virtual VkBool32 GetPhysicalDevicePresentationSupport(const encode::VulkanInstanceTable* table,
109110
VkPhysicalDevice physical_device,
110111
uint32_t queue_family_index) = 0;
112+
113+
std::unordered_map<Window*, int32_t> created_window_;
111114
};
112115

113116
GFXRECON_END_NAMESPACE(decode)

0 commit comments

Comments
 (0)