[androidkit] implement ThreadedSurface backed by WindowSurface
This moves ownership of the SkSurface to ThreadedSurface::fWindowSurface.
When we pass a Message to the SurfaceThread, we will include a pointer to ThreadedSurface.fWindowSurface so we can call:
getCanvas() to draw the SkPicture
WindowSurface's constructor so it can hold the WindowContext made during the init
To reference WindowSurface in SurfaceThread, we need to make a header for Surface.cpp (added in this cl)
Change-Id: I8c67223eee301a1b6e0d05934e1f8597cf70bc64
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/404918
Commit-Queue: Jorge Betancourt <jmbetancourt@google.com>
Reviewed-by: Florin Malita <fmalita@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index 30ffbc9..b4ed1cf 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -2500,6 +2500,7 @@
"modules/androidkit/src/RuntimeShaderBuilder.cpp",
"modules/androidkit/src/Shader.cpp",
"modules/androidkit/src/Surface.cpp",
+ "modules/androidkit/src/Surface.h",
"modules/androidkit/src/SurfaceThread.cpp",
"modules/androidkit/src/SurfaceThread.h",
]
diff --git a/modules/androidkit/src/Surface.cpp b/modules/androidkit/src/Surface.cpp
index e9e7e23..b24891a 100644
--- a/modules/androidkit/src/Surface.cpp
+++ b/modules/androidkit/src/Surface.cpp
@@ -4,20 +4,13 @@
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
+#include "modules/androidkit/src/Surface.h"
#include <android/bitmap.h>
#include <android/log.h>
-#include <android/native_window_jni.h>
-#include <android/native_window.h>
-#include <jni.h>
-#include "include/core/SkPictureRecorder.h"
-#include "include/core/SkRefCnt.h"
-#include "include/core/SkSurface.h"
-#include "include/core/SkTypes.h"
#include "tools/sk_app/Application.h"
#include "tools/sk_app/DisplayParams.h"
-#include "tools/sk_app/WindowContext.h"
#include "tools/sk_app/android/WindowContextFactory_android.h"
namespace sk_app {
@@ -28,58 +21,70 @@
}
}
-#include "modules/androidkit/src/SurfaceThread.h"
+WindowSurface::WindowSurface(ANativeWindow* win, std::unique_ptr<sk_app::WindowContext> wctx)
+ : fWindow(win)
+ , fWindowContext(std::move(wctx))
+{
+ SkASSERT(fWindow);
+ SkASSERT(fWindowContext);
+
+ fSurface = fWindowContext->getBackbufferSurface();
+}
+
+void WindowSurface::release(JNIEnv* env) {
+ fWindowContext.reset();
+ ANativeWindow_release(fWindow);
+}
+
+SkCanvas* WindowSurface::getCanvas() {
+ if (fSurface) {
+ return fSurface->getCanvas();
+ }
+ return nullptr;
+}
+
+void WindowSurface::flushAndSubmit() {
+ fSurface->flushAndSubmit();
+ fWindowContext->swapBuffers();
+ fSurface = fWindowContext->getBackbufferSurface();
+}
+
+// SkSurface created from being passed an android.view.Surface
+// For now, assume we are always rendering with OpenGL
+// TODO: add option of choose backing
+ThreadedSurface::ThreadedSurface(JNIEnv* env, jobject surface)
+ : fThread(std::make_unique<SurfaceThread>()) {
+ ANativeWindow* window = ANativeWindow_fromSurface(env, surface);
+ fWidth = ANativeWindow_getWidth(window);
+ fHeight = ANativeWindow_getHeight(window);
+
+ Message message(kInitialize);
+ message.fNativeWindow = window;
+ message.fWindowSurface = &fWindowSurface;
+ fThread->postMessage(message);
+}
+
+void ThreadedSurface::release(JNIEnv* env) {
+ Message message(kDestroy);
+ message.fWindowSurface = &fWindowSurface;
+ fThread->postMessage(message);
+ fThread->release();
+}
+
+SkCanvas* ThreadedSurface::getCanvas() {
+ return fRecorder.beginRecording(fWidth,
+ fHeight);
+}
+
+void ThreadedSurface::flushAndSubmit() {
+ Message message(kRenderPicture);
+ message.fWindowSurface = &fWindowSurface;
+ message.fPicture = fRecorder.finishRecordingAsPicture().release();
+ fThread->postMessage(message);
+}
namespace {
-class Surface : public SkRefCnt {
-public:
- virtual void release(JNIEnv*) = 0;
- virtual void flushAndSubmit() = 0;
- virtual SkCanvas* getCanvas() = 0;
-
- int width() const { return fSurface ? fSurface->width() : 0; }
- int height() const { return fSurface ? fSurface->height() : 0; }
-
-protected:
- sk_sp<SkSurface> fSurface;
-};
-
-class WindowSurface final : public Surface {
-public:
- WindowSurface(ANativeWindow* win, std::unique_ptr<sk_app::WindowContext> wctx)
- : fWindow(win)
- , fWindowContext(std::move(wctx))
- {
- SkASSERT(fWindow);
- SkASSERT(fWindowContext);
-
- fSurface = fWindowContext->getBackbufferSurface();
- }
-
-private:
- void release(JNIEnv* env) override {
- fWindowContext.reset();
- ANativeWindow_release(fWindow);
- }
-
- SkCanvas* getCanvas() override {
- if (fSurface) {
- return fSurface->getCanvas();
- }
- return nullptr;
- }
-
- void flushAndSubmit() override {
- fSurface->flushAndSubmit();
- fWindowContext->swapBuffers();
- fSurface = fWindowContext->getBackbufferSurface();
- }
-
- ANativeWindow* fWindow;
- std::unique_ptr<sk_app::WindowContext> fWindowContext;
-};
-
class BitmapSurface final : public Surface {
public:
BitmapSurface(JNIEnv* env, jobject bitmap) {
@@ -152,43 +157,6 @@
jobject fBitmap;
};
-// SkSurface created from being passed an android.view.Surface
-// For now, assume we are always rendering with OpenGL
-// TODO: add option of choose backing
-class ThreadedSurface final : public Surface {
-public:
- ThreadedSurface(JNIEnv* env, jobject surface) {
- fWindow = ANativeWindow_fromSurface(env, surface);
- Message message(kInitialize);
- message.fNativeWindow = fWindow;
- }
-
-private:
- void release(JNIEnv* env) override {
- fThread.postMessage(Message(kDestroy));
- if (fWindow) {
- ANativeWindow_release(fWindow);
- }
- fSurface.reset();
- }
-
- SkCanvas* getCanvas() override {
- return fRecorder.beginRecording(ANativeWindow_getWidth(fWindow),
- ANativeWindow_getHeight(fWindow));
- }
-
- void flushAndSubmit() override{
- Message message(kRenderPicture);
- message.fNativeWindow = fWindow;
- message.fPicture = fRecorder.finishRecordingAsPicture().release();
- fThread.postMessage(message);
- }
-
- ANativeWindow* fWindow;
- SkPictureRecorder fRecorder;
- SurfaceThread fThread;
-};
-
// *** JNI methods ***
static jlong Surface_CreateBitmap(JNIEnv* env, jobject, jobject bitmap) {
diff --git a/modules/androidkit/src/Surface.h b/modules/androidkit/src/Surface.h
new file mode 100644
index 0000000..7162087
--- /dev/null
+++ b/modules/androidkit/src/Surface.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2021 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef AndroidKit_Surface_DEFINED
+#define AndroidKit_Surface_DEFINED
+
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkSurface.h"
+
+#include <jni.h>
+#include <android/native_window_jni.h>
+#include <android/native_window.h>
+
+#include "tools/sk_app/WindowContext.h"
+
+#include "include/core/SkPictureRecorder.h"
+#include "include/core/SkRefCnt.h"
+#include "include/core/SkSurface.h"
+#include "include/core/SkTypes.h"
+
+class SurfaceThread;
+#include "modules/androidkit/src/SurfaceThread.h"
+
+class Surface : public SkRefCnt {
+public:
+ virtual void release(JNIEnv*) = 0;
+ virtual void flushAndSubmit() = 0;
+ virtual SkCanvas* getCanvas() = 0;
+
+ int width() const { return fSurface ? fSurface->width() : 0; }
+ int height() const { return fSurface ? fSurface->height() : 0; }
+
+protected:
+ sk_sp<SkSurface> fSurface;
+};
+
+class WindowSurface final : public Surface {
+public:
+ WindowSurface(ANativeWindow* win, std::unique_ptr<sk_app::WindowContext> wctx);
+
+private:
+ void release(JNIEnv* env) override;
+ SkCanvas* getCanvas() override;
+ void flushAndSubmit() override;
+
+ ANativeWindow* fWindow;
+ std::unique_ptr<sk_app::WindowContext> fWindowContext;
+};
+
+class ThreadedSurface final : public Surface {
+public:
+ ThreadedSurface(JNIEnv* env, jobject surface);
+
+private:
+ void release(JNIEnv* env) override;
+ SkCanvas* getCanvas() override;
+ void flushAndSubmit() override;
+
+ WindowSurface* fWindowSurface = nullptr;
+ SkPictureRecorder fRecorder;
+ std::unique_ptr<SurfaceThread> fThread;
+ int fWidth;
+ int fHeight;
+};
+
+#endif
diff --git a/modules/androidkit/src/SurfaceThread.cpp b/modules/androidkit/src/SurfaceThread.cpp
index 7ddc2c2..7c4a382 100644
--- a/modules/androidkit/src/SurfaceThread.cpp
+++ b/modules/androidkit/src/SurfaceThread.cpp
@@ -6,14 +6,15 @@
*/
#include "modules/androidkit/src/SurfaceThread.h"
-#include <pthread.h>
-#include <android/looper.h>
+#include "tools/sk_app/WindowContext.h"
+#include "tools/sk_app/android/WindowContextFactory_android.h"
+
+#include "include/core/SkCanvas.h"
#include "include/core/SkTypes.h"
-
SurfaceThread::SurfaceThread() {
- SkDebugf("initialized");
pipe(fPipe);
+ fRunning = true;
pthread_create(&fThread, nullptr, pthread_main, this);
}
@@ -25,6 +26,10 @@
read(fPipe[0], message, sizeof(Message));
}
+void SurfaceThread::release() {
+ pthread_join(fThread, nullptr);
+}
+
int SurfaceThread::message_callback(int /* fd */, int /* events */, void* data) {
auto surfaceThread = (SurfaceThread*)data;
Message message;
@@ -33,17 +38,30 @@
switch (message.fType) {
case kInitialize: {
- SkDebugf("initialize WindowContext");
+ sk_app::DisplayParams params;
+ auto winctx = sk_app::window_context_factory::MakeGLForAndroid(message.fNativeWindow, params);
+ if (!winctx) {
+ break;
+ }
+ *message.fWindowSurface = new WindowSurface(message.fNativeWindow, std::move(winctx));
break;
}
case kDestroy: {
- SkDebugf("surface destroyed, shut down thread");
+ SkDebugf("surface destroyed, shutting down thread");
+ surfaceThread->fRunning = false;
+ if(auto* windowSurface = reinterpret_cast<Surface*>(*message.fWindowSurface)){
+ windowSurface->release(nullptr);
+ delete windowSurface;
+ }
return 0;
break;
}
case kRenderPicture: {
sk_sp<SkPicture> picture(message.fPicture);
- SkDebugf("take in picture and surface from message and call surface.getCanvas().drawPicture()");
+ if(auto* windowSurface = reinterpret_cast<Surface*>(*message.fWindowSurface)){
+ windowSurface->getCanvas()->drawPicture(picture);
+ windowSurface->flushAndSubmit();
+ }
break;
}
default: {
@@ -61,7 +79,7 @@
ALooper_addFd(looper, surfaceThread->fPipe[0], 1, ALOOPER_EVENT_INPUT,
surfaceThread->message_callback, surfaceThread);
- while (true) {
+ while (surfaceThread->fRunning) {
const int ident = ALooper_pollAll(0, nullptr, nullptr, nullptr);
if (ident >= 0) {
diff --git a/modules/androidkit/src/SurfaceThread.h b/modules/androidkit/src/SurfaceThread.h
index 992a30f..403d93f 100644
--- a/modules/androidkit/src/SurfaceThread.h
+++ b/modules/androidkit/src/SurfaceThread.h
@@ -4,13 +4,22 @@
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
+#ifndef AndroidKit_SurfaceThread_DEFINED
+#define AndroidKit_SurfaceThread_DEFINED
+
#include <pthread.h>
#include <unistd.h>
#include <android/looper.h>
#include <android/native_window.h>
+#include "tools/sk_app/DisplayParams.h"
+
#include "include/core/SkPictureRecorder.h"
+class WindowSurface;
+
+#include "modules/androidkit/src/Surface.h"
+
enum MessageType {
kUndefined,
kInitialize,
@@ -21,7 +30,8 @@
struct Message {
MessageType fType = kUndefined;
ANativeWindow* fNativeWindow = nullptr;
- SkPicture* fPicture;
+ SkPicture* fPicture = nullptr;
+ WindowSurface** fWindowSurface = nullptr;
Message() {}
Message(MessageType t) : fType(t) {}
@@ -33,11 +43,16 @@
void postMessage(const Message& message) const;
void readMessage(Message* message) const;
-
+ void release();
private:
static void* pthread_main(void* arg);
static int message_callback(int fd, int events, void* data);
+ // TODO: This has to be static, which is weird now, but fine in a singleton
+ // Switch to singleton design or find other way to break out of thread loop
+ bool fRunning;
pthread_t fThread;
int fPipe[2]; // acts as a Message queue, read from [0] write to [1]
};
+
+#endif
diff --git a/platform_tools/android/apps/androidkitdemo/src/main/java/org/skia/androidkitdemo1/MainActivity.java b/platform_tools/android/apps/androidkitdemo/src/main/java/org/skia/androidkitdemo1/MainActivity.java
index b30db9c..94ab98e 100644
--- a/platform_tools/android/apps/androidkitdemo/src/main/java/org/skia/androidkitdemo1/MainActivity.java
+++ b/platform_tools/android/apps/androidkitdemo/src/main/java/org/skia/androidkitdemo1/MainActivity.java
@@ -17,6 +17,8 @@
import org.skia.androidkit.*;
public class MainActivity extends Activity implements SurfaceHolder.Callback {
+ public Surface surfaceSurface;
+
static {
System.loadLibrary("androidkit");
}
@@ -51,19 +53,19 @@
image.setImageBitmap(bmp);
//Surface
- Surface surfaceSurface;
SurfaceView surfaceView = findViewById(R.id.surface);
surfaceView.getHolder().addCallback(this);
}
@Override
public void surfaceCreated(@NonNull SurfaceHolder holder) {
- Surface surfaceSurface = Surface.createThreadedSurface(holder.getSurface());
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
-
+ surfaceSurface = Surface.createThreadedSurface(holder.getSurface());
+ surfaceSurface.getCanvas().drawColor(0xffffffe0);
+ surfaceSurface.flushAndSubmit();
}
@Override