Remove deprecated Stream APIs

This commit is contained in:
Mathias Agopian
2022-04-14 12:51:03 -07:00
committed by Mathias Agopian
parent 68da1a25eb
commit dab02e915e
31 changed files with 11 additions and 1347 deletions
+2
View File
@@ -7,6 +7,8 @@ A new header is inserted each time a *tag* is created.
## v1.21.2
- remove deprecated `Stream` APIs, see `Texture::import()` for an alternative [⚠️ **API Change**].
## v1.21.1
- engine: Allow both screen-space refraction and screen-space reflections on the same object [⚠️ **Material breakage**].
@@ -98,16 +98,6 @@ Java_com_google_android_filament_Stream_nBuilderStreamSource(JNIEnv* env,
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;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
builder->builder()->stream(externalTextureId);
#pragma clang diagnostic pop
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_Stream_nBuilderWidth(JNIEnv*, jclass,
jlong nativeStreamBuilder, jint width) {
@@ -143,41 +133,6 @@ Java_com_google_android_filament_Stream_nSetDimensions(JNIEnv*, jclass, jlong na
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,
callback->getHandler(), &JniBufferCallback::postToJavaAndDestroy, 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;
@@ -103,9 +103,6 @@ public class Stream {
/** Not synchronized but copy-free. Good for video. */
NATIVE,
/** Synchronized, but GL-only and incurs copies. Good for AR on devices before API 26. */
TEXTURE_ID,
/** Synchronized, copy-free, and take a release callback. Good for AR but requires API 26+. */
ACQUIRED,
};
@@ -121,7 +118,7 @@ public class Stream {
* By default, Stream objects are {@link StreamType#ACQUIRED ACQUIRED} and must have external images pushed to them via
* {@link #setAcquiredImage}.
*
* To create a {@link StreamType#NATIVE NATIVE} or {@link StreamType#TEXTURE_ID TEXTURE_ID} stream, call one of the <pre>stream</pre> methods
* To create a {@link StreamType#NATIVE NATIVE} stream, call one of the <pre>stream</pre> methods
* on the builder.
*/
public static class Builder {
@@ -156,27 +153,6 @@ public class Stream {
throw new IllegalArgumentException("Invalid stream source: " + streamSource);
}
/**
* Creates a {@link StreamType#TEXTURE_ID TEXTURE_ID} stream. A copy stream will sample data from the supplied
* external texture and copy it into an internal private texture.
*
* <p>Currently only OpenGL external texture ids are supported.</p>
*
* @param externalTextureId An opaque texture id (typically a GLuint created with
* <code>glGenTextures()</code>) in a context shared with
* filament -- in that case this texture's target must be
* <code>GL_TEXTURE_EXTERNAL_OES.</code>
* @return This Builder, for chaining calls.
* @see Texture#setExternalStream
* @deprecated this method existed only for ARCore which doesn't need this anymore, use {@link Texture.Builder#importTexture(long)} instead.
*/
@Deprecated
@NonNull
public Builder stream(long externalTextureId) {
nBuilderStream(mNativeBuilder, externalTextureId);
return this;
}
/**
* @param width initial width of the incoming stream. Whether this value is used is
* stream dependent. On Android, it must be set when using
@@ -276,92 +252,6 @@ public class Stream {
nSetDimensions(getNativeObject(), width, height);
}
/**
* Reads back the content of the last frame of a <code>Stream</code> since the last call to
* {@link Renderer#beginFrame}.
*
* <p>The Stream must be a copy stream, which can be checked with {@link #getStreamType()}.
* This function is a no-op otherwise.</p>
*
* <pre>
*
* Stream buffer User buffer (PixelBufferDescriptor)
* +--------------------+
* | | .stride .alignment
* | | ----------------------->-->
* | | O----------------------+--+ low addresses
* | | | | | |
* | w | | | .top | |
* | <---------> | | V | |
* | +---------+ | | +---------+ | |
* | | ^ | | ======> | | | | |
* | x | h| | | |.left| | | |
* +------>| v | | +---->| | | |
* | +.........+ | | +.........+ | |
* | ^ | | | |
* | y | | +----------------------+--+ high addresses
* O------------+-------+
*
* </pre>
*
* <p>Typically readPixels() will be called after {@link Renderer#beginFrame}.</p>
*
* <p>After calling this method, the callback associated with <code>buffer</code>
* will be invoked on the main thread, indicating that the read-back has completed.
* Typically, this will happen after multiple calls to {@link Renderer#beginFrame},
* {@link Renderer#render}, {@link Renderer#endFrame}.</p>
*
* <p><code>readPixels</code> is intended for debugging and testing.
* It will impact performance significantly.</p>
*
* @param xoffset left offset of the sub-region to read back
* @param yoffset bottom offset of the sub-region to read back
* @param width width of the sub-region to read back
* @param height height of the sub-region to read back
* @param buffer client-side buffer where the read-back will be written
*
* <p>
* The following format are always supported:
* <li>{@link Texture.Format#RGBA}</li>
* <li>{@link Texture.Format#RGBA_INTEGER}</li>
* </p>
*
* <p>
* The following types are always supported:
* <li>{@link Texture.Type#UBYTE}</li>
* <li>{@link Texture.Type#UINT}</li>
* <li>{@link Texture.Type#INT}</li>
* <li>{@link Texture.Type#FLOAT}</li>
* </p>
*
* <p>Other combination of format/type may be supported. If a combination is
* not supported, this operation may fail silently. Use a DEBUG build
* to get some logs about the failure.</p>
*
* @exception BufferOverflowException if the specified parameters would result in reading
* outside of <code>buffer</code>.
*/
public void readPixels(
@IntRange(from = 0) int xoffset, @IntRange(from = 0) int yoffset,
@IntRange(from = 0) int width, @IntRange(from = 0) int height,
@NonNull Texture.PixelBufferDescriptor buffer) {
if (buffer.storage.isReadOnly()) {
throw new ReadOnlyBufferException();
}
int result = nReadPixels(getNativeObject(), mNativeEngine,
xoffset, yoffset, width, height,
buffer.storage, buffer.storage.remaining(),
buffer.left, buffer.top, buffer.type.ordinal(), buffer.alignment,
buffer.stride, buffer.format.ordinal(),
buffer.handler, buffer.callback);
if (result < 0) {
throw new BufferOverflowException();
}
}
/**
* Returns the presentation time of the currently displayed frame in nanosecond.
*
@@ -387,7 +277,6 @@ public class Stream {
private static native long nCreateBuilder();
private static native void nDestroyBuilder(long nativeStreamBuilder);
private static native void nBuilderStreamSource(long nativeStreamBuilder, Object streamSource);
private static native void nBuilderStream(long nativeStreamBuilder, long externalTextureId);
private static native void nBuilderWidth(long nativeStreamBuilder, int width);
private static native void nBuilderHeight(long nativeStreamBuilder, int height);
private static native long nBuilderBuild(long nativeStreamBuilder, long nativeEngine);
@@ -79,8 +79,6 @@ class MainActivity : Activity(), ActivityCompat.OnRequestPermissionsResultCallba
// Performs the rendering and schedules new frames
private val frameScheduler = FrameCallback()
private var externalTextureID: Int = 0
@RequiresApi(30)
class Api30Impl {
companion object {
@@ -103,8 +101,6 @@ class MainActivity : Activity(), ActivityCompat.OnRequestPermissionsResultCallba
setupView()
setupScene()
externalTextureID = createExternalTexture()
@Suppress("deprecation")
val display = if (Build.VERSION.SDK_INT >= 30) {
Api30Impl.getDisplay(this)
@@ -112,7 +108,7 @@ class MainActivity : Activity(), ActivityCompat.OnRequestPermissionsResultCallba
windowManager.defaultDisplay!!
}
streamHelper = StreamHelper(engine, materialInstance, display, externalTextureID)
streamHelper = StreamHelper(engine, materialInstance, display)
this.title = streamHelper.getTestName()
}
@@ -445,31 +441,4 @@ class MainActivity : Activity(), ActivityCompat.OnRequestPermissionsResultCallba
check(EGL14.eglMakeCurrent(display, surface, surface, context)) { "Error making GL context." }
return context
}
private fun createExternalTexture(): Int {
val textures = IntArray(1)
GLES30.glGenTextures(1, textures, 0)
val result = textures[0]
val textureTarget = GLES11Ext.GL_TEXTURE_EXTERNAL_OES
GLES30.glBindTexture(textureTarget, result)
GLES30.glTexParameteri(textureTarget, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_CLAMP_TO_EDGE)
GLES30.glTexParameteri(textureTarget, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_CLAMP_TO_EDGE)
GLES30.glTexParameteri(textureTarget, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_NEAREST)
GLES30.glTexParameteri(textureTarget, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_NEAREST)
if (!GLES30.glIsTexture(result)) {
throw RuntimeException("OpenGL error: $result is an invalid texture.")
}
val error = GLES30.glGetError()
if (error != GLES30.GL_NO_ERROR) {
val errorString = GLU.gluErrorString(error)
throw RuntimeException("OpenGL error: $errorString!")
}
return result
}
}
@@ -37,7 +37,6 @@ class StreamHelper(
private val filamentEngine: Engine,
private val filamentMaterial: MaterialInstance,
private val display: Display,
private val externalTextureId: Int
) {
/**
* The StreamSource configures the source data for the texture.
@@ -51,7 +50,6 @@ class StreamHelper(
*/
enum class StreamSource {
CANVAS_STREAM_NATIVE, // copy-free but does not guarantee synchronization
CANVAS_STREAM_TEXID, // synchronized but incurs a copy
CANVAS_STREAM_ACQUIRED, // synchronized and copy-free
}
@@ -127,10 +125,6 @@ class StreamHelper(
surface.unlockCanvasAndPost(canvas)
if (streamSource == StreamSource.CANVAS_STREAM_TEXID) {
surfaceTexture!!.updateTexImage()
}
if (streamSource == StreamSource.CANVAS_STREAM_ACQUIRED) {
val image = imageReader!!.acquireLatestImage()
filamentStream!!.setAcquiredImage(
@@ -146,7 +140,7 @@ class StreamHelper(
fun nextTest() {
stopTest()
streamSource = StreamSource.values()[(streamSource.ordinal + 1) % 3]
streamSource = StreamSource.values()[(streamSource.ordinal + 1) % StreamSource.values().size]
startTest()
}
@@ -213,23 +207,6 @@ class StreamHelper(
filamentTexture.setExternalStream(filamentEngine, filamentStream!!)
}
if (streamSource == StreamSource.CANVAS_STREAM_TEXID) {
// Create the Android surface that will hold the canvas image.
surfaceTexture = SurfaceTexture(externalTextureId)
surfaceTexture!!.setDefaultBufferSize(resolution.width, resolution.height)
canvasSurface = Surface(surfaceTexture)
// Create the Filament Stream object that gets bound to the Texture.
filamentStream = Stream.Builder()
.stream(externalTextureId.toLong())
.width(resolution.width)
.height(resolution.height)
.build(filamentEngine)
filamentTexture.setExternalStream(filamentEngine, filamentStream!!)
}
if (streamSource == StreamSource.CANVAS_STREAM_ACQUIRED) {
filamentStream = Stream.Builder()
.width(resolution.width)
-3
View File
@@ -65,8 +65,6 @@ if (FILAMENT_SUPPORTS_OPENGL AND NOT FILAMENT_USE_EXTERNAL_GLES3 AND NOT FILAMEN
src/opengl/gl_headers.h
src/opengl/GLUtils.cpp
src/opengl/GLUtils.h
src/opengl/OpenGLBlitter.cpp
src/opengl/OpenGLBlitter.h
src/opengl/OpenGLContext.cpp
src/opengl/OpenGLContext.h
src/opengl/OpenGLDriver.cpp
@@ -89,7 +87,6 @@ if (FILAMENT_SUPPORTS_OPENGL AND NOT FILAMENT_USE_EXTERNAL_GLES3 AND NOT FILAMEN
endif ()
if (ANDROID)
list(APPEND SRCS src/opengl/platforms/ExternalStreamManagerAndroid.cpp)
list(APPEND SRCS src/android/ExternalTextureManagerAndroid.cpp)
list(APPEND SRCS src/opengl/platforms/PlatformEGLAndroid.cpp)
elseif (IOS)
list(APPEND SRCS src/opengl/platforms/PlatformCocoaTouchGL.mm)
@@ -788,7 +788,6 @@ enum class BlendFunction : uint8_t {
//! Stream for external textures
enum class StreamType {
NATIVE, //!< Not synchronized but copy-free. Good for video.
TEXTURE_ID, //!< Synchronized, but GL-only and incurs copies. Good for AR on devices before API 26.
ACQUIRED, //!< Synchronized, copy-free, and take a release callback. Good for AR but requires API 26+.
};
@@ -246,11 +246,6 @@ DECL_DRIVER_API_R_N(backend::SwapChainHandle, createSwapChainHeadless,
uint32_t, height,
uint64_t, flags)
DECL_DRIVER_API_R_N(backend::StreamHandle, createStreamFromTextureId,
intptr_t, externalTextureId,
uint32_t, width,
uint32_t, height)
DECL_DRIVER_API_R_0(backend::TimerQueryHandle, createTimerQuery)
@@ -461,14 +456,6 @@ DECL_DRIVER_API_N(readPixels,
uint32_t, height,
backend::PixelBufferDescriptor&&, data)
DECL_DRIVER_API_N(readStreamPixels,
backend::StreamHandle, sh,
uint32_t, x,
uint32_t, y,
uint32_t, width,
uint32_t, height,
backend::PixelBufferDescriptor&&, data)
/*
* Rendering operations
* --------------------
@@ -82,15 +82,6 @@ public:
virtual void detach(Stream* stream) noexcept = 0;
virtual void updateTexImage(Stream* stream, int64_t* timestamp) noexcept = 0;
// external texture storage
virtual ExternalTexture* createExternalTextureStorage() noexcept = 0;
// this is called synchronously in the application thread (NOT the Driver thread)
virtual void reallocateExternalStorage(ExternalTexture* ets,
uint32_t w, uint32_t h, TextureFormat format) noexcept = 0;
virtual void destroyExternalTextureStorage(ExternalTexture* ets) noexcept = 0;
// The method allows platforms to convert a user-supplied external image object into a new type
// (e.g. HardwareBuffer => EGLImage). It makes sense for the default implementation to do nothing.
virtual AcquiredImage transformAcquiredImage(AcquiredImage source) noexcept { return source; }
@@ -1,222 +0,0 @@
/*
* Copyright (C) 2018 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 "ExternalTextureManagerAndroid.h"
#include <utils/api_level.h>
#include <utils/compiler.h>
#include <utils/Log.h>
#include <dlfcn.h>
using namespace utils;
namespace filament::backend {
template <typename T>
static void loadSymbol(T*& pfn, const char *symbol) noexcept {
pfn = (T*)dlsym(RTLD_DEFAULT, symbol);
}
// ------------------------------------------------------------------------------------------------
namespace ndk {
/*
* This mimics the GraphicBuffer class for pre-API 26
*/
template<typename NATIVE_TYPE, typename REF>
struct ANativeObjectBase : public NATIVE_TYPE, public REF {
};
struct android_native_base_t {
unsigned int magic;
unsigned int version;
void* reserved[4];
void (* incRef)(struct android_native_base_t* base);
void (* decRef)(struct android_native_base_t* base);
};
struct ANativeWindowBuffer {
struct android_native_base_t common;
unsigned int width;
unsigned int height;
unsigned int stride;
unsigned int format;
unsigned int usage_deprecated;
uintptr_t layerCount;
void* reserved[1];
const void* handle;
uint64_t usage;
void* reserved_proc[8 - (sizeof(uint64_t) / sizeof(void*))];
};
struct RefBase {
virtual ~RefBase() = default;
void* ref;
};
struct GraphicBuffer : public ANativeObjectBase<ANativeWindowBuffer, RefBase> {
};
struct GraphicBufferWrapper {
GraphicBuffer* graphicBuffer;
};
} // namespace ndk
// ------------------------------------------------------------------------------------------------
using namespace ndk;
struct EGLExternalTexture : public ExternalTextureManagerAndroid::ExternalTexture {
GraphicBufferWrapper* graphicBufferWrapper = nullptr;
};
ExternalTextureManagerAndroid& ExternalTextureManagerAndroid::create() noexcept {
return *(new ExternalTextureManagerAndroid{});
}
void ExternalTextureManagerAndroid::destroy(ExternalTextureManagerAndroid* pExternalTextureManager) noexcept {
delete pExternalTextureManager;
}
// called on gl thread
ExternalTextureManagerAndroid::ExternalTextureManagerAndroid() noexcept
: mVm(VirtualMachineEnv::get()) {
}
// not quite sure on which thread this is going to be called
ExternalTextureManagerAndroid::~ExternalTextureManagerAndroid() noexcept {
if (__builtin_available(android 26, *)) {
} else {
if (mGraphicBufferClass) {
JNIEnv* env = VirtualMachineEnv::getThreadEnvironment();
if (env) {
env->DeleteGlobalRef(mGraphicBufferClass);
}
}
}
}
// called on gl thread
Platform::ExternalTexture* ExternalTextureManagerAndroid::createExternalTexture() noexcept {
if (__builtin_available(android 26, *)) {
} else {
// initialize java stuff on-demand
if (!mGraphicBufferClass) {
JNIEnv* env = mVm.getEnvironment();
mGraphicBufferClass = env->FindClass("android/view/GraphicBuffer");
mGraphicBuffer_nCreateGraphicBuffer = env->GetStaticMethodID(
mGraphicBufferClass, "nCreateGraphicBuffer", "(IIII)J");
mGraphicBuffer_nDestroyGraphicBuffer = env->GetStaticMethodID(
mGraphicBufferClass, "nDestroyGraphicBuffer", "(J)V");
mGraphicBufferClass = static_cast<jclass>(env->NewGlobalRef(mGraphicBufferClass));
}
}
EGLExternalTexture* ets = new EGLExternalTexture;
return ets;
}
// called on app thread
void ExternalTextureManagerAndroid::reallocate(
Platform::ExternalTexture* ets, uint32_t w, uint32_t h,
TextureFormat format, uint64_t usage) noexcept {
destroyStorage(ets);
alloc(ets, w, h, format, usage);
}
// called on gl thread
void ExternalTextureManagerAndroid::destroy(Platform::ExternalTexture* ets) noexcept {
destroyStorage(ets);
delete static_cast<EGLExternalTexture*>(ets);
}
// called on app thread
void ExternalTextureManagerAndroid::alloc(
Platform::ExternalTexture* t,
uint32_t w, uint32_t h, TextureFormat format, uint64_t usage) noexcept {
EGLExternalTexture* ets = static_cast<EGLExternalTexture*>(t);
AHardwareBuffer_Desc desc = { w, h, 1, 0, usage, 0, 0, 0 };
switch (format) {
case TextureFormat::RGB8:
// don't use R8G8B8 here because some drivers produce garbled images
desc.format = AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM;
break;
case TextureFormat::RGBA8:
desc.format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
break;
case TextureFormat::RGB565:
desc.format = AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM;
break;
case TextureFormat::RGB10_A2:
desc.format = AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM;
break;
default:
slog.e << "Unsupported format " << (int)format << ", use RGBA8 or RGB8 only."
<< io::endl;
return;
}
if (__builtin_available(android 26, *)) {
// allocate new storage...
AHardwareBuffer* buffer = nullptr;
if (AHardwareBuffer_allocate(&desc, &buffer) < 0) {
slog.e << "AHardwareBuffer_allocate() failed" << io::endl;
return;
}
ets->hardwareBuffer = buffer;
} else {
// note: This is called on the application thread (not the GL thread)
JNIEnv* env = VirtualMachineEnv::getThreadEnvironment();
if (!env) {
return; // this should not happen
}
jlong graphicBufferWrapperJni = env->CallStaticLongMethod(mGraphicBufferClass,
mGraphicBuffer_nCreateGraphicBuffer, w, h, desc.format, usage);
VirtualMachineEnv::handleException(env);
if (graphicBufferWrapperJni) {
GraphicBuffer* const gb = ((GraphicBufferWrapper*)graphicBufferWrapperJni)->graphicBuffer;
ets->graphicBufferWrapper = (GraphicBufferWrapper*)graphicBufferWrapperJni;
ets->clientBuffer = static_cast<ANativeWindowBuffer*>(gb);
}
}
}
// called on gl thread
void ExternalTextureManagerAndroid::destroyStorage(Platform::ExternalTexture* t) noexcept {
EGLExternalTexture* ets = static_cast<EGLExternalTexture*>(t);
if (__builtin_available(android 26, *)) {
// destroy the current storage if any
if (ets->hardwareBuffer) {
AHardwareBuffer_release(ets->hardwareBuffer);
ets->hardwareBuffer = nullptr;
}
} else {
if (ets->graphicBufferWrapper) {
JNIEnv* env = VirtualMachineEnv::get().getEnvironment();
env->CallStaticVoidMethod(mGraphicBufferClass,
mGraphicBuffer_nDestroyGraphicBuffer, (jlong)ets->graphicBufferWrapper);
VirtualMachineEnv::handleException(env);
ets->graphicBufferWrapper = nullptr;
}
}
}
} // namespace filament::backend
@@ -1,84 +0,0 @@
/*
* Copyright (C) 2018 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.
*/
#ifndef TNT_FILAMENT_DRIVER_ANDROID_EXTERNAL_TEXTURE_MANAGER_ANDROID_H
#define TNT_FILAMENT_DRIVER_ANDROID_EXTERNAL_TEXTURE_MANAGER_ANDROID_H
#include "private/backend/VirtualMachineEnv.h"
#include <backend/DriverEnums.h>
#include <backend/Platform.h>
#include <android/api-level.h>
#include <android/hardware_buffer.h>
namespace filament::backend {
/*
* ExternalTextureManagerAndroid::ExternalTexture is basically a wrapper for AHardwareBuffer.
*
* This class doesn't rely on GL or EGL, and could be used for other Android platform if needed
* (e.g. Vulkan).
*
* ExternalTextureManagerAndroid handle allocation/destruction using either Java or the NDK,
* whichever is available.
*/
class ExternalTextureManagerAndroid {
public:
struct ExternalTexture : public Platform::ExternalTexture {
void* clientBuffer = nullptr;
AHardwareBuffer* hardwareBuffer = nullptr;
};
// must be called on backend thread
static ExternalTextureManagerAndroid& create() noexcept;
// must be called on backend thread
static void destroy(ExternalTextureManagerAndroid* pExternalTextureManager) noexcept;
// must be called on backend thread (only because we don't synchronize
Platform::ExternalTexture* createExternalTexture() noexcept;
// called on app thread
void reallocate(
Platform::ExternalTexture* ets, uint32_t w, uint32_t h,
TextureFormat format, uint64_t usage) noexcept;
// must be called on backend thread
void destroy(Platform::ExternalTexture* ets) noexcept;
private:
ExternalTextureManagerAndroid() noexcept;
~ExternalTextureManagerAndroid() noexcept;
// called on app thread
void alloc(Platform::ExternalTexture* ets,
uint32_t w, uint32_t h, TextureFormat format, uint64_t usage) noexcept;
// called on gl thread
void destroyStorage(Platform::ExternalTexture* ets) noexcept;
VirtualMachineEnv& mVm;
jclass mGraphicBufferClass = nullptr;
jmethodID mGraphicBuffer_nCreateGraphicBuffer = nullptr;
jmethodID mGraphicBuffer_nDestroyGraphicBuffer = nullptr;
};
} // namespace filament::backend
#endif // TNT_FILAMENT_DRIVER_ANDROID_EXTERNAL_TEXTURE_MANAGER_ANDROID_H
-13
View File
@@ -359,10 +359,6 @@ void MetalDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch,
construct_handle<MetalSwapChain>(sch, *mContext, width, height, flags);
}
void MetalDriver::createStreamFromTextureIdR(Handle<HwStream>, intptr_t externalTextureId,
uint32_t width, uint32_t height) {
}
void MetalDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {
// nothing to do, timer query was constructed in createTimerQueryS
}
@@ -431,10 +427,6 @@ Handle<HwSwapChain> MetalDriver::createSwapChainHeadlessS() noexcept {
return alloc_handle<MetalSwapChain>();
}
Handle<HwStream> MetalDriver::createStreamFromTextureIdS() noexcept {
return {};
}
Handle<HwTimerQuery> MetalDriver::createTimerQueryS() noexcept {
// The handle must be constructed here, as a synchronous call to getTimerQueryValue might happen
// before createTimerQueryR is executed.
@@ -1053,11 +1045,6 @@ void MetalDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y,
}];
}
void MetalDriver::readStreamPixels(Handle<HwStream> sh, uint32_t x, uint32_t y, uint32_t width,
uint32_t height, PixelBufferDescriptor&& data) {
}
void MetalDriver::blit(TargetBufferFlags buffers,
Handle<HwRenderTarget> dst, Viewport dstRect,
Handle<HwRenderTarget> src, Viewport srcRect,
-5
View File
@@ -308,11 +308,6 @@ void NoopDriver::readPixels(Handle<HwRenderTarget> src,
scheduleDestroy(std::move(p));
}
void NoopDriver::readStreamPixels(Handle<HwStream> sh, uint32_t x, uint32_t y, uint32_t width,
uint32_t height, PixelBufferDescriptor&& p) {
scheduleDestroy(std::move(p));
}
void NoopDriver::blit(TargetBufferFlags buffers,
Handle<HwRenderTarget> dst, Viewport dstRect,
Handle<HwRenderTarget> src, Viewport srcRect,
@@ -1,215 +0,0 @@
/*
* 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 "OpenGLBlitter.h"
#include "GLUtils.h"
#include "OpenGLContext.h"
#include <utils/compiler.h>
#include <utils/Log.h>
#include <math/vec2.h>
using namespace filament::math;
using namespace utils;
namespace filament::backend {
static const char s_vertexES[] = R"SHADER(#version 300 es
in vec4 pos;
void main() {
gl_Position = pos;
}
)SHADER";
static const char s_vertexGL[] = R"SHADER(#version 410 core
in vec4 pos;
void main() {
gl_Position = pos;
}
)SHADER";
static const char s_fragmentES[] = R"SHADER(#version 300 es
#extension GL_OES_EGL_image_external_essl3 : enable
precision mediump float;
uniform samplerExternalOES sampler;
out vec4 fragColor;
void main() {
fragColor = texelFetch(sampler, ivec2(gl_FragCoord.xy), 0);
}
)SHADER";
static const char s_fragmentGL[] = R"SHADER(#version 410 core
precision mediump float;
uniform sampler2D sampler;
out vec4 fragColor;
void main() {
fragColor = texelFetch(sampler, ivec2(gl_FragCoord.xy), 0);
}
)SHADER";
void OpenGLBlitter::init() noexcept {
glGenSamplers(1, &mSampler);
glSamplerParameteri(mSampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glSamplerParameteri(mSampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glSamplerParameteri(mSampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glSamplerParameteri(mSampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glSamplerParameteri(mSampler, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
GLint status;
char const* vsource[2] = { s_vertexES, s_vertexGL };
char const* fsource[2] = { s_fragmentES, s_fragmentGL };
const size_t index = BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GLES ? 0 : 1;
mVertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(mVertexShader, 1, vsource + index, nullptr);
glCompileShader(mVertexShader);
glGetShaderiv(mVertexShader, GL_COMPILE_STATUS, &status);
assert_invariant(status == GL_TRUE);
mFragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(mFragmentShader, 1, fsource + index, nullptr);
glCompileShader(mFragmentShader);
glGetShaderiv(mFragmentShader, GL_COMPILE_STATUS, &status);
assert_invariant(status == GL_TRUE);
mProgram = glCreateProgram();
glAttachShader(mProgram, mVertexShader);
glAttachShader(mProgram, mFragmentShader);
glLinkProgram(mProgram);
glGetProgramiv(mProgram, GL_LINK_STATUS, &status);
assert_invariant(status == GL_TRUE);
glUseProgram(mProgram);
GLint loc = glGetUniformLocation(mProgram, "sampler");
GLuint tmu = 0;
glUniform1i(loc, tmu);
glGenFramebuffers(1, &mFBO);
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLBlitter::terminate() noexcept {
glDeleteSamplers(1, &mSampler);
glDetachShader(mProgram, mVertexShader);
glDetachShader(mProgram, mFragmentShader);
glDeleteShader(mVertexShader);
glDeleteShader(mFragmentShader);
glDeleteProgram(mProgram);
glDeleteFramebuffers(1, &mFBO);
}
void OpenGLBlitter::blit(GLuint srcTextureExternal, GLuint dstTexture2d, GLuint w, GLuint h) noexcept {
const float2 vtx[3] = {{ -1.0f, 3.0f },
{ -1.0f, -1.0f },
{ 3.0f, -1.0f }};
// we're using tmu 0 as the source texture
GLuint tmu = 0;
// source texture
glBindSampler(tmu, mSampler);
glBindTexture(GL_TEXTURE_EXTERNAL_OES, srcTextureExternal);
CHECK_GL_ERROR(utils::slog.e)
// destination texture
glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dstTexture2d, 0);
CHECK_GL_ERROR(utils::slog.e)
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_FRAMEBUFFER)
// geometry
glBindBuffer(GL_ARRAY_BUFFER, 0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, vtx);
// blit...
glViewport(0, 0, w, h);
glUseProgram(mProgram);
glDrawArrays(GL_TRIANGLES, 0, 3);
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLBlitter::State::save() noexcept {
mHasState = true;
// TODO: technically we should also save glVertexAttribPointer
GLuint tmu = 0;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &framebuffer);
glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &array);
glGetIntegerv(GL_CURRENT_PROGRAM, &program);
glGetIntegerv(GL_VIEWPORT, viewport);
glGetIntegerv(GL_COLOR_WRITEMASK, writeMask);
scissorTest = glIsEnabled(GL_SCISSOR_TEST);
stencilTest = glIsEnabled(GL_STENCIL_TEST);
cullFace = glIsEnabled(GL_CULL_FACE);
glGetVertexAttribiv(0, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &vertexAttrib);
// save the current active texture first
glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTexture);
// we're using tmu 0 as the source texture
glActiveTexture(GL_TEXTURE0 + tmu);
// save what depends on glActiveTexture
glGetIntegerv(GL_SAMPLER_BINDING, &sampler);
glGetIntegerv(GL_TEXTURE_BINDING_2D, &texture);
/*
* Set the state
*/
// we're using tmu 0 as the source texture
glDisable(GL_SCISSOR_TEST);
glDisable(GL_STENCIL_TEST);
glDisable(GL_CULL_FACE);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLBlitter::State::restore() noexcept {
GLuint tmu = 0;
glColorMask(
(GLboolean)writeMask[0],
(GLboolean)writeMask[1],
(GLboolean)writeMask[2],
(GLboolean)writeMask[3]);
if (cullFace) {
glEnable(GL_CULL_FACE);
}
if (stencilTest) {
glEnable(GL_STENCIL_TEST);
}
if (scissorTest) {
glEnable(GL_SCISSOR_TEST);
}
glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
glUseProgram(static_cast<GLuint>(program));
glBindBuffer(GL_ARRAY_BUFFER, static_cast<GLuint>(array));
glBindFramebuffer(GL_FRAMEBUFFER, static_cast<GLuint>(framebuffer));
if (!vertexAttrib) {
glDisableVertexAttribArray(0);
}
glBindTexture(GL_TEXTURE_2D, static_cast<GLuint>(texture));
glBindSampler(tmu, static_cast<GLuint>(sampler));
glActiveTexture(static_cast<GLenum>(activeTexture));
CHECK_GL_ERROR(utils::slog.e)
}
} // namespace filament::backend
@@ -1,67 +0,0 @@
/*
* 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.
*/
#ifndef TNT_FILAMENT_BACKEND_OPENGL_OPENGLBLITTER_H
#define TNT_FILAMENT_BACKEND_OPENGL_OPENGLBLITTER_H
#include <utils/compiler.h>
#include "gl_headers.h"
namespace filament::backend {
class OpenGLContext;
class OpenGLBlitter {
public:
explicit OpenGLBlitter(OpenGLContext& context) noexcept : mContext(context) {}
void init() noexcept;
void terminate() noexcept;
void blit(GLuint srcTextureExternal, GLuint dstTexture2d, GLuint w, GLuint h) noexcept;
class State {
bool mHasState = false;
GLint activeTexture, sampler, texture, framebuffer, array, vertexAttrib, program;
GLboolean stencilTest, scissorTest, cullFace;
GLint viewport[4], writeMask[4];
void save() noexcept;
void restore() noexcept;
public:
void setup() noexcept {
if (UTILS_UNLIKELY(!mHasState)) {
save();
}
}
~State() noexcept {
if (UTILS_UNLIKELY(mHasState)) {
restore();
}
}
};
private:
UTILS_UNUSED OpenGLContext& mContext;
GLuint mSampler{};
GLuint mVertexShader{};
GLuint mFragmentShader{};
GLuint mProgram{};
GLuint mFBO{};
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_OPENGL_OPENGLBLITTER_H
@@ -20,7 +20,6 @@
#include "private/backend/OpenGLPlatform.h"
#include "CommandStreamDispatcher.h"
#include "OpenGLBlitter.h"
#include "OpenGLDriverFactory.h"
#include "OpenGLProgram.h"
#include "OpenGLTimerQuery.h"
@@ -168,13 +167,6 @@ OpenGLDriver::OpenGLDriver(OpenGLPlatform* platform) noexcept
slog.i << "OS version: " << mPlatform.getOSVersion() << io::endl;
#endif
// Initialize the blitter only if we have OES_EGL_image_external_essl3
if (mContext.ext.OES_EGL_image_external_essl3) {
mOpenGLBlitter = new OpenGLBlitter(mContext);
mOpenGLBlitter->init();
mContext.resetProgram();
}
if (mContext.ext.EXT_disjoint_timer_query ||
BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GL) {
// timer queries are available
@@ -197,7 +189,6 @@ OpenGLDriver::OpenGLDriver(OpenGLPlatform* platform) noexcept
}
OpenGLDriver::~OpenGLDriver() noexcept {
delete mOpenGLBlitter;
}
Dispatcher OpenGLDriver::getDispatcher() const noexcept {
@@ -223,9 +214,6 @@ void OpenGLDriver::terminate() {
glDeleteSamplers(1, &item.second);
}
mSamplerMap.clear();
if (mOpenGLBlitter) {
mOpenGLBlitter->terminate();
}
delete mTimerQueryImpl;
@@ -381,10 +369,6 @@ Handle<HwSwapChain> OpenGLDriver::createSwapChainHeadlessS() noexcept {
return initHandle<HwSwapChain>();
}
Handle<HwStream> OpenGLDriver::createStreamFromTextureIdS() noexcept {
return initHandle<GLStream>();
}
Handle<HwTimerQuery> OpenGLDriver::createTimerQueryS() noexcept {
return initHandle<GLTimerQuery>();
}
@@ -1137,24 +1121,6 @@ void OpenGLDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch,
sc->swapChain = mPlatform.createSwapChain(width, height, flags);
}
void OpenGLDriver::createStreamFromTextureIdR(Handle<HwStream> sh,
intptr_t externalTextureId, uint32_t width, uint32_t height) {
DEBUG_MARKER()
GLStream* s = handle_cast<GLStream*>(sh);
// It would be better if we could query the externalTextureId size, unfortunately
// this is not supported in GL for GL_TEXTURE_EXTERNAL_OES targets
s->width = width;
s->height = height;
s->gl.externalTextureId = static_cast<GLuint>(externalTextureId);
s->streamType = StreamType::TEXTURE_ID;
glGenTextures(GLStream::ROUND_ROBIN_TEXTURE_COUNT, s->user_thread.read);
glGenTextures(GLStream::ROUND_ROBIN_TEXTURE_COUNT, s->user_thread.write);
for (auto& info : s->user_thread.infos) {
info.ets = mPlatform.createExternalTextureStorage();
}
}
void OpenGLDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {
DEBUG_MARKER()
@@ -1301,15 +1267,6 @@ void OpenGLDriver::destroyStream(Handle<HwStream> sh) {
}
if (s->streamType == StreamType::NATIVE) {
mPlatform.destroyStream(s->stream);
} else if (s->streamType == StreamType::TEXTURE_ID) {
glDeleteTextures(GLStream::ROUND_ROBIN_TEXTURE_COUNT, s->user_thread.read);
glDeleteTextures(GLStream::ROUND_ROBIN_TEXTURE_COUNT, s->user_thread.write);
if (s->gl.fbo) {
glDeleteFramebuffers(1, &s->gl.fbo);
}
for (auto const& info : s->user_thread.infos) {
mPlatform.destroyExternalTextureStorage(info.ets);
}
}
destruct(sh, s);
}
@@ -1368,7 +1325,6 @@ void OpenGLDriver::setAcquiredImage(Handle<HwStream> sh, void* hwbuffer,
void OpenGLDriver::updateStreams(DriverApi* driver) {
if (UTILS_UNLIKELY(!mExternalStreams.empty())) {
OpenGLBlitter::State state;
for (GLTexture* t : mExternalStreams) {
assert_invariant(t);
@@ -1379,11 +1335,6 @@ void OpenGLDriver::updateStreams(DriverApi* driver) {
continue;
}
if (s->streamType == StreamType::TEXTURE_ID) {
state.setup();
updateStreamTexId(t, driver);
}
if (s->streamType == StreamType::ACQUIRED) {
updateStreamAcquired(t, driver);
}
@@ -2080,20 +2031,12 @@ void OpenGLDriver::setExternalStream(Handle<HwTexture> th, Handle<HwStream> sh)
UTILS_NOINLINE
void OpenGLDriver::attachStream(GLTexture* t, GLStream* hwStream) noexcept {
auto& gl = mContext;
mExternalStreams.push_back(t);
switch (hwStream->streamType) {
case StreamType::NATIVE:
mPlatform.attach(hwStream->stream, t->gl.id);
break;
case StreamType::TEXTURE_ID:
assert_invariant(t->target == SamplerType::SAMPLER_EXTERNAL);
// The texture doesn't need a texture name anymore, get rid of it
gl.unbindTexture(t->gl.target, t->gl.id);
glDeleteTextures(1, &t->gl.id);
t->gl.id = hwStream->user_thread.read[hwStream->user_thread.cur];
break;
case StreamType::ACQUIRED:
break;
}
@@ -2115,8 +2058,6 @@ void OpenGLDriver::detachStream(GLTexture* t) noexcept {
mPlatform.detach(t->hwStream->stream);
// ^ this deletes the texture id
break;
case StreamType::TEXTURE_ID:
break;
case StreamType::ACQUIRED:
gl.unbindTexture(t->gl.target, t->gl.id);
glDeleteTextures(1, &t->gl.id);
@@ -2141,7 +2082,6 @@ void OpenGLDriver::replaceStream(GLTexture* texture, GLStream* newStream) noexce
mPlatform.detach(texture->hwStream->stream);
// ^ this deletes the texture id
break;
case StreamType::TEXTURE_ID:
case StreamType::ACQUIRED:
break;
}
@@ -2151,10 +2091,6 @@ void OpenGLDriver::replaceStream(GLTexture* texture, GLStream* newStream) noexce
glGenTextures(1, &texture->gl.id);
mPlatform.attach(newStream->stream, texture->gl.id);
break;
case StreamType::TEXTURE_ID:
assert_invariant(texture->target == SamplerType::SAMPLER_EXTERNAL);
texture->gl.id = newStream->user_thread.read[newStream->user_thread.cur];
break;
case StreamType::ACQUIRED:
// Just re-use the old texture id.
break;
@@ -2524,179 +2460,6 @@ void OpenGLDriver::updateStreamAcquired(GLTexture* gltexture, DriverApi* driver)
});
}
#define DEBUG_NO_EXTERNAL_STREAM_COPY false
void OpenGLDriver::updateStreamTexId(GLTexture* t, DriverApi* driver) noexcept {
SYSTRACE_CALL();
auto& gl = mContext;
GLStream* s = static_cast<GLStream*>(t->hwStream);
assert_invariant(s);
assert_invariant(s->streamType == StreamType::TEXTURE_ID);
// round-robin to the next texture name
if (UTILS_UNLIKELY(DEBUG_NO_EXTERNAL_STREAM_COPY ||
gl.bugs.disable_shared_context_draws || !mOpenGLBlitter)) {
driver->queueCommand([this, t, s]() {
// the stream may have been destroyed since we enqueued the command
// also make sure that this texture is still associated with the same stream
auto& streams = mExternalStreams;
if (UTILS_LIKELY(std::find(streams.begin(), streams.end(), t) != streams.end()) &&
(t->hwStream == s)) {
t->gl.id = s->gl.externalTextureId;
}
});
} else {
s->user_thread.cur = uint8_t(
(s->user_thread.cur + 1) % GLStream::ROUND_ROBIN_TEXTURE_COUNT);
GLuint writeTexture = s->user_thread.write[s->user_thread.cur];
GLuint readTexture = s->user_thread.read[s->user_thread.cur];
// Make sure we're using the proper size
GLStream::Info& info = s->user_thread.infos[s->user_thread.cur];
if (UTILS_UNLIKELY(info.width != s->width || info.height != s->height)) {
// nothing guarantees that this buffer is free (i.e. has been consumed by the
// GL thread), so we could potentially cause a glitch by reallocating the
// texture here. This should be very rare though.
// This could be fixed by always using a new temporary texture here, and
// replacing it in the queueCommand() below. imho, not worth it.
info.width = s->width;
info.height = s->height;
Platform::ExternalTexture* ets = s->user_thread.infos[s->user_thread.cur].ets;
mPlatform.reallocateExternalStorage(ets, info.width, info.height, TextureFormat::RGB8);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, writeTexture);
glBindTexture(GL_TEXTURE_EXTERNAL_OES, readTexture);
#ifdef GL_OES_EGL_image
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)ets->image);
glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, (GLeglImageOES)ets->image);
#endif
}
// copy the texture...
#ifndef NDEBUG
if (t->gl.fence) {
// we're about to overwrite a buffer that hasn't been consumed
slog.d << "OpenGLDriver::updateStream(): about to overwrite buffer " <<
int(s->user_thread.cur) << " of Texture at " << t << " of Stream at " << s
<< io::endl;
}
#endif
mOpenGLBlitter->blit(s->gl.externalTextureId, writeTexture, s->width, s->height);
// We need a fence to guarantee that this copy has happened when we need the texture
// in OpenGLProgram::updateSamplers(), i.e. when we bind textures just before use.
GLsync fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
// Per https://www.khronos.org/opengl/wiki/Sync_Object, flush to make sure that the
// sync object is in the driver's command queue.
glFlush();
// Update the stream timestamp. It's not clear to me that this is correct; which
// timestamp do we really want? Here we use "now" because we have nothing else we
// can use.
s->user_thread.timestamp = std::chrono::steady_clock::now().time_since_epoch().count();
driver->queueCommand([this, t, s, fence, readTexture, writeTexture]() {
// the stream may have been destroyed since we enqueued the command
// also make sure that this texture is still associated with the same stream
auto& streams = mExternalStreams;
if (UTILS_LIKELY(std::find(streams.begin(), streams.end(), t) != streams.end()) &&
(t->hwStream == s)) {
if (UTILS_UNLIKELY(t->gl.fence)) {
// if the texture still has a fence set, destroy it now, so it's not leaked.
glDeleteSync(t->gl.fence);
}
t->gl.id = readTexture;
t->gl.fence = fence;
s->gl.externalTexture2DId = writeTexture;
} else {
glDeleteSync(fence);
}
});
}
}
void OpenGLDriver::readStreamPixels(Handle<HwStream> sh,
uint32_t x, uint32_t y, uint32_t width, uint32_t height,
PixelBufferDescriptor&& p) {
DEBUG_MARKER()
auto& gl = mContext;
GLStream* s = handle_cast<GLStream*>(sh);
if (UTILS_UNLIKELY(s->streamType == StreamType::ACQUIRED)) {
PANIC_LOG("readStreamPixels with ACQUIRED streams is not yet implemented.");
return;
}
if (UTILS_LIKELY(s->streamType == StreamType::TEXTURE_ID)) {
GLuint tid = s->gl.externalTexture2DId;
if (tid == 0) {
return;
}
GLenum glFormat = getFormat(p.format);
GLenum glType = getType(p.type);
gl.pixelStore(GL_PACK_ROW_LENGTH, p.stride);
gl.pixelStore(GL_PACK_ALIGNMENT, p.alignment);
gl.pixelStore(GL_PACK_SKIP_PIXELS, p.left);
gl.pixelStore(GL_PACK_SKIP_ROWS, p.top);
if (s->gl.fbo == 0) {
glGenFramebuffers(1, &s->gl.fbo);
}
gl.bindFramebuffer(GL_FRAMEBUFFER, s->gl.fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tid, 0);
CHECK_GL_ERROR(utils::slog.e)
/*
* It looks like glReadPixels() behaves differently, or even wrongly,
* when the FBO is backed by an EXTERNAL texture...
*
*
* External texture FBO User buffer
*
* O---+----------------+
* | | | .stride .alignment
* | | | ----------------------->-->
* | | y | O----------------------+--+ low adresses
* | | | | | | |
* | | w | | | .bottom | |
* | V <---------> | | V | |
* | +---------+ | | +---------+ | |
* | | ^ | | ======> | | | | |
* | x | h| | | |.left| | | |
* +------>| v | | +---->| | | |
* | +.........+ | | +.........+ | |
* | | | | |
* | | +----------------------+--+ high adresses
* +--------------------+
*
* Origin is at the The image is NOT y-reversed
* top-left corner and bottom is counted from
* the top! "bottom" is in fact treated
* as "top".
*/
// The filament API provides yoffset as the "bottom" offset, therefore it needs to
// be corrected to match glReadPixels()'s behavior.
y = (s->height - height) - y;
// TODO: we could use a PBO to make this asynchronous
glReadPixels(GLint(x), GLint(y), GLint(width), GLint(height), glFormat, glType, p.buffer);
CHECK_GL_ERROR(utils::slog.e)
gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
scheduleDestroy(std::move(p));
}
}
// ------------------------------------------------------------------------------------------------
// Setting rendering state
// ------------------------------------------------------------------------------------------------
@@ -145,7 +145,6 @@ public:
};
struct GLStream : public HwStream {
static constexpr size_t ROUND_ROBIN_TEXTURE_COUNT = 3; // 3 maximum
using HwStream::HwStream;
struct Info {
// storage for the read/write textures below
@@ -153,29 +152,11 @@ public:
GLuint width = 0;
GLuint height = 0;
};
struct {
// id of the texture where the external frames are streamed (i.e. the texture
// used by SurfaceTexture on Android)
GLuint externalTextureId = 0;
/*
* This is for making a cpu copy of the camera frame
*/
GLuint externalTexture2DId = 0;
GLuint fbo = 0;
} gl; // 20 bytes
/*
* The fields below are accessed from the main application thread
* (not the GL thread)
*/
struct {
// texture id used to texture from, always used in the GL thread
GLuint read[ROUND_ROBIN_TEXTURE_COUNT]; // 12 bytes
// texture id to write into, always used from the user thread
GLuint write[ROUND_ROBIN_TEXTURE_COUNT]; // 12 bytes
Info infos[ROUND_ROBIN_TEXTURE_COUNT]; // 48 bytes
int64_t timestamp = 0;
uint8_t cur = 0;
AcquiredImage acquired;
@@ -375,8 +356,6 @@ private:
OpenGLPlatform& mPlatform;
OpenGLBlitter* mOpenGLBlitter = nullptr;
void updateStreamTexId(GLTexture* t, DriverApi* driver) noexcept;
void updateStreamAcquired(GLTexture* t, DriverApi* driver) noexcept;
void updateBuffer(GLBufferObject* buffer, BufferDescriptor const& p,
uint32_t byteOffset, uint32_t alignment = 16) noexcept;
@@ -55,11 +55,6 @@ public:
void detach(Stream* stream) noexcept final {}
void updateTexImage(Stream* stream, int64_t* timestamp) noexcept final {}
ExternalTexture* createExternalTextureStorage() noexcept final { return nullptr; }
void reallocateExternalStorage(ExternalTexture* ets,
uint32_t w, uint32_t h, TextureFormat format) noexcept final { }
void destroyExternalTextureStorage(ExternalTexture* ets) noexcept final { }
int getOSVersion() const noexcept final { return 0; }
bool pumpEvents() noexcept override;
@@ -56,11 +56,6 @@ public:
void detach(Stream* stream) noexcept final {}
void updateTexImage(Stream* stream, int64_t* timestamp) noexcept final {}
ExternalTexture* createExternalTextureStorage() noexcept final { return nullptr; }
void reallocateExternalStorage(ExternalTexture* ets,
uint32_t w, uint32_t h, TextureFormat format) noexcept final { }
void destroyExternalTextureStorage(ExternalTexture* ets) noexcept final { }
int getOSVersion() const noexcept final { return 0; }
bool setExternalImage(void* externalImage, void* texture) noexcept final;
@@ -51,11 +51,6 @@ public:
void detach(Stream* stream) noexcept final override {}
void updateTexImage(Stream* stream, int64_t* timestamp) noexcept final override {}
ExternalTexture* createExternalTextureStorage() noexcept final override { return nullptr; }
void reallocateExternalStorage(ExternalTexture* ets,
uint32_t w, uint32_t h, TextureFormat format) noexcept final override { }
void destroyExternalTextureStorage(ExternalTexture* ets) noexcept final override { }
int getOSVersion() const noexcept final override { return 0; }
};
@@ -62,11 +62,6 @@ public:
void detach(Stream* stream) noexcept override {}
void updateTexImage(Stream* stream, int64_t* timestamp) noexcept override {}
ExternalTexture* createExternalTextureStorage() noexcept override { return nullptr; }
void reallocateExternalStorage(ExternalTexture* ets,
uint32_t w, uint32_t h, TextureFormat format) noexcept override {}
void destroyExternalTextureStorage(ExternalTexture* ets) noexcept override {}
protected:
static void logEglError(const char* name) noexcept;
@@ -19,7 +19,6 @@
#include "opengl/OpenGLDriver.h"
#include "opengl/OpenGLContext.h"
#include "android/ExternalTextureManagerAndroid.h"
#include "ExternalStreamManagerAndroid.h"
#include "private/backend/VirtualMachineEnv.h"
@@ -73,8 +72,7 @@ using EGLStream = Platform::Stream;
PlatformEGLAndroid::PlatformEGLAndroid() noexcept
: PlatformEGL(),
mExternalStreamManager(ExternalStreamManagerAndroid::create()),
mExternalTextureManager(ExternalTextureManagerAndroid::create()) {
mExternalStreamManager(ExternalStreamManagerAndroid::create()) {
char scratch[PROP_VALUE_MAX + 1];
int length = __system_property_get("ro.build.version.release", scratch);
@@ -104,7 +102,6 @@ PlatformEGLAndroid::~PlatformEGLAndroid() noexcept = default;
void PlatformEGLAndroid::terminate() noexcept {
ExternalStreamManagerAndroid::destroy(&mExternalStreamManager);
ExternalTextureManagerAndroid::destroy(&mExternalTextureManager);
PlatformEGL::terminate();
}
@@ -167,58 +164,6 @@ void PlatformEGLAndroid::updateTexImage(Stream* stream, int64_t* timestamp) noex
mExternalStreamManager.updateTexImage(stream, timestamp);
}
Platform::ExternalTexture* PlatformEGLAndroid::createExternalTextureStorage() noexcept {
return mExternalTextureManager.createExternalTexture();
}
void PlatformEGLAndroid::reallocateExternalStorage(
Platform::ExternalTexture* externalTexture,
uint32_t w, uint32_t h, TextureFormat format) noexcept {
if (externalTexture) {
if ((EGLImageKHR)externalTexture->image != EGL_NO_IMAGE_KHR) {
eglDestroyImageKHR(mEGLDisplay, (EGLImageKHR)externalTexture->image);
externalTexture->image = (uintptr_t)EGL_NO_IMAGE_KHR;
}
mExternalTextureManager.reallocate(externalTexture, w, h, format,
AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE);
auto ets = (ExternalTextureManagerAndroid::ExternalTexture*)externalTexture;
EGLClientBuffer clientBuffer;
if (ets->hardwareBuffer) {
clientBuffer = eglGetNativeClientBufferANDROID(ets->hardwareBuffer);
if (UTILS_UNLIKELY(!clientBuffer)) {
logEglError("eglGetNativeClientBufferANDROID");
return;
}
} else if (ets->clientBuffer) {
clientBuffer = (EGLClientBuffer)ets->clientBuffer;
} else {
// woops, reallocate failed.
return;
}
const EGLint attr[] = { EGL_NONE };
EGLImageKHR image = eglCreateImageKHR(mEGLDisplay,
EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attr);
if (UTILS_UNLIKELY(!image)) {
logEglError("eglCreateImageKHR");
}
ets->image = (uintptr_t)image;
}
}
void PlatformEGLAndroid::destroyExternalTextureStorage(
Platform::ExternalTexture* externalTexture) noexcept {
if (externalTexture) {
mExternalTextureManager.destroy(externalTexture);
if ((EGLImageKHR)externalTexture->image != EGL_NO_IMAGE_KHR) {
eglDestroyImageKHR(mEGLDisplay, (EGLImageKHR)externalTexture->image);
externalTexture->image = (uintptr_t)EGL_NO_IMAGE_KHR;
}
}
}
int PlatformEGLAndroid::getOSVersion() const noexcept {
return mOSVersion;
}
@@ -22,7 +22,6 @@
namespace filament::backend {
class ExternalStreamManagerAndroid;
class ExternalTextureManagerAndroid;
class PlatformEGLAndroid final : public PlatformEGL {
public:
@@ -44,17 +43,11 @@ public:
void detach(Stream* stream) noexcept final;
void updateTexImage(Stream* stream, int64_t* timestamp) noexcept final;
ExternalTexture* createExternalTextureStorage() noexcept final;
void reallocateExternalStorage(ExternalTexture* ets,
uint32_t w, uint32_t h, TextureFormat format) noexcept final;
void destroyExternalTextureStorage(ExternalTexture* ets) noexcept final;
AcquiredImage transformAcquiredImage(AcquiredImage source) noexcept final;
private:
int mOSVersion;
ExternalStreamManagerAndroid& mExternalStreamManager;
ExternalTextureManagerAndroid& mExternalTextureManager;
};
} // namespace filament::backend
@@ -55,11 +55,6 @@ public:
void detach(Stream* stream) noexcept final override {}
void updateTexImage(Stream* stream, int64_t* timestamp) noexcept final override {}
ExternalTexture* createExternalTextureStorage() noexcept final override { return nullptr; }
void reallocateExternalStorage(ExternalTexture* ets,
uint32_t w, uint32_t h, TextureFormat format) noexcept final override { }
void destroyExternalTextureStorage(ExternalTexture* ets) noexcept final override { }
int getOSVersion() const noexcept final override { return 0; }
private:
@@ -51,11 +51,6 @@ public:
void detach(Stream* stream) noexcept final override {}
void updateTexImage(Stream* stream, int64_t* timestamp) noexcept final override {}
ExternalTexture* createExternalTextureStorage() noexcept final override { return nullptr; }
void reallocateExternalStorage(ExternalTexture* ets,
uint32_t w, uint32_t h, TextureFormat format) noexcept final override { }
void destroyExternalTextureStorage(ExternalTexture* ets) noexcept final override { }
int getOSVersion() const noexcept final override { return 0; }
private:
@@ -52,11 +52,6 @@ public:
void detach(Stream* stream) noexcept final override {}
void updateTexImage(Stream* stream, int64_t* timestamp) noexcept final override {}
ExternalTexture* createExternalTextureStorage() noexcept final override { return nullptr; }
void reallocateExternalStorage(ExternalTexture* ets,
uint32_t w, uint32_t h, TextureFormat format) noexcept final override { }
void destroyExternalTextureStorage(ExternalTexture* ets) noexcept final override { }
int getOSVersion() const noexcept final override { return 0; }
};
@@ -608,10 +608,6 @@ void VulkanDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch,
construct<VulkanSwapChain>(sch, mContext, mStagePool, width, height);
}
void VulkanDriver::createStreamFromTextureIdR(Handle<HwStream> sh, intptr_t externalTextureId,
uint32_t width, uint32_t height) {
}
void VulkanDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {
// nothing to do, timer query was constructed in createTimerQueryS
}
@@ -678,10 +674,6 @@ Handle<HwSwapChain> VulkanDriver::createSwapChainHeadlessS() noexcept {
return allocHandle<VulkanSwapChain>();
}
Handle<HwStream> VulkanDriver::createStreamFromTextureIdS() noexcept {
return {};
}
Handle<HwTimerQuery> VulkanDriver::createTimerQueryS() noexcept {
// The handle must be constructed here, as a synchronous call to getTimerQueryValue might happen
// before createTimerQueryR is executed.
@@ -1629,11 +1621,6 @@ void VulkanDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y
scheduleDestroy(std::move(pbd));
}
void VulkanDriver::readStreamPixels(Handle<HwStream> sh, uint32_t x, uint32_t y, uint32_t width,
uint32_t height, PixelBufferDescriptor&& p) {
scheduleDestroy(std::move(p));
}
void VulkanDriver::blit(TargetBufferFlags buffers, Handle<HwRenderTarget> dst, Viewport dstRect,
Handle<HwRenderTarget> src, Viewport srcRect, SamplerMagFilter filter) {
assert_invariant(mContext.currentRenderPass.renderPass == VK_NULL_HANDLE);
+4 -83
View File
@@ -35,10 +35,9 @@ class Engine;
/**
* Stream is used to attach a video stream to a Filament `Texture`.
*
* Note that the `Stream` class is fairly Android centric. It supports three different
* Note that the `Stream` class is fairly Android centric. It supports two different
* configurations:
*
* - TEXTURE_ID...takes an OpenGL texture ID and incurs a copy
* - ACQUIRED.....connects to an Android AHardwareBuffer
* - NATIVE.......connects to an Android SurfaceTexture
*
@@ -67,10 +66,6 @@ class Engine;
* - Filament invokes low-level graphics commands on the \em{driver thread}.
* - The thread that calls `beginFrame` is called the \em{main thread}.
*
* The TEXTURE_ID configuration achieves synchronization automatically. In this mode, Filament
* performs a copy on the main thread during `beginFrame` by blitting the external image into
* an internal round-robin queue of images. This copy has a run-time cost.
*
* For ACQUIRED streams, there is no need to perform the copy because Filament explictly acquires
* the stream, then releases it later via a callback function. This configuration is especially
* useful when the Vulkan backend is enabled.
@@ -97,7 +92,7 @@ public:
* By default, Stream objects are ACQUIRED and must have external images pushed to them via
* <pre>Stream::setAcquiredImage</pre>.
*
* To create a NATIVE or TEXTURE_ID stream, call one of the <pre>stream</pre> methods
* To create a NATIVE stream, call one of the <pre>stream</pre> methods
* on the builder.
*/
class Builder : public BuilderBase<BuilderDetails> {
@@ -122,23 +117,6 @@ public:
*/
Builder& stream(void* stream) noexcept;
/**
* Creates a TEXTURE_ID stream. This will sample data from the supplied
* external texture and copy it into an internal private texture.
*
* @param externalTextureId An opaque texture id (typically a GLuint created with glGenTextures)
* In a context shared with filament. In that case this texture's
* target must be GL_TEXTURE_EXTERNAL_OES and the wrap mode must
* be CLAMP_TO_EDGE.
*
* @return This Builder, for chaining calls.
*
* @see Texture::setExternalStream()
* @deprecated this method existed only for ARCore which doesn't need this anymore, use Texture::import() instead.
*/
UTILS_DEPRECATED
Builder& stream(intptr_t externalTextureId) noexcept;
/**
*
* @param width initial width of the incoming stream. Whether this value is used is
@@ -173,7 +151,7 @@ public:
};
/**
* Indicates whether this stream is a NATIVE stream, TEXTURE_ID stream, or ACQUIRED stream.
* Indicates whether this stream is a NATIVE stream or ACQUIRED stream.
*/
StreamType getStreamType() const noexcept;
@@ -190,7 +168,7 @@ public:
* also where the callback is invoked. This method can only be used for streams that were
* constructed without calling the `stream` method on the builder.
*
* @see Stream for more information about NATIVE, TEXTURE_ID, and ACQUIRED configurations.
* @see Stream for more information about NATIVE and ACQUIRED configurations.
*
* @param image Pointer to AHardwareBuffer, casted to void* since this is a public header.
* @param callback This is triggered by Filament when it wishes to release the image.
@@ -222,63 +200,6 @@ public:
*/
void setDimensions(uint32_t width, uint32_t height) noexcept;
/**
* Read-back the content of the last frame of a Stream since the last call to
* Renderer.beginFrame().
*
* The Stream must be of type externalTextureId. This function is a no-op otherwise.
*
* @param xoffset Left offset of the sub-region to read back.
* @param yoffset Bottom offset of the sub-region to read back.
* @param width Width of the sub-region to read back.
* @param height Height of the sub-region to read back.
* @param buffer Client-side buffer where the read-back will be written.
*
* The following format are always supported:
* - PixelBufferDescriptor::PixelDataFormat::RGBA
* - PixelBufferDescriptor::PixelDataFormat::RGBA_INTEGER
*
* The following types are always supported:
* - PixelBufferDescriptor::PixelDataType::UBYTE
* - PixelBufferDescriptor::PixelDataType::UINT
* - PixelBufferDescriptor::PixelDataType::INT
* - PixelBufferDescriptor::PixelDataType::FLOAT
*
* Other combination of format/type may be supported. If a combination is
* not supported, this operation may fail silently. Use a DEBUG build
* to get some logs about the failure.
*
* Stream buffer User buffer (PixelBufferDescriptor&)
* +--------------------+
* | | .stride .alignment
* | | ----------------------->-->
* | | O----------------------+--+ low addresses
* | | | | | |
* | w | | | .top | |
* | <---------> | | V | |
* | +---------+ | | +---------+ | |
* | | ^ | | ======> | | | | |
* | x | h| | | |.left| | | |
* +------>| v | | +---->| | | |
* | +.........+ | | +.........+ | |
* | ^ | | | |
* | y | | +----------------------+--+ high addresses
* O------------+-------+
*
* Typically readPixels() will be called after Renderer.beginFrame().
*
* After issuing this method, the callback associated with `buffer` will be invoked on the
* main thread, indicating that the read-back has completed. Typically, this will happen
* after multiple calls to beginFrame(), render(), endFrame().
*
* It is also possible to use a Fence to wait for the read-back.
*
* @remark
* readPixels() is intended for debugging and testing. It will impact performance significantly.
*/
void readPixels(uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
backend::PixelBufferDescriptor&& buffer) noexcept;
/**
* Returns the presentation time of the currently displayed frame in nanosecond.
*
-5
View File
@@ -36,11 +36,6 @@ void Stream::setDimensions(uint32_t width, uint32_t height) noexcept {
upcast(this)->setDimensions(width, height);
}
void Stream::readPixels(uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
backend::PixelBufferDescriptor&& buffer) noexcept {
upcast(this)->readPixels(xoffset, yoffset, width, height, std::move(buffer));
}
int64_t Stream::getTimestamp() const noexcept {
return upcast(this)->getTimestamp();
}
+1 -41
View File
@@ -33,7 +33,6 @@ using namespace backend;
struct Stream::BuilderDetails {
void* mStream = nullptr;
intptr_t mExternalTextureId = 0;
uint32_t mWidth = 0;
uint32_t mHeight = 0;
};
@@ -52,11 +51,6 @@ Stream::Builder& Stream::Builder::stream(void* stream) noexcept {
return *this;
}
Stream::Builder& Stream::Builder::stream(intptr_t externalTextureId) noexcept {
mImpl->mExternalTextureId = externalTextureId;
return *this;
}
Stream::Builder& Stream::Builder::width(uint32_t width) noexcept {
mImpl->mWidth = width;
return *this;
@@ -68,11 +62,6 @@ Stream::Builder& Stream::Builder::height(uint32_t height) noexcept {
}
Stream* Stream::Builder::build(Engine& engine) {
if (!ASSERT_PRECONDITION_NON_FATAL(!mImpl->mStream || !mImpl->mExternalTextureId,
"One and only one of the stream or external texture can be specified")) {
return nullptr;
}
return upcast(engine).createStream(*this);
}
@@ -80,21 +69,14 @@ Stream* Stream::Builder::build(Engine& engine) {
FStream::FStream(FEngine& engine, const Builder& builder) noexcept
: mEngine(engine),
mStreamType(
builder->mExternalTextureId ? StreamType::TEXTURE_ID :
(builder->mStream ? StreamType::NATIVE : StreamType::ACQUIRED)
),
mStreamType(builder->mStream ? StreamType::NATIVE : StreamType::ACQUIRED),
mNativeStream(builder->mStream),
mExternalTextureId(builder->mExternalTextureId),
mWidth(builder->mWidth),
mHeight(builder->mHeight) {
if (mNativeStream) {
// Note: this is a synchronous call. On Android, this calls back into Java.
mStreamHandle = engine.getDriverApi().createStreamNative(mNativeStream);
} else if (mExternalTextureId) {
mStreamHandle = engine.getDriverApi().createStreamFromTextureId(
mExternalTextureId, mWidth, mHeight);
} else {
mStreamHandle = engine.getDriverApi().createStreamAcquired();
}
@@ -124,28 +106,6 @@ void FStream::setDimensions(uint32_t width, uint32_t height) noexcept {
mEngine.getDriverApi().setStreamDimensions(mStreamHandle, mWidth, mHeight);
}
void FStream::readPixels(uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
backend::PixelBufferDescriptor&& buffer) noexcept {
if (getStreamType() == StreamType::TEXTURE_ID) {
// this works only on external texture id streams
const size_t sizeNeeded = PixelBufferDescriptor::computeDataSize(
buffer.format, buffer.type,
buffer.stride ? buffer.stride : width,
buffer.top + height,
buffer.alignment);
if (!ASSERT_POSTCONDITION_NON_FATAL(buffer.size >= sizeNeeded,
"buffer.size too small %u bytes, needed %u bytes", buffer.size, sizeNeeded)) {
return;
}
FEngine::DriverApi& driver = mEngine.getDriverApi();
driver.readStreamPixels(mStreamHandle,
xoffset, yoffset, width, height, std::move(buffer));
}
}
int64_t FStream::getTimestamp() const noexcept {
FEngine::DriverApi& driver = mEngine.getDriverApi();
return driver.getStreamTimestamp(mStreamHandle);
-4
View File
@@ -41,9 +41,6 @@ public:
void setDimensions(uint32_t width, uint32_t height) noexcept;
void readPixels(uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
backend::PixelBufferDescriptor&& buffer) noexcept;
StreamType getStreamType() const noexcept { return mStreamType; }
uint32_t getWidth() const noexcept { return mWidth; }
@@ -57,7 +54,6 @@ private:
const StreamType mStreamType;
backend::Handle<backend::HwStream> mStreamHandle;
void* mNativeStream = nullptr;
intptr_t mExternalTextureId;
uint32_t mWidth;
uint32_t mHeight;
};