add additional Java source files needed for Android build
This commit is contained in:
144
android/src/main/java/com/google/android/filament/SwapChain.java
Normal file
144
android/src/main/java/com/google/android/filament/SwapChain.java
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.android.filament;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* A <code>SwapChain</code> represents an Operating System's <b>native</b> renderable surface.
|
||||
*
|
||||
* <p>Typically it's a native window or a view. Because a <code>SwapChain</code> is initialized
|
||||
* from a native object, it is given to filament as an <code>Object</code>, which must be of the
|
||||
* proper type for each platform filament is running on.</p>
|
||||
*
|
||||
* <code>
|
||||
* SwapChain swapChain = engine.createSwapChain(nativeWindow);
|
||||
* </code>
|
||||
*
|
||||
* <p>The <code>nativeWindow</code> parameter above must be of type:</p>
|
||||
*
|
||||
* <center>
|
||||
* <table border="1">
|
||||
* <tr><th> Platform </th><th> nativeWindow type </th></tr>
|
||||
* <tr><td> Android </td><td>{@link android.view.Surface Surface}</td></tr>
|
||||
* </table>
|
||||
* </center>
|
||||
* <p>
|
||||
*
|
||||
* <h1>Examples</h1>
|
||||
*
|
||||
* <h2>Android</h2>
|
||||
*
|
||||
*
|
||||
* <p>A {@link android.view.Surface Surface} can be retrieved from a
|
||||
* {@link android.view.SurfaceView SurfaceView} or {@link android.view.SurfaceHolder SurfaceHolder}
|
||||
* easily using {@link android.view.SurfaceHolder#getSurface SurfaceHolder.getSurface()} and/or
|
||||
* {@link android.view.SurfaceView#getHolder SurfaceView.getHolder()}.</p>
|
||||
*
|
||||
* <p>To use a {@link android.view.TextureView Textureview} as a <code>SwapChain</code>, it is
|
||||
* necessary to first get its {@link android.graphics.SurfaceTexture SurfaceTexture},
|
||||
* for instance using {@link android.view.TextureView.SurfaceTextureListener SurfaceTextureListener}
|
||||
* and then create a {@link android.view.Surface Surface}:</p>
|
||||
*
|
||||
* <pre>
|
||||
* // using a TextureView.SurfaceTextureListener:
|
||||
* public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
|
||||
* mSurface = new Surface(surfaceTexture);
|
||||
* // mSurface can now be used with Engine.createSwapChain()
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @see Engine
|
||||
*/
|
||||
public class SwapChain {
|
||||
private final Object mSurface;
|
||||
private long mNativeObject;
|
||||
|
||||
public static final long CONFIG_DEFAULT = 0x0;
|
||||
|
||||
/**
|
||||
* This flag indicates that the <code>SwapChain</code> must be allocated with an
|
||||
* alpha-channel.
|
||||
*/
|
||||
public static final long CONFIG_TRANSPARENT = 0x1;
|
||||
|
||||
/**
|
||||
* This flag indicates that the <code>SwapChain</code> may be used as a source surface
|
||||
* for reading back render results. This config must be set when creating
|
||||
* any <code>SwapChain</code> that will be used as the source for a blit operation.
|
||||
*
|
||||
* @see Renderer#copyFrame
|
||||
*/
|
||||
public static final long CONFIG_READABLE = 0x2;
|
||||
|
||||
/**
|
||||
* Indicates that the native X11 window is an XCB window rather than an XLIB window.
|
||||
* This is ignored on non-Linux platforms and in builds that support only one X11 API.
|
||||
*/
|
||||
public static final long CONFIG_ENABLE_XCB = 0x4;
|
||||
|
||||
SwapChain(long nativeSwapChain, Object surface) {
|
||||
mNativeObject = nativeSwapChain;
|
||||
mSurface = surface;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the native <code>Object</code> this <code>SwapChain</code> was created from or null
|
||||
* for a headless SwapChain.
|
||||
*/
|
||||
public Object getNativeWindow() {
|
||||
return mSurface;
|
||||
}
|
||||
|
||||
/**
|
||||
* FrameCompletedCallback is a callback function that notifies an application when a frame's
|
||||
* contents have completed rendering on the GPU.
|
||||
*
|
||||
* <p>
|
||||
* Use setFrameCompletedCallback to set a callback on an individual SwapChain. Each time a frame
|
||||
* completes GPU rendering, the callback will be called.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* The FrameCompletedCallback is guaranteed to be called on the main Filament thread.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Warning: Only Filament's Metal backend supports frame callbacks. Other backends ignore the
|
||||
* callback (which will never be called) and proceed normally.
|
||||
* </p>
|
||||
*
|
||||
* @param handler A {@link java.util.concurrent.Executor Executor}.
|
||||
* @param callback The Runnable callback to invoke.
|
||||
*/
|
||||
public void setFrameCompletedCallback(@NonNull Object handler, @NonNull Runnable callback) {
|
||||
nSetFrameCompletedCallback(getNativeObject(), handler, callback);
|
||||
}
|
||||
|
||||
public long getNativeObject() {
|
||||
if (mNativeObject == 0) {
|
||||
throw new IllegalStateException("Calling method on destroyed SwapChain");
|
||||
}
|
||||
return mNativeObject;
|
||||
}
|
||||
|
||||
void clearNativeObject() {
|
||||
mNativeObject = 0;
|
||||
}
|
||||
|
||||
private static native void nSetFrameCompletedCallback(long nativeSwapChain, Object handler, Runnable callback);
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
// /*
|
||||
// * Copyright (C) 2020 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.
|
||||
// */
|
||||
|
||||
// package com.google.android.filament.android;
|
||||
|
||||
// import android.content.Context;
|
||||
// import android.hardware.display.DisplayManager;
|
||||
// import android.os.Build;
|
||||
// import android.os.Handler;
|
||||
// import android.view.Display;
|
||||
|
||||
// import com.google.android.filament.Renderer;
|
||||
|
||||
// import androidx.annotation.NonNull;
|
||||
// import androidx.annotation.Nullable;
|
||||
|
||||
// /**
|
||||
// * DisplayHelper is here to help managing a Display, for instance being notified when its
|
||||
// * resolution or refresh rate changes.
|
||||
// */
|
||||
// public class DisplayHelper {
|
||||
|
||||
// private Handler mHandler = null;
|
||||
// private DisplayManager mDisplayManager;
|
||||
// private Display mDisplay;
|
||||
// private Renderer mRenderer;
|
||||
// private DisplayManager.DisplayListener mListener;
|
||||
|
||||
// /**
|
||||
// * Creates a DisplayHelper which helps managing a {@link Display}.
|
||||
// *
|
||||
// * The {@link Display} to manage is specified with {@link #attach}
|
||||
// *
|
||||
// * @param context a {@link Context} to used to retrieve the {@link DisplayManager}
|
||||
// */
|
||||
// public DisplayHelper(@NonNull Context context) {
|
||||
// mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Creates a DisplayHelper which helps manage a {@link Display} and provides a Handler
|
||||
// * where callbacks can execute filament code. Use this method if filament is executing
|
||||
// * on another thread.
|
||||
// *
|
||||
// * @param context a {@link Context} to used to retrieve teh {@link DisplayManager}
|
||||
// * @param handler a {@link Handler} used to run callbacks accessing filament
|
||||
// */
|
||||
// public DisplayHelper(@NonNull Context context, @NonNull Handler handler) {
|
||||
// this(context);
|
||||
// mHandler = handler;
|
||||
// }
|
||||
|
||||
// @Override
|
||||
// protected void finalize() throws Throwable {
|
||||
// try {
|
||||
// // just for safety
|
||||
// detach();
|
||||
// } finally {
|
||||
// super.finalize();
|
||||
// }
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Sets the filament {@link Renderer} associated to the {@link Display}, from this point
|
||||
// * on, {@link Renderer.DisplayInfo} will be automatically updated when the {@link Display}
|
||||
// * properties change.
|
||||
// *
|
||||
// * This is typically called from {@link UiHelper.RendererCallback#onNativeWindowChanged}.
|
||||
// *
|
||||
// * @param renderer a filament {@link Renderer} instance
|
||||
// * @param display a {@link Display} to be associated with the {@link Renderer}
|
||||
// */
|
||||
// public void attach(@NonNull Renderer renderer, @NonNull Display display) {
|
||||
// if (renderer == mRenderer && display == mDisplay) {
|
||||
// return;
|
||||
// }
|
||||
// mRenderer = renderer;
|
||||
// mDisplay = display;
|
||||
// mListener = new DisplayManager.DisplayListener() {
|
||||
// @Override
|
||||
// public void onDisplayAdded(int displayId) {
|
||||
// }
|
||||
// @Override
|
||||
// public void onDisplayRemoved(int displayId) {
|
||||
// }
|
||||
// @Override
|
||||
// public void onDisplayChanged(int displayId) {
|
||||
// if (displayId == display.getDisplayId()) {
|
||||
// updateDisplayInfo();
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
// mDisplayManager.registerDisplayListener(mListener, mHandler);
|
||||
|
||||
// // always invoke the callback when it's registered
|
||||
// if (mHandler != null) {
|
||||
// mHandler.post(new Runnable() {
|
||||
// @Override
|
||||
// public void run() {
|
||||
// updateDisplayInfo();
|
||||
// }
|
||||
// });
|
||||
// } else {
|
||||
// updateDisplayInfo();
|
||||
// }
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Disconnect the previously set {@link Renderer} from {@link Display}
|
||||
// * This is typically called from {@link UiHelper.RendererCallback#onDetachedFromSurface}.
|
||||
// */
|
||||
// public void detach() {
|
||||
// if (mListener != null) {
|
||||
// mDisplayManager.unregisterDisplayListener(mListener);
|
||||
// mListener = null;
|
||||
// mDisplay = null;
|
||||
// mRenderer = null;
|
||||
// }
|
||||
// }
|
||||
|
||||
// private void updateDisplayInfo() {
|
||||
// mRenderer.setDisplayInfo(
|
||||
// DisplayHelper.getDisplayInfo(mDisplay, mRenderer.getDisplayInfo()));
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Returns the {@link Display} currently monitored
|
||||
// * @return the {@link Display} set in {@link #attach} or null
|
||||
// */
|
||||
// public Display getDisplay() {
|
||||
// return mDisplay;
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Populate a {@link Renderer.DisplayInfo} with properties from the given {@link Display}
|
||||
// *
|
||||
// * @param display {@link Display} to get {@link Renderer.DisplayInfo} from
|
||||
// * @param info an instance of {@link Renderer.DisplayInfo} or null
|
||||
// * @return an populated instance of {@link Renderer.DisplayInfo}
|
||||
// */
|
||||
// @NonNull
|
||||
// public static Renderer.DisplayInfo getDisplayInfo(@NonNull Display display, @Nullable Renderer.DisplayInfo info) {
|
||||
// if (info == null) {
|
||||
// info = new Renderer.DisplayInfo();
|
||||
// }
|
||||
// info.refreshRate = DisplayHelper.getRefreshRate(display);
|
||||
// return info;
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * @return the {@link Display} application vsync offset 0 if not supported
|
||||
// * @see Display#getAppVsyncOffsetNanos
|
||||
// */
|
||||
// public static long getAppVsyncOffsetNanos(@NonNull Display display) {
|
||||
// if (Build.VERSION.SDK_INT >= 29) {
|
||||
// return display.getAppVsyncOffsetNanos();
|
||||
// }
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * @return the {@link Display} presentation deadline before the h/w vsync event in nanoseconds
|
||||
// * @see Display#getPresentationDeadlineNanos
|
||||
// */
|
||||
// public static long getPresentationDeadlineNanos(@NonNull Display display) {
|
||||
// if (Build.VERSION.SDK_INT >= 29) {
|
||||
// return display.getPresentationDeadlineNanos();
|
||||
// }
|
||||
// // not supported, pick something reasonable
|
||||
// return 11_600_000;
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * @return the {@link Display} refresh rate in Hz
|
||||
// * @see Display#getRefreshRate
|
||||
// */
|
||||
// public static float getRefreshRate(@NonNull Display display) {
|
||||
// return display.getRefreshRate();
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Returns a {@link Display}'s refresh period in nanoseconds
|
||||
// * @param display the {@link Display} to get the refresh period from
|
||||
// * @return the {@link Display} refresh period in nanoseconds
|
||||
// */
|
||||
// public static long getRefreshPeriodNanos(@NonNull Display display) {
|
||||
// return (long) (1_000_000_000.0 / display.getRefreshRate());
|
||||
// }
|
||||
// }
|
||||
@@ -0,0 +1,581 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
package com.google.android.filament.android;
|
||||
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.SurfaceTexture;
|
||||
import android.os.Build;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.TextureView;
|
||||
|
||||
import com.google.android.filament.SwapChain;
|
||||
|
||||
/**
|
||||
* UiHelper is a simple class that can manage either a SurfaceView, TextureView, or a SurfaceHolder
|
||||
* so it can be used to render into with Filament.
|
||||
*
|
||||
* Here is a simple example with a SurfaceView. The code would be exactly the same with a
|
||||
* TextureView:
|
||||
*
|
||||
* <pre>
|
||||
* public class FilamentActivity extends Activity {
|
||||
* private UiHelper mUiHelper;
|
||||
* private SurfaceView mSurfaceView;
|
||||
*
|
||||
* // Filament specific APIs
|
||||
* private Engine mEngine;
|
||||
* private Renderer mRenderer;
|
||||
* private View mView; // com.google.android.filament.View, not android.view.View
|
||||
* private SwapChain mSwapChain;
|
||||
*
|
||||
* public void onCreate(Bundle savedInstanceState) {
|
||||
* super.onCreate(savedInstanceState);
|
||||
*
|
||||
* // Create a SurfaceView and add it to the activity
|
||||
* mSurfaceView = new SurfaceView(this);
|
||||
* setContentView(mSurfaceView);
|
||||
*
|
||||
* // Create the Filament UI helper
|
||||
* mUiHelper = new UiHelper(UiHelper.ContextErrorPolicy.DONT_CHECK);
|
||||
*
|
||||
* // Attach the SurfaceView to the helper, you could do the same with a TextureView
|
||||
* mUiHelper.attachTo(mSurfaceView);
|
||||
*
|
||||
* // Set a rendering callback that we will use to invoke Filament
|
||||
* mUiHelper.setRenderCallback(new UiHelper.RendererCallback() {
|
||||
* public void onNativeWindowChanged(Surface surface) {
|
||||
* if (mSwapChain != null) mEngine.destroySwapChain(mSwapChain);
|
||||
* mSwapChain = mEngine.createSwapChain(surface, mUiHelper.getSwapChainFlags());
|
||||
* }
|
||||
*
|
||||
* // The native surface went away, we must stop rendering.
|
||||
* public void onDetachedFromSurface() {
|
||||
* if (mSwapChain != null) {
|
||||
* mEngine.destroySwapChain(mSwapChain);
|
||||
*
|
||||
* // Required to ensure we don't return before Filament is done executing the
|
||||
* // destroySwapChain command, otherwise Android might destroy the Surface
|
||||
* // too early
|
||||
* mEngine.flushAndWait();
|
||||
*
|
||||
* mSwapChain = null;
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* // The native surface has changed size. This is always called at least once
|
||||
* // after the surface is created (after onNativeWindowChanged() is invoked).
|
||||
* public void onResized(int width, int height) {
|
||||
* // Compute camera projection and set the viewport on the view
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* mEngine = Engine.create();
|
||||
* mRenderer = mEngine.createRenderer();
|
||||
* mView = mEngine.createView();
|
||||
* // Create scene, camera, etc.
|
||||
* }
|
||||
*
|
||||
* public void onDestroy() {
|
||||
* super.onDestroy();
|
||||
* // Always detach the surface before destroying the engine
|
||||
* mUiHelper.detach();
|
||||
*
|
||||
* mEngine.destroy();
|
||||
* }
|
||||
*
|
||||
* // This is an example of a render function. You will most likely invoke this from
|
||||
* // a Choreographer callback to trigger rendering at vsync.
|
||||
* public void render() {
|
||||
* if (mUiHelper.isReadyToRender) {
|
||||
* // If beginFrame() returns false you should skip the frame
|
||||
* // This means you are sending frames too quickly to the GPU
|
||||
* if (mRenderer.beginFrame(swapChain)) {
|
||||
* mRenderer.render(mView);
|
||||
* mRenderer.endFrame();
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
public class UiHelper {
|
||||
private static final String LOG_TAG = "UiHelper";
|
||||
private static final boolean LOGGING = false;
|
||||
|
||||
private int mDesiredWidth;
|
||||
private int mDesiredHeight;
|
||||
private Object mNativeWindow;
|
||||
|
||||
private RendererCallback mRenderCallback;
|
||||
private boolean mHasSwapChain;
|
||||
|
||||
private RenderSurface mRenderSurface;
|
||||
|
||||
private boolean mOpaque = true;
|
||||
private boolean mOverlay = false;
|
||||
|
||||
/**
|
||||
* Enum used to decide whether UiHelper should perform extra error checking.
|
||||
*
|
||||
* @see UiHelper#UiHelper(ContextErrorPolicy)
|
||||
*/
|
||||
public enum ContextErrorPolicy {
|
||||
/** Check for extra errors. */
|
||||
CHECK,
|
||||
/** Do not check for extra errors. */
|
||||
DONT_CHECK
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface used to know when the native surface is created, destroyed or resized.
|
||||
*
|
||||
* @see #setRenderCallback(RendererCallback)
|
||||
*/
|
||||
public interface RendererCallback {
|
||||
/**
|
||||
* Called when the underlying native window has changed.
|
||||
*/
|
||||
void onNativeWindowChanged(Surface surface);
|
||||
|
||||
/**
|
||||
* Called when the surface is going away. After this call <code>isReadyToRender()</code>
|
||||
* returns false. You MUST have stopped drawing when returning.
|
||||
* This is called from detach() or if the surface disappears on its own.
|
||||
*/
|
||||
void onDetachedFromSurface();
|
||||
|
||||
/**
|
||||
* Called when the underlying native window has been resized.
|
||||
*/
|
||||
void onResized(int width, int height);
|
||||
}
|
||||
|
||||
private interface RenderSurface {
|
||||
void resize(int width, int height);
|
||||
void detach();
|
||||
}
|
||||
|
||||
private static class SurfaceViewHandler implements RenderSurface {
|
||||
private SurfaceView mSurfaceView;
|
||||
|
||||
SurfaceViewHandler(SurfaceView surface) {
|
||||
mSurfaceView = surface;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resize(int width, int height) {
|
||||
mSurfaceView.getHolder().setFixedSize(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void detach() {
|
||||
}
|
||||
}
|
||||
|
||||
private static class SurfaceHolderHandler implements RenderSurface {
|
||||
private SurfaceHolder mSurfaceHolder;
|
||||
|
||||
SurfaceHolderHandler(SurfaceHolder surface) {
|
||||
mSurfaceHolder = surface;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resize(int width, int height) {
|
||||
mSurfaceHolder.setFixedSize(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void detach() {
|
||||
}
|
||||
}
|
||||
|
||||
private class TextureViewHandler implements RenderSurface {
|
||||
private TextureView mTextureView;
|
||||
private Surface mSurface;
|
||||
|
||||
TextureViewHandler(TextureView surface) { mTextureView = surface; }
|
||||
|
||||
@Override
|
||||
public void resize(int width, int height) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
|
||||
mTextureView.getSurfaceTexture().setDefaultBufferSize(width, height);
|
||||
}
|
||||
// the call above won't cause TextureView.onSurfaceTextureSizeChanged()
|
||||
mRenderCallback.onResized(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void detach() {
|
||||
setSurface(null);
|
||||
}
|
||||
|
||||
void setSurface(Surface surface) {
|
||||
if (surface == null) {
|
||||
if (mSurface != null) {
|
||||
mSurface.release();
|
||||
}
|
||||
}
|
||||
mSurface = surface;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a UiHelper which will help manage the native surface provided by a
|
||||
* SurfaceView or a TextureView.
|
||||
*/
|
||||
public UiHelper() {
|
||||
this(ContextErrorPolicy.CHECK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a UiHelper which will help manage the native surface provided by a
|
||||
* SurfaceView or a TextureView.
|
||||
*
|
||||
* @param policy The error checking policy to use.
|
||||
*/
|
||||
public UiHelper(ContextErrorPolicy policy) {
|
||||
// TODO: do something with policy
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the renderer callback that will be notified when the native surface is
|
||||
* created, destroyed or resized.
|
||||
*
|
||||
* @param renderCallback The callback to register.
|
||||
*/
|
||||
public void setRenderCallback(@Nullable RendererCallback renderCallback) {
|
||||
mRenderCallback = renderCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current render callback associated with this UiHelper.
|
||||
*/
|
||||
@Nullable
|
||||
public RendererCallback getRenderCallback() {
|
||||
return mRenderCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free resources associated to the native window specified in {@link #attachTo(SurfaceView)},
|
||||
* {@link #attachTo(TextureView)}, or {@link #attachTo(SurfaceHolder)}.
|
||||
*/
|
||||
public void detach() {
|
||||
destroySwapChain();
|
||||
mNativeWindow = null;
|
||||
mRenderSurface = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether we are ready to render into the attached surface.
|
||||
*
|
||||
* Using OpenGL ES when this returns true, will result in drawing commands being lost,
|
||||
* HOWEVER, GLES state will be preserved. This is useful to initialize the engine.
|
||||
*
|
||||
* @return true: rendering is possible, false: rendering is not possible.
|
||||
*/
|
||||
public boolean isReadyToRender() {
|
||||
return mHasSwapChain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the size of the render target buffers of the native surface.
|
||||
*/
|
||||
public void setDesiredSize(int width, int height) {
|
||||
mDesiredWidth = width;
|
||||
mDesiredHeight = height;
|
||||
if (mRenderSurface != null) {
|
||||
mRenderSurface.resize(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the requested width for the native surface.
|
||||
*/
|
||||
public int getDesiredWidth() {
|
||||
return mDesiredWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the requested height for the native surface.
|
||||
*/
|
||||
public int getDesiredHeight() {
|
||||
return mDesiredHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the render target is opaque.
|
||||
*/
|
||||
public boolean isOpaque() {
|
||||
return mOpaque;
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls whether the render target (SurfaceView or TextureView) is opaque or not.
|
||||
* The render target is considered opaque by default.
|
||||
*
|
||||
* Must be called before calling {@link #attachTo(SurfaceView)}, {@link #attachTo(TextureView)},
|
||||
* or {@link #attachTo(SurfaceHolder)}.
|
||||
*
|
||||
* @param opaque Indicates whether the render target should be opaque. True by default.
|
||||
*/
|
||||
public void setOpaque(boolean opaque) {
|
||||
mOpaque = opaque;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the SurfaceView used as a render target should be positioned above
|
||||
* other surfaces but below the activity's surface. False by default.
|
||||
*/
|
||||
public boolean isMediaOverlay() {
|
||||
return mOverlay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls whether the surface of the SurfaceView used as a render target should be
|
||||
* positioned above other surfaces but below the activity's surface. This property
|
||||
* only has an effect when used in combination with {@link #setOpaque(boolean) setOpaque(false)}
|
||||
* and does not affect TextureView targets.
|
||||
*
|
||||
* Must be called before calling {@link #attachTo(SurfaceView)}
|
||||
* or {@link #attachTo(TextureView)}.
|
||||
*
|
||||
* Has no effect when using {@link #attachTo(SurfaceHolder)}.
|
||||
*
|
||||
* @param overlay Indicates whether the render target should be rendered below the activity's
|
||||
* surface when transparent.
|
||||
*/
|
||||
public void setMediaOverlay(boolean overlay) {
|
||||
mOverlay = overlay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the flags to pass to
|
||||
* {@link com.google.android.filament.Engine#createSwapChain(Object, long)} to honor all
|
||||
* the options set on this UiHelper.
|
||||
*/
|
||||
public long getSwapChainFlags() {
|
||||
return isOpaque() ? SwapChain.CONFIG_DEFAULT : SwapChain.CONFIG_TRANSPARENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate UiHelper with a SurfaceView.
|
||||
*
|
||||
* As soon as SurfaceView is ready (i.e. has a Surface), we'll create the
|
||||
* EGL resources needed, and call user callbacks if needed.
|
||||
*/
|
||||
public void attachTo(@NonNull SurfaceView view) {
|
||||
if (attach(view)) {
|
||||
boolean translucent = !isOpaque();
|
||||
// setZOrderOnTop() and setZOrderMediaOverlay() override each other,
|
||||
// we must only call one of them
|
||||
if (isMediaOverlay()) {
|
||||
view.setZOrderMediaOverlay(translucent);
|
||||
} else {
|
||||
view.setZOrderOnTop(translucent);
|
||||
}
|
||||
|
||||
int format = isOpaque() ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT;
|
||||
view.getHolder().setFormat(format);
|
||||
|
||||
mRenderSurface = new SurfaceViewHandler(view);
|
||||
|
||||
final SurfaceHolder.Callback callback = new SurfaceHolder.Callback() {
|
||||
@Override
|
||||
public void surfaceCreated(SurfaceHolder holder) {
|
||||
if (LOGGING) Log.d(LOG_TAG, "surfaceCreated()");
|
||||
createSwapChain(holder.getSurface());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceChanged(
|
||||
SurfaceHolder holder, int format, int width, int height) {
|
||||
// Note: this is always called at least once after surfaceCreated()
|
||||
if (LOGGING) Log.d(LOG_TAG, "surfaceChanged(" + width + ", " + height + ")");
|
||||
mRenderCallback.onResized(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||
if (LOGGING) Log.d(LOG_TAG, "surfaceDestroyed()");
|
||||
destroySwapChain();
|
||||
}
|
||||
};
|
||||
|
||||
SurfaceHolder holder = view.getHolder();
|
||||
holder.addCallback(callback);
|
||||
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
|
||||
holder.setFixedSize(mDesiredWidth, mDesiredHeight);
|
||||
}
|
||||
|
||||
// in case the SurfaceView's surface already existed
|
||||
final Surface surface = holder.getSurface();
|
||||
if (surface != null && surface.isValid()) {
|
||||
callback.surfaceCreated(holder);
|
||||
callback.surfaceChanged(holder, format,
|
||||
holder.getSurfaceFrame().width(), holder.getSurfaceFrame().height());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate UiHelper with a TextureView.
|
||||
*
|
||||
* As soon as TextureView is ready (i.e. has a buffer), we'll create the
|
||||
* EGL resources needed, and call user callbacks if needed.
|
||||
*/
|
||||
public void attachTo(@NonNull TextureView view) {
|
||||
if (attach(view)) {
|
||||
view.setOpaque(isOpaque());
|
||||
|
||||
mRenderSurface = new TextureViewHandler(view);
|
||||
|
||||
TextureView.SurfaceTextureListener listener = new TextureView.SurfaceTextureListener() {
|
||||
@Override
|
||||
public void onSurfaceTextureAvailable(
|
||||
SurfaceTexture surfaceTexture, int width, int height) {
|
||||
if (LOGGING) Log.d(LOG_TAG, "onSurfaceTextureAvailable()");
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
|
||||
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
|
||||
surfaceTexture.setDefaultBufferSize(mDesiredWidth, mDesiredHeight);
|
||||
}
|
||||
}
|
||||
|
||||
Surface surface = new Surface(surfaceTexture);
|
||||
TextureViewHandler textureViewHandler = (TextureViewHandler) mRenderSurface;
|
||||
textureViewHandler.setSurface(surface);
|
||||
|
||||
createSwapChain(surface);
|
||||
|
||||
// Call this the first time because onSurfaceTextureSizeChanged()
|
||||
// isn't called at initialization time
|
||||
mRenderCallback.onResized(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceTextureSizeChanged(
|
||||
SurfaceTexture surfaceTexture, int width, int height) {
|
||||
if (LOGGING) Log.d(LOG_TAG, "onSurfaceTextureSizeChanged()");
|
||||
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
|
||||
surfaceTexture.setDefaultBufferSize(mDesiredWidth, mDesiredHeight);
|
||||
mRenderCallback.onResized(mDesiredWidth, mDesiredHeight);
|
||||
} else {
|
||||
mRenderCallback.onResized(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
|
||||
if (LOGGING) Log.d(LOG_TAG, "onSurfaceTextureDestroyed()");
|
||||
destroySwapChain();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceTextureUpdated(SurfaceTexture surface) { }
|
||||
};
|
||||
|
||||
view.setSurfaceTextureListener(listener);
|
||||
|
||||
// in case the View's SurfaceTexture already existed
|
||||
if (view.isAvailable()) {
|
||||
SurfaceTexture surfaceTexture = view.getSurfaceTexture();
|
||||
listener.onSurfaceTextureAvailable(surfaceTexture, mDesiredWidth, mDesiredHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate UiHelper with a SurfaceHolder.
|
||||
*
|
||||
* As soon as a Surface is created, we'll create the
|
||||
* EGL resources needed, and call user callbacks if needed.
|
||||
*/
|
||||
public void attachTo(@NonNull SurfaceHolder holder) {
|
||||
if (attach(holder)) {
|
||||
int format = isOpaque() ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT;
|
||||
holder.setFormat(format);
|
||||
|
||||
mRenderSurface = new SurfaceHolderHandler(holder);
|
||||
|
||||
final SurfaceHolder.Callback callback = new SurfaceHolder.Callback() {
|
||||
@Override
|
||||
public void surfaceCreated(SurfaceHolder surfaceHolder) {
|
||||
if (LOGGING) Log.d(LOG_TAG, "surfaceCreated()");
|
||||
createSwapChain(holder.getSurface());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
||||
// Note: this is always called at least once after surfaceCreated()
|
||||
if (LOGGING) Log.d(LOG_TAG, "surfaceChanged(" + width + ", " + height + ")");
|
||||
mRenderCallback.onResized(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
|
||||
if (LOGGING) Log.d(LOG_TAG, "surfaceDestroyed()");
|
||||
destroySwapChain();
|
||||
}
|
||||
};
|
||||
|
||||
holder.addCallback(callback);
|
||||
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
|
||||
holder.setFixedSize(mDesiredWidth, mDesiredHeight);
|
||||
}
|
||||
|
||||
// in case the SurfaceHolder's surface already existed
|
||||
final Surface surface = holder.getSurface();
|
||||
if (surface != null && surface.isValid()) {
|
||||
callback.surfaceCreated(holder);
|
||||
callback.surfaceChanged(holder, format,
|
||||
holder.getSurfaceFrame().width(), holder.getSurfaceFrame().height());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean attach(@NonNull Object nativeWindow) {
|
||||
if (mNativeWindow != null) {
|
||||
// we are already attached to a native window
|
||||
if (mNativeWindow == nativeWindow) {
|
||||
// nothing to do
|
||||
return false;
|
||||
}
|
||||
destroySwapChain();
|
||||
}
|
||||
mNativeWindow = nativeWindow;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void createSwapChain(@NonNull Surface surface) {
|
||||
mRenderCallback.onNativeWindowChanged(surface);
|
||||
mHasSwapChain = true;
|
||||
}
|
||||
|
||||
private void destroySwapChain() {
|
||||
if (mRenderSurface != null) {
|
||||
mRenderSurface.detach();
|
||||
}
|
||||
mRenderCallback.onDetachedFromSurface();
|
||||
mHasSwapChain = false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user