Files
filament/android/filament-android/src/main/cpp/Stream.cpp
Philip Rideout d85f8164c0 Stream API: add support for acquire / release semantics.
Previously, Streams had two modes (native and texid), this adds a third
mode called "acquired", which allows for copy-free synchronized external
textures in OpenGL and paves the way for Vulkan.

The native mode is now deprecated but texid mode needs to stay around
until all clients can be upgraded.

In an early prototype, this functionality was added directly into
Texture but required quite a bit of additional state tracking, so the
Stream API seemed like a better fit.

This API is probably not necessary for Metal due to Metal's shared
ownership semantics.

This has been tested with a new Android sample that will be added in a
subsequent PR.
2019-11-15 08:40:41 -08:00

214 lines
7.1 KiB
C++

/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <assert.h>
#include <jni.h>
#include <filament/Stream.h>
#include <backend/PixelBufferDescriptor.h>
#include "common/NioUtils.h"
#include "common/CallbackUtils.h"
#ifdef ANDROID
#if __has_include(<android/hardware_buffer_jni.h>)
#include <android/hardware_buffer_jni.h>
#else
struct AHardwareBuffer;
typedef struct AHardwareBuffer AHardwareBuffer;
#endif
#include <android/log.h>
#include <dlfcn.h>
template <typename T>
static void loadSymbol(T*& pfn, const char *symbol) noexcept {
pfn = (T*)dlsym(RTLD_DEFAULT, symbol);
}
#endif
using namespace filament;
using namespace backend;
class StreamBuilder {
public:
StreamBuilder() noexcept {
mBuilder = new Stream::Builder{};
}
~StreamBuilder() {
assert(mStreamSource == nullptr);
delete mBuilder;
}
Stream::Builder* builder() const noexcept { return mBuilder; }
void setStreamSource(JNIEnv* env, jobject streamSource) noexcept {
mStreamSource = env->NewGlobalRef(streamSource);
mBuilder->stream(mStreamSource);
}
void cleanup(JNIEnv* env) {
// This will be invoked by the GC thread, which may not have
// the same JNIEnv
if (mStreamSource) {
env->DeleteGlobalRef(mStreamSource);
mStreamSource = nullptr;
}
}
private:
Stream::Builder* mBuilder = nullptr;
jobject mStreamSource = nullptr;
};
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_Stream_nCreateBuilder(JNIEnv*, jclass) {
return (jlong) new StreamBuilder{};
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Stream_nDestroyBuilder(JNIEnv* env, jclass,
jlong nativeStreamBuilder) {
StreamBuilder* builder = (StreamBuilder*) nativeStreamBuilder;
builder->cleanup(env);
delete builder;
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Stream_nBuilderStreamSource(JNIEnv* env,
jclass, jlong nativeStreamBuilder, jobject streamSource) {
StreamBuilder* builder = (StreamBuilder*) nativeStreamBuilder;
builder->setStreamSource(env, streamSource);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Stream_nBuilderStream(JNIEnv*, jclass,
jlong nativeStreamBuilder, jlong externalTextureId) {
StreamBuilder* builder = (StreamBuilder*) nativeStreamBuilder;
builder->builder()->stream(externalTextureId);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Stream_nBuilderWidth(JNIEnv*, jclass,
jlong nativeStreamBuilder, jint width) {
StreamBuilder* builder = (StreamBuilder*) nativeStreamBuilder;
builder->builder()->width((uint32_t) width);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Stream_nBuilderHeight(JNIEnv*, jclass,
jlong nativeStreamBuilder, jint height) {
StreamBuilder* builder = (StreamBuilder*) nativeStreamBuilder;
builder->builder()->height((uint32_t) height);
}
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_Stream_nBuilderBuild(JNIEnv*, jclass,
jlong nativeStreamBuilder, jlong nativeEngine) {
StreamBuilder* builder = (StreamBuilder*) nativeStreamBuilder;
Engine* engine = (Engine*) nativeEngine;
return (jlong) builder->builder()->build(*engine);
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Stream_nGetStreamType(JNIEnv*, jclass, jlong nativeStream) {
Stream* stream = (Stream*) nativeStream;
return (jint) stream->getStreamType();
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Stream_nSetDimensions(JNIEnv*, jclass, jlong nativeStream,
jint width, jint height) {
Stream* stream = (Stream*) nativeStream;
stream->setDimensions((uint32_t) width, (uint32_t) height);
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Stream_nReadPixels(JNIEnv *env, jclass,
jlong nativeStream, jlong nativeEngine,
jint xoffset, jint yoffset, jint width, jint height,
jobject storage, jint remaining,
jint left, jint top, jint type, jint alignment, jint stride, jint format,
jobject handler, jobject runnable) {
Stream *stream = (Stream *) nativeStream;
Engine *engine = (Engine *) nativeEngine;
stride = stride ? stride : width;
size_t sizeInBytes = PixelBufferDescriptor::computeDataSize(
(PixelDataFormat) format, (PixelDataType) type,
(size_t) stride, (size_t) (height + top), (size_t) alignment);
AutoBuffer nioBuffer(env, storage, 0);
if (sizeInBytes > (remaining << nioBuffer.getShift())) {
// BufferOverflowException
return -1;
}
void *buffer = nioBuffer.getData();
auto *callback = JniBufferCallback::make(engine, env, handler, runnable, std::move(nioBuffer));
PixelBufferDescriptor desc(buffer, sizeInBytes, (backend::PixelDataFormat) format,
(backend::PixelDataType) type, (uint8_t) alignment, (uint32_t) left, (uint32_t) top,
(uint32_t) stride, &JniBufferCallback::invoke, callback);
stream->readPixels(uint32_t(xoffset), uint32_t(yoffset), uint32_t(width), uint32_t(height),
std::move(desc));
return 0;
}
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_Stream_nGetTimestamp(JNIEnv*, jclass, jlong nativeStream) {
Stream *stream = (Stream *) nativeStream;
return stream->getTimestamp();
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Stream_nSetAcquiredImage(JNIEnv* env, jclass, jlong nativeStream,
jlong nativeEngine, jobject hwbuffer, jobject handler, jobject runnable) {
Engine* engine = (Engine*) nativeEngine;
Stream* stream = (Stream*) nativeStream;
#ifdef ANDROID
AHardwareBuffer* (*AHardwareBuffer_fromHardwareBuffer)(JNIEnv*, jobject);
loadSymbol(AHardwareBuffer_fromHardwareBuffer, "AHardwareBuffer_fromHardwareBuffer");
AHardwareBuffer* nativeBuffer = AHardwareBuffer_fromHardwareBuffer(env, hwbuffer);
if (!nativeBuffer) {
__android_log_print(ANDROID_LOG_INFO, "Filament", "Unable to obtain native HardwareBuffer.");
return;
}
auto* callback = JniImageCallback::make(engine, env, handler, runnable, (long) nativeBuffer);
#else
// TODO: for non-Android platforms, it is unclear how to go from "jobject" to "void*"
// For now this code is reserved for future use.
auto* callback = JniImageCallback::make(engine, env, handler, runnable, 0);
void* nativeBuffer = nullptr;
#endif
stream->setAcquiredImage((void*) nativeBuffer, &JniImageCallback::invoke, callback);
}