diff --git a/android/filament-android/src/main/cpp/Engine.cpp b/android/filament-android/src/main/cpp/Engine.cpp
index a5638bbbe4..e208fb9415 100644
--- a/android/filament-android/src/main/cpp/Engine.cpp
+++ b/android/filament-android/src/main/cpp/Engine.cpp
@@ -59,6 +59,13 @@ Java_com_google_android_filament_Engine_nCreateSwapChain(JNIEnv* env,
return (jlong) engine->createSwapChain(win, (uint64_t) flags);
}
+extern "C" JNIEXPORT jlong JNICALL
+Java_com_google_android_filament_Engine_nCreateSwapChainHeadless(JNIEnv* env,
+ jclass klass, jlong nativeEngine, jint width, jint height, jlong flags) {
+ Engine* engine = (Engine*) nativeEngine;
+ return (jlong) engine->createSwapChain(width, height, (uint64_t) flags);
+}
+
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_Engine_nCreateSwapChainFromRawPointer(JNIEnv*,
jclass, jlong nativeEngine, jlong pointer, jlong flags) {
diff --git a/android/filament-android/src/main/java/com/google/android/filament/Engine.java b/android/filament-android/src/main/java/com/google/android/filament/Engine.java
index 4c4acd6510..70622cc7f8 100644
--- a/android/filament-android/src/main/java/com/google/android/filament/Engine.java
+++ b/android/filament-android/src/main/java/com/google/android/filament/Engine.java
@@ -290,6 +290,32 @@ public class Engine {
throw new IllegalArgumentException("Invalid surface " + surface);
}
+ /**
+ * Creates a headless {@link SwapChain}
+ *
+ * @param width width of the rendering buffer
+ * @param height height of the rendering buffer
+ * @param flags configuration flags, see {@link SwapChain}
+ *
+ * @return a newly created {@link SwapChain} object
+ *
+ * @exception IllegalStateException can be thrown if the SwapChain couldn't be created
+ *
+ * @see SwapChain#CONFIG_DEFAULT
+ * @see SwapChain#CONFIG_TRANSPARENT
+ * @see SwapChain#CONFIG_READABLE
+ *
+ */
+ @NonNull
+ public SwapChain createSwapChain(int width, int height, long flags) {
+ if (width >= 0 && height >= 0) {
+ long nativeSwapChain = nCreateSwapChainHeadless(getNativeObject(), width, height, flags);
+ if (nativeSwapChain == 0) throw new IllegalStateException("Couldn't create SwapChain");
+ return new SwapChain(nativeSwapChain, null);
+ }
+ throw new IllegalArgumentException("Invalid parameters");
+ }
+
/**
* Creates a {@link SwapChain} from a {@link NativeSurface}.
*
@@ -608,6 +634,7 @@ public class Engine {
private static native void nDestroyEngine(long nativeEngine);
private static native long nGetBackend(long nativeEngine);
private static native long nCreateSwapChain(long nativeEngine, Object nativeWindow, long flags);
+ private static native long nCreateSwapChainHeadless(long nativeEngine, int width, int height, long flags);
private static native long nCreateSwapChainFromRawPointer(long nativeEngine, long pointer, long flags);
private static native void nDestroySwapChain(long nativeEngine, long nativeSwapChain);
private static native long nCreateView(long nativeEngine);
diff --git a/android/filament-android/src/main/java/com/google/android/filament/SwapChain.java b/android/filament-android/src/main/java/com/google/android/filament/SwapChain.java
index f8db30b3a2..7dc8d6f89c 100644
--- a/android/filament-android/src/main/java/com/google/android/filament/SwapChain.java
+++ b/android/filament-android/src/main/java/com/google/android/filament/SwapChain.java
@@ -85,15 +85,15 @@ public class SwapChain {
*/
public static final long CONFIG_READABLE = 0x2;
- SwapChain(long nativeSwapChain, @NonNull Object surface) {
+ SwapChain(long nativeSwapChain, Object surface) {
mNativeObject = nativeSwapChain;
mSurface = surface;
}
/**
- * @return the native Object this SwapChain was created from.
+ * @return the native Object this SwapChain was created from or null
+ * for a headless SwapChain.
*/
- @NonNull
public Object getNativeWindow() {
return mSurface;
}
diff --git a/filament/backend/include/private/backend/DriverAPI.inc b/filament/backend/include/private/backend/DriverAPI.inc
index fefae305ae..869538b0c6 100644
--- a/filament/backend/include/private/backend/DriverAPI.inc
+++ b/filament/backend/include/private/backend/DriverAPI.inc
@@ -124,6 +124,9 @@ DECL_DRIVER_API_N(endFrame,
// can start rendering. e.g. correspond to glFlush() for a GLES driver.
DECL_DRIVER_API_0(flush)
+// flush and wait for the effects to be done
+DECL_DRIVER_API_0(finish)
+
/*
* Creating driver objects
* -----------------------
@@ -180,6 +183,11 @@ DECL_DRIVER_API_R_N(backend::SwapChainHandle, createSwapChain,
void*, nativeWindow,
uint64_t, flags)
+DECL_DRIVER_API_R_N(backend::SwapChainHandle, createSwapChainHeadless,
+ uint32_t, width,
+ uint32_t, height,
+ uint64_t, flags)
+
DECL_DRIVER_API_R_N(backend::StreamHandle, createStreamFromTextureId,
intptr_t, externalTextureId,
uint32_t, width,
diff --git a/filament/backend/include/private/backend/OpenGLPlatform.h b/filament/backend/include/private/backend/OpenGLPlatform.h
index 3bf7b93d43..7df84cd238 100644
--- a/filament/backend/include/private/backend/OpenGLPlatform.h
+++ b/filament/backend/include/private/backend/OpenGLPlatform.h
@@ -41,6 +41,10 @@ public:
virtual void terminate() noexcept = 0;
virtual SwapChain* createSwapChain(void* nativeWindow, uint64_t& flags) noexcept = 0;
+
+ // headless swapchain
+ virtual SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t& flags) noexcept = 0;
+
virtual void destroySwapChain(SwapChain* swapChain) noexcept = 0;
virtual void createDefaultRenderTarget(uint32_t& framebuffer, uint32_t& colorbuffer,
diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm
index 93400c879a..b1d2f28cc8 100644
--- a/filament/backend/src/metal/MetalDriver.mm
+++ b/filament/backend/src/metal/MetalDriver.mm
@@ -128,8 +128,12 @@ void MetalDriver::endFrame(uint32_t frameId) {
CVMetalTextureCacheFlush(mContext->textureCache, 0);
}
-void MetalDriver::flush(int dummy) {
+void MetalDriver::flush(int) {
+ // TODO: implement flush, equivalent of glFlush() (needed for performance)
+}
+void MetalDriver::finish(int) {
+ // TODO: implement finish, equivalent of glFinish() (needed for unit tests)
}
void MetalDriver::createVertexBufferR(Handle vbh, uint8_t bufferCount,
@@ -218,6 +222,11 @@ void MetalDriver::createSwapChainR(Handle sch, void* nativeWindow,
construct_handle(mHandleMap, sch, mContext->device, metalLayer);
}
+void MetalDriver::createSwapChainHeadlessR(Handle sch,
+ uint32_t width, uint32_t height, uint64_t flags) {
+ // TODO: implement headless swapchain
+}
+
void MetalDriver::createStreamFromTextureIdR(Handle, intptr_t externalTextureId,
uint32_t width, uint32_t height) {
@@ -267,6 +276,10 @@ Handle MetalDriver::createSwapChainS() noexcept {
return alloc_handle();
}
+Handle MetalDriver::createSwapChainHeadlessS() noexcept {
+ return alloc_handle();
+}
+
Handle MetalDriver::createStreamFromTextureIdS() noexcept {
return {};
}
diff --git a/filament/backend/src/opengl/OpenGLContext.cpp b/filament/backend/src/opengl/OpenGLContext.cpp
index 8aaaf14538..13af021c3a 100644
--- a/filament/backend/src/opengl/OpenGLContext.cpp
+++ b/filament/backend/src/opengl/OpenGLContext.cpp
@@ -36,11 +36,7 @@ OpenGLContext::OpenGLContext() noexcept {
UTILS_UNUSED char const* const shader = (char const*) glGetString(GL_SHADING_LANGUAGE_VERSION);
#ifndef NDEBUG
- slog.i
- << vendor << io::endl
- << renderer << io::endl
- << version << io::endl
- << shader << io::endl;
+ slog.i << vendor << ", " << renderer << ", " << version << ", " << shader << io::endl;
#endif
// OpenGL (ES) version
@@ -51,7 +47,8 @@ OpenGLContext::OpenGLContext() noexcept {
glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &gets.max_uniform_block_size);
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &gets.uniform_buffer_offset_alignment);
-#ifndef NDEBUG
+#if 0
+ // this is useful for development, but too verbose even for debug builds
slog.i
<< "GL_MAX_RENDERBUFFER_SIZE = " << gets.max_renderbuffer_size << io::endl
<< "GL_MAX_UNIFORM_BLOCK_SIZE = " << gets.max_uniform_block_size << io::endl
diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp
index fc876afd4f..c86cb5f319 100644
--- a/filament/backend/src/opengl/OpenGLDriver.cpp
+++ b/filament/backend/src/opengl/OpenGLDriver.cpp
@@ -328,7 +328,7 @@ void OpenGLDriver::setRasterStateSlow(RasterState rs) noexcept {
// For reference on a 64-bits machine:
// GLFence : 8
// GLIndexBuffer : 12 moderate
-// GLSamplerGroup : 16 moderate
+// GLSamplerGroup : 16 moderate
// -- less than 16 bytes
// GLRenderPrimitive : 40 many
@@ -351,7 +351,8 @@ OpenGLDriver::HandleAllocator::HandleAllocator(const utils::HeapArea& area)
mPool2( pointermath::add(area.begin(), (6 * area.getSize()) / 16),
area.end()) {
-#ifndef NDEBUG
+#if 0
+ // this is useful for development, but too verbose even for debug builds
slog.d << "HwFence: " << sizeof(HwFence) << io::endl;
slog.d << "GLIndexBuffer: " << sizeof(GLIndexBuffer) << io::endl;
slog.d << "GLSamplerGroup: " << sizeof(GLSamplerGroup) << io::endl;
@@ -464,6 +465,10 @@ Handle OpenGLDriver::createSwapChainS() noexcept {
return Handle( allocateHandle(sizeof(HwSwapChain)) );
}
+Handle OpenGLDriver::createSwapChainHeadlessS() noexcept {
+ return Handle( allocateHandle(sizeof(HwSwapChain)) );
+}
+
Handle OpenGLDriver::createStreamFromTextureIdS() noexcept {
return Handle( allocateHandle(sizeof(GLStream)) );
}
@@ -983,6 +988,14 @@ void OpenGLDriver::createSwapChainR(Handle sch, void* nativeWindow,
sc->swapChain = mPlatform.createSwapChain(nativeWindow, flags);
}
+void OpenGLDriver::createSwapChainHeadlessR(Handle sch,
+ uint32_t width, uint32_t height, uint64_t flags) {
+ DEBUG_MARKER()
+
+ HwSwapChain* sc = construct(sch);
+ sc->swapChain = mPlatform.createSwapChain(width, height, flags);
+}
+
void OpenGLDriver::createStreamFromTextureIdR(Handle sh,
intptr_t externalTextureId, uint32_t width, uint32_t height) {
DEBUG_MARKER()
@@ -2467,6 +2480,11 @@ void OpenGLDriver::flush(int) {
}
}
+void OpenGLDriver::finish(int) {
+ DEBUG_MARKER()
+ glFinish();
+}
+
UTILS_NOINLINE
void OpenGLDriver::clearWithRasterPipe(
bool clearColor, float4 const& linearColor,
diff --git a/filament/backend/src/opengl/PlatformCocoaGL.h b/filament/backend/src/opengl/PlatformCocoaGL.h
index 2c5823d552..8679414d5b 100644
--- a/filament/backend/src/opengl/PlatformCocoaGL.h
+++ b/filament/backend/src/opengl/PlatformCocoaGL.h
@@ -36,6 +36,7 @@ public:
void terminate() noexcept final;
SwapChain* createSwapChain(void* nativewindow, uint64_t& flags) noexcept final;
+ SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t& flags) noexcept final;
void destroySwapChain(SwapChain* swapChain) noexcept final;
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept final;
void commit(SwapChain* swapChain) noexcept final;
diff --git a/filament/backend/src/opengl/PlatformCocoaGL.mm b/filament/backend/src/opengl/PlatformCocoaGL.mm
index f27d2f239a..0369268b6c 100644
--- a/filament/backend/src/opengl/PlatformCocoaGL.mm
+++ b/filament/backend/src/opengl/PlatformCocoaGL.mm
@@ -27,6 +27,8 @@
#include
#include
+#include
+
namespace filament {
using namespace backend;
@@ -34,6 +36,7 @@ using namespace backend;
struct PlatformCocoaGLImpl {
NSOpenGLContext* mGLContext = nullptr;
NSView* mCurrentView = nullptr;
+ std::vector mHeadlessSwapChains;
};
PlatformCocoaGL::PlatformCocoaGL()
@@ -75,12 +78,26 @@ void PlatformCocoaGL::terminate() noexcept {
}
Platform::SwapChain* PlatformCocoaGL::createSwapChain(void* nativewindow, uint64_t& flags) noexcept {
- // Transparent swap chain is not supported
+ // Transparent SwapChain is not supported
flags &= ~backend::SWAP_CHAIN_CONFIG_TRANSPARENT;
return (SwapChain*) nativewindow;
}
+Platform::SwapChain* PlatformCocoaGL::createSwapChain(uint32_t width, uint32_t height, uint64_t& flags) noexcept {
+ NSView* nsView = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, width, height)];
+ // adding the pointer to the array retains the NSView
+ pImpl->mHeadlessSwapChains.push_back(nsView);
+ return (__bridge SwapChain*)nsView;
+}
+
void PlatformCocoaGL::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
+ auto& v = pImpl->mHeadlessSwapChains;
+ NSView* nsView = (__bridge NSView*)swapChain;
+ auto it = std::find(v.begin(), v.end(), nsView);
+ if (it != v.end()) {
+ // removing the pointer from the array releases the NSView
+ v.erase(it);
+ }
}
void PlatformCocoaGL::makeCurrent(Platform::SwapChain* drawSwapChain,
diff --git a/filament/backend/src/opengl/PlatformCocoaTouchGL.h b/filament/backend/src/opengl/PlatformCocoaTouchGL.h
index 7f3e61810c..d2288221b8 100644
--- a/filament/backend/src/opengl/PlatformCocoaTouchGL.h
+++ b/filament/backend/src/opengl/PlatformCocoaTouchGL.h
@@ -36,6 +36,7 @@ public:
void terminate() noexcept final;
SwapChain* createSwapChain(void* nativewindow, uint64_t& flags) noexcept final;
+ SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t& flags) noexcept final;
void destroySwapChain(SwapChain* swapChain) noexcept final;
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept final;
void commit(SwapChain* swapChain) noexcept final;
diff --git a/filament/backend/src/opengl/PlatformCocoaTouchGL.mm b/filament/backend/src/opengl/PlatformCocoaTouchGL.mm
index 1fab394aa3..70f627e50b 100644
--- a/filament/backend/src/opengl/PlatformCocoaTouchGL.mm
+++ b/filament/backend/src/opengl/PlatformCocoaTouchGL.mm
@@ -106,6 +106,11 @@ Platform::SwapChain* PlatformCocoaTouchGL::createSwapChain(void* nativewindow, u
return (SwapChain*) nativewindow;
}
+Platform::SwapChain* PlatformCocoaTouchGL::createSwapChain(uint32_t width, uint32_t height, uint64_t& flags) noexcept {
+ // TODO: implement headless SwapChain
+ return nullptr;
+}
+
void PlatformCocoaTouchGL::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
}
diff --git a/filament/backend/src/opengl/PlatformEGL.cpp b/filament/backend/src/opengl/PlatformEGL.cpp
index 4a2bf9336d..377170ef26 100644
--- a/filament/backend/src/opengl/PlatformEGL.cpp
+++ b/filament/backend/src/opengl/PlatformEGL.cpp
@@ -343,8 +343,10 @@ void PlatformEGL::terminate() noexcept {
Platform::SwapChain* PlatformEGL::createSwapChain(
void* nativeWindow, uint64_t& flags) noexcept {
EGLSurface sur = eglCreateWindowSurface(mEGLDisplay,
- (flags & backend::SWAP_CHAIN_CONFIG_TRANSPARENT) ? mEGLTransparentConfig : mEGLConfig,
+ (flags & backend::SWAP_CHAIN_CONFIG_TRANSPARENT) ?
+ mEGLTransparentConfig : mEGLConfig,
(EGLNativeWindowType)nativeWindow, nullptr);
+
if (UTILS_UNLIKELY(sur == EGL_NO_SURFACE)) {
logEglError("eglCreateWindowSurface");
return nullptr;
@@ -361,6 +363,26 @@ Platform::SwapChain* PlatformEGL::createSwapChain(
return (SwapChain*)sur;
}
+Platform::SwapChain* PlatformEGL::createSwapChain(
+ uint32_t width, uint32_t height, uint64_t& flags) noexcept {
+
+ EGLint attribs[] = {
+ EGL_WIDTH, EGLint(width),
+ EGL_HEIGHT, EGLint(height),
+ EGL_NONE
+ };
+
+ EGLSurface sur = eglCreatePbufferSurface(mEGLDisplay,
+ (flags & backend::SWAP_CHAIN_CONFIG_TRANSPARENT) ?
+ mEGLTransparentConfig : mEGLConfig, attribs);
+
+ if (UTILS_UNLIKELY(sur == EGL_NO_SURFACE)) {
+ logEglError("eglCreatePbufferSurface");
+ return nullptr;
+ }
+ return (SwapChain*)sur;
+}
+
void PlatformEGL::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
EGLSurface sur = (EGLSurface) swapChain;
if (sur != EGL_NO_SURFACE) {
diff --git a/filament/backend/src/opengl/PlatformEGL.h b/filament/backend/src/opengl/PlatformEGL.h
index 4cbecba323..f68705006e 100644
--- a/filament/backend/src/opengl/PlatformEGL.h
+++ b/filament/backend/src/opengl/PlatformEGL.h
@@ -43,6 +43,7 @@ public:
void terminate() noexcept override;
SwapChain* createSwapChain(void* nativewindow, uint64_t& flags) noexcept final;
+ SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t& flags) noexcept final;
void destroySwapChain(SwapChain* swapChain) noexcept final;
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept final;
void commit(SwapChain* swapChain) noexcept final;
diff --git a/filament/backend/src/opengl/PlatformGLX.cpp b/filament/backend/src/opengl/PlatformGLX.cpp
index 92d086efd5..aea8661591 100644
--- a/filament/backend/src/opengl/PlatformGLX.cpp
+++ b/filament/backend/src/opengl/PlatformGLX.cpp
@@ -238,15 +238,34 @@ void PlatformGLX::terminate() noexcept {
bluegl::unbind();
}
-Platform::SwapChain* PlatformGLX::createSwapChain(
- void* nativeWindow, uint64_t& flags) noexcept {
-
+Platform::SwapChain* PlatformGLX::createSwapChain(void* nativeWindow, uint64_t& flags) noexcept {
// Transparent swap chain is not supported
flags &= ~backend::SWAP_CHAIN_CONFIG_TRANSPARENT;
return (SwapChain*) nativeWindow;
}
-void PlatformGLX::destroySwapChain(Platform::SwapChain* /*swapChain*/) noexcept {
+Platform::SwapChain* PlatformGLX::createSwapChain(
+ uint32_t width, uint32_t height, uint64_t& flags) noexcept {
+ // Transparent swap chain is not supported
+ flags &= ~backend::SWAP_CHAIN_CONFIG_TRANSPARENT;
+ int pbufferAttribs[] = {
+ GLX_PBUFFER_WIDTH, int(width),
+ GLX_PBUFFER_HEIGHT, int(height),
+ GL_NONE
+ };
+ GLXPbuffer sur = g_glx.createPbuffer(mGLXDisplay, mGLXConfig[0], pbufferAttribs);
+ if (sur) {
+ mPBuffers.push_back(sur);
+ }
+ return (SwapChain*) sur;
+}
+
+void PlatformGLX::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
+ auto it = std::find(mPBuffers.begin(), mPBuffers.end(), (GLXPbuffer)swapChain);
+ if (it != mPBuffers.end()) {
+ g_glx.destroyPbuffer(mGLXDisplay, (GLXPbuffer)swapChain);
+ mPBuffers.erase(it);
+ }
}
void PlatformGLX::makeCurrent(
diff --git a/filament/backend/src/opengl/PlatformGLX.h b/filament/backend/src/opengl/PlatformGLX.h
index 76cc51f7ea..45c3abf1fa 100644
--- a/filament/backend/src/opengl/PlatformGLX.h
+++ b/filament/backend/src/opengl/PlatformGLX.h
@@ -26,6 +26,8 @@
#include "private/backend/OpenGLPlatform.h"
+#include
+
namespace filament {
class PlatformGLX final : public backend::OpenGLPlatform {
@@ -36,6 +38,7 @@ public:
void terminate() noexcept override;
SwapChain* createSwapChain(void* nativewindow, uint64_t& flags) noexcept override;
+ SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t& flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
void commit(SwapChain* swapChain) noexcept override;
@@ -64,6 +67,7 @@ private:
GLXContext mGLXContext;
GLXFBConfig* mGLXConfig;
GLXPbuffer mDummySurface;
+ std::vector mPBuffers;
};
} // namespace filament
diff --git a/filament/backend/src/opengl/PlatformWGL.cpp b/filament/backend/src/opengl/PlatformWGL.cpp
index 413fa7bdd1..7d9060f1b7 100644
--- a/filament/backend/src/opengl/PlatformWGL.cpp
+++ b/filament/backend/src/opengl/PlatformWGL.cpp
@@ -182,6 +182,11 @@ Platform::SwapChain* PlatformWGL::createSwapChain(void* nativeWindow, uint64_t&
return swapChain;
}
+Platform::SwapChain* PlatformWGL::createSwapChain(uint32_t width, uint32_t height, uint64_t& flags) noexcept {
+ // TODO: implement headless SwapChain
+ return nullptr;
+}
+
void PlatformWGL::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
HDC dc = (HDC) swapChain;
HWND window = WindowFromDC(dc);
diff --git a/filament/backend/src/opengl/PlatformWGL.h b/filament/backend/src/opengl/PlatformWGL.h
index 955c9b5656..467352ea5a 100644
--- a/filament/backend/src/opengl/PlatformWGL.h
+++ b/filament/backend/src/opengl/PlatformWGL.h
@@ -34,6 +34,7 @@ public:
void terminate() noexcept override;
SwapChain* createSwapChain(void* nativewindow, uint64_t& flags) noexcept override;
+ SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t& flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
void commit(SwapChain* swapChain) noexcept override;
diff --git a/filament/backend/src/opengl/PlatformWebGL.cpp b/filament/backend/src/opengl/PlatformWebGL.cpp
index 00305c610d..bf2b73c38e 100644
--- a/filament/backend/src/opengl/PlatformWebGL.cpp
+++ b/filament/backend/src/opengl/PlatformWebGL.cpp
@@ -33,6 +33,12 @@ Platform::SwapChain* PlatformWebGL::createSwapChain(
return (SwapChain*) nativeWindow;
}
+Platform::SwapChain* PlatformWebGL::createSwapChain(
+ uint32_t width, uint32_t height, uint64_t& flags) noexcept {
+ // TODO: implement headless SwapChain
+ return nullptr;
+}
+
void PlatformWebGL::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
}
diff --git a/filament/backend/src/opengl/PlatformWebGL.h b/filament/backend/src/opengl/PlatformWebGL.h
index 8208cd4d48..906cd80047 100644
--- a/filament/backend/src/opengl/PlatformWebGL.h
+++ b/filament/backend/src/opengl/PlatformWebGL.h
@@ -35,6 +35,7 @@ public:
void terminate() noexcept override;
SwapChain* createSwapChain(void* nativewindow, uint64_t& flags) noexcept final override;
+ SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t& flags) noexcept final override;
void destroySwapChain(SwapChain* swapChain) noexcept final override;
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept final override;
void commit(SwapChain* swapChain) noexcept final override;
diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp
index 7d51fa495d..16908c9a94 100644
--- a/filament/backend/src/vulkan/VulkanDriver.cpp
+++ b/filament/backend/src/vulkan/VulkanDriver.cpp
@@ -283,6 +283,10 @@ void VulkanDriver::flush(int) {
// Todo: equivalent of glFlush()
}
+void VulkanDriver::finish(int) {
+ // Todo: equivalent of glFinish()
+}
+
void VulkanDriver::createSamplerGroupR(Handle sbh, size_t count) {
construct_handle(mHandleMap, sbh, mContext, count);
}
@@ -436,6 +440,12 @@ void VulkanDriver::createSwapChainR(Handle sch, void* nativeWindow,
mContext.currentSurface = ≻
}
+void VulkanDriver::createSwapChainHeadlessR(Handle sch,
+ uint32_t width, uint32_t height, uint64_t flags) {
+ //auto* swapChain = construct_handle(mHandleMap, sch);
+ // TODO: implement headless swapchain
+}
+
void VulkanDriver::createStreamFromTextureIdR(Handle sh, intptr_t externalTextureId,
uint32_t width, uint32_t height) {
}
@@ -484,6 +494,10 @@ Handle VulkanDriver::createSwapChainS() noexcept {
return alloc_handle();
}
+Handle VulkanDriver::createSwapChainHeadlessS() noexcept {
+ return alloc_handle();
+}
+
Handle VulkanDriver::createStreamFromTextureIdS() noexcept {
return {};
}
diff --git a/filament/include/filament/Engine.h b/filament/include/filament/Engine.h
index 9bccff2bd4..a6296a8387 100644
--- a/filament/include/filament/Engine.h
+++ b/filament/include/filament/Engine.h
@@ -259,6 +259,20 @@ public:
*/
SwapChain* createSwapChain(void* nativeWindow, uint64_t flags = 0) noexcept;
+
+ /**
+ * Creates a headless SwapChain.
+ *
+ * @param width Width of the drawing buffer in pixels.
+ * @param height Height of the drawing buffer in pixels.
+ * @param flags One or more configuration flags as defined in `SwapChain`.
+ *
+ * @return A pointer to the newly created SwapChain or nullptr if it couldn't be created.
+ *
+ * @see Renderer.beginFrame()
+ */
+ SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags = 0) noexcept;
+
/**
* Creates a renderer associated to this engine.
*
diff --git a/filament/src/Engine.cpp b/filament/src/Engine.cpp
index 9de0f5806b..0fd55e1328 100644
--- a/filament/src/Engine.cpp
+++ b/filament/src/Engine.cpp
@@ -379,7 +379,15 @@ void FEngine::flush() {
}
void FEngine::flushAndWait() {
- FFence::waitAndDestroy(FEngine::createFence(FFence::Type::SOFT), FFence::Mode::FLUSH);
+ // enqueue finish command -- this will stall in the driver until the GPU is done
+ getDriverApi().finish();
+
+ // then create a fence that will trigger when we're past the finish() above
+ FFence::waitAndDestroy(
+ FEngine::createFence(FFence::Type::SOFT), FFence::Mode::FLUSH);
+
+ // finally, execute callbacks that might have been scheduled
+ getDriver().purge();
}
// -----------------------------------------------------------------------------------------------
@@ -587,6 +595,14 @@ FSwapChain* FEngine::createSwapChain(void* nativeWindow, uint64_t flags) noexcep
return p;
}
+FSwapChain* FEngine::createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept {
+ FSwapChain* p = mHeapAllocator.make(*this, width, height, flags);
+ if (p) {
+ mSwapChains.insert(p);
+ }
+ return p;
+}
+
/*
* Objects created with a component manager
*/
@@ -847,6 +863,10 @@ SwapChain* Engine::createSwapChain(void* nativeWindow, uint64_t flags) noexcept
return upcast(this)->createSwapChain(nativeWindow, flags);
}
+SwapChain* Engine::createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept {
+ return upcast(this)->createSwapChain(width, height, flags);
+}
+
void Engine::destroy(const VertexBuffer* p) {
upcast(this)->destroy(upcast(p));
}
diff --git a/filament/src/SwapChain.cpp b/filament/src/SwapChain.cpp
index 4d303f5389..d0ebca2072 100644
--- a/filament/src/SwapChain.cpp
+++ b/filament/src/SwapChain.cpp
@@ -22,9 +22,13 @@ namespace filament {
namespace details {
FSwapChain::FSwapChain(FEngine& engine, void* nativeWindow, uint64_t flags)
- : mNativeWindow(nativeWindow) {
- mConfigFlags = flags;
- mSwapChain = engine.getDriverApi().createSwapChain(nativeWindow, mConfigFlags);
+ : mNativeWindow(nativeWindow), mConfigFlags(flags) {
+ mSwapChain = engine.getDriverApi().createSwapChain(nativeWindow, flags);
+}
+
+FSwapChain::FSwapChain(FEngine& engine, uint32_t width, uint32_t height, uint64_t flags)
+ : mConfigFlags(flags) {
+ mSwapChain = engine.getDriverApi().createSwapChainHeadless(width, height, flags);
}
void FSwapChain::terminate(FEngine& engine) noexcept {
diff --git a/filament/src/details/Engine.h b/filament/src/details/Engine.h
index 6ccc8c0e68..9251ae8069 100644
--- a/filament/src/details/Engine.h
+++ b/filament/src/details/Engine.h
@@ -239,6 +239,7 @@ public:
FView* createView() noexcept;
FFence* createFence(FFence::Type type) noexcept;
FSwapChain* createSwapChain(void* nativeWindow, uint64_t flags) noexcept;
+ FSwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept;
FCamera* createCamera(utils::Entity entity) noexcept;
FCamera* getCameraComponent(utils::Entity entity) noexcept;
diff --git a/filament/src/details/SwapChain.h b/filament/src/details/SwapChain.h
index b7d2781d8b..9d562a6ea6 100644
--- a/filament/src/details/SwapChain.h
+++ b/filament/src/details/SwapChain.h
@@ -33,6 +33,7 @@ class FEngine;
class FSwapChain : public SwapChain {
public:
FSwapChain(FEngine& engine, void* nativeWindow, uint64_t flags);
+ FSwapChain(FEngine& engine, uint32_t width, uint32_t height, uint64_t flags);
void terminate(FEngine& engine) noexcept;
void makeCurrent(backend::DriverApi& driverApi) noexcept {
diff --git a/filament/test/CMakeLists.txt b/filament/test/CMakeLists.txt
index 68642665a9..4e8605742a 100644
--- a/filament/test/CMakeLists.txt
+++ b/filament/test/CMakeLists.txt
@@ -9,7 +9,7 @@ if(NOT IOS AND NOT WEBGL)
# The following tests rely on private APIs that are stripped
# away in Release builds
if (TNT_DEV)
- add_executable(test_${TARGET} filament_test_exposure.cpp filament_framegraph_test.cpp filament_test.cpp)
+ add_executable(test_${TARGET} filament_test_exposure.cpp filament_rendering_test.cpp filament_framegraph_test.cpp filament_test.cpp)
target_link_libraries(test_${TARGET} PRIVATE filament gtest)
target_compile_options(test_${TARGET} PRIVATE ${COMPILER_FLAGS})
diff --git a/filament/test/filament_rendering_test.cpp b/filament/test/filament_rendering_test.cpp
new file mode 100644
index 0000000000..c03cb0eaf3
--- /dev/null
+++ b/filament/test/filament_rendering_test.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2019 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
+
+#include
+#include
+#include
+#include
+
+using namespace filament;
+using namespace backend;
+
+class RenderingTest : public testing::Test {
+protected:
+ Engine* mEngine = nullptr;
+ SwapChain* mSurface = nullptr;
+ Renderer* mRenderer = nullptr;
+ View* mView = nullptr;
+ Scene* mScene = nullptr;
+ Camera* mCamera = nullptr;
+
+ using closure_t = std::function;
+
+ void SetUp() override {
+ mEngine = Engine::create();
+ mSurface = mEngine->createSwapChain(16, 16);
+ mRenderer = mEngine->createRenderer();
+
+ mScene = mEngine->createScene();
+ mCamera = mEngine->createCamera();
+
+ mView = mEngine->createView();
+ mView->setViewport({0, 0, 16, 16});
+ mView->setScene(mScene);
+ mView->setCamera(mCamera);
+ }
+
+ void TearDown() override {
+ mEngine->destroy(mCamera);
+ mEngine->destroy(mScene);
+ mEngine->destroy(mView);
+ mEngine->destroy(mRenderer);
+ mEngine->destroy(mSurface);
+ Engine::destroy(&mEngine);
+ }
+
+ void runTest(closure_t closure) {
+ auto* user = new closure_t(std::move(closure));
+
+ size_t size = 16 * 16 * 4;
+ void* buffer = malloc(size);
+ memset(buffer, 0, size);
+ PixelBufferDescriptor pd(buffer, size,
+ PixelDataFormat::RGBA, PixelDataType::UBYTE,
+ callback, user);
+
+ Renderer* pRenderer = mRenderer;
+ pRenderer->beginFrame(mSurface);
+ pRenderer->render(mView);
+ pRenderer->readPixels(0, 0, 16, 16, std::move(pd));
+ pRenderer->endFrame();
+
+ // Note: this is where the runTest() callback will be called.
+ mEngine->flushAndWait();
+ }
+
+private:
+ static void callback(void* buffer, size_t size, void* user) {
+ closure_t* closure = (closure_t *)user;
+ uint8_t const* rgba = (uint8_t const*)buffer;
+ (*closure)(rgba, 16, 16);
+ delete closure;
+ ::free(buffer);
+ }
+};
+
+TEST_F(RenderingTest, ClearRed) {
+ mView->setClearColor(LinearColorA{1, 0, 0, 1});
+ mView->setToneMapping(View::ToneMapping::LINEAR);
+ mView->setDithering(View::Dithering::NONE);
+ runTest([this](uint8_t const* rgba, uint32_t width, uint32_t height) {
+ EXPECT_EQ(rgba[0], 0xff);
+ EXPECT_EQ(rgba[1], 0);
+ EXPECT_EQ(rgba[2], 0);
+ EXPECT_EQ(rgba[3], 0xff);
+ });
+}
+
+TEST_F(RenderingTest, ClearGreen) {
+ mView->setClearColor(LinearColorA{0, 1, 0, 1});
+ mView->setToneMapping(View::ToneMapping::LINEAR);
+ mView->setDithering(View::Dithering::NONE);
+ runTest([this](uint8_t const* rgba, uint32_t width, uint32_t height) {
+ EXPECT_EQ(rgba[0], 0);
+ EXPECT_EQ(rgba[1], 0xff);
+ EXPECT_EQ(rgba[2], 0);
+ EXPECT_EQ(rgba[3], 0xff);
+ });
+}