Implement Headless SwapChain mode.

This is useful for unit tests. The content of the swapchain can be
read to main memory with Renderer::readPixels().


This PR doesn't implement this new feature in the following backends and
platforms (TODO):

VulkanDriver.cpp
MetalDriver.mm

PlatformWGL.cpp
PlatformWebGL.cpp
PlatformCocoaTouchGL.mm
This commit is contained in:
Mathias Agopian
2019-11-01 14:46:23 -07:00
committed by Mathias Agopian
parent dd45703d73
commit bf72adbb57
28 changed files with 347 additions and 23 deletions

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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 <code>Object</code> this <code>SwapChain</code> was created from.
* @return the native <code>Object</code> this <code>SwapChain</code> was created from or null
* for a headless SwapChain.
*/
@NonNull
public Object getNativeWindow() {
return mSurface;
}

View File

@@ -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,

View File

@@ -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,

View File

@@ -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<HwVertexBuffer> vbh, uint8_t bufferCount,
@@ -218,6 +222,11 @@ void MetalDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
construct_handle<MetalSwapChain>(mHandleMap, sch, mContext->device, metalLayer);
}
void MetalDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch,
uint32_t width, uint32_t height, uint64_t flags) {
// TODO: implement headless swapchain
}
void MetalDriver::createStreamFromTextureIdR(Handle<HwStream>, intptr_t externalTextureId,
uint32_t width, uint32_t height) {
@@ -267,6 +276,10 @@ Handle<HwSwapChain> MetalDriver::createSwapChainS() noexcept {
return alloc_handle<MetalSwapChain, HwSwapChain>();
}
Handle<HwSwapChain> MetalDriver::createSwapChainHeadlessS() noexcept {
return alloc_handle<MetalSwapChain, HwSwapChain>();
}
Handle<HwStream> MetalDriver::createStreamFromTextureIdS() noexcept {
return {};
}

View File

@@ -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

View File

@@ -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<HwSwapChain> OpenGLDriver::createSwapChainS() noexcept {
return Handle<HwSwapChain>( allocateHandle(sizeof(HwSwapChain)) );
}
Handle<HwSwapChain> OpenGLDriver::createSwapChainHeadlessS() noexcept {
return Handle<HwSwapChain>( allocateHandle(sizeof(HwSwapChain)) );
}
Handle<HwStream> OpenGLDriver::createStreamFromTextureIdS() noexcept {
return Handle<HwStream>( allocateHandle(sizeof(GLStream)) );
}
@@ -983,6 +988,14 @@ void OpenGLDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
sc->swapChain = mPlatform.createSwapChain(nativeWindow, flags);
}
void OpenGLDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch,
uint32_t width, uint32_t height, uint64_t flags) {
DEBUG_MARKER()
HwSwapChain* sc = construct<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()
@@ -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,

View File

@@ -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;

View File

@@ -27,6 +27,8 @@
#include <OpenGL/OpenGL.h>
#include <Cocoa/Cocoa.h>
#include <vector>
namespace filament {
using namespace backend;
@@ -34,6 +36,7 @@ using namespace backend;
struct PlatformCocoaGLImpl {
NSOpenGLContext* mGLContext = nullptr;
NSView* mCurrentView = nullptr;
std::vector<NSView*> 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,

View File

@@ -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;

View File

@@ -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 {
}

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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(

View File

@@ -26,6 +26,8 @@
#include "private/backend/OpenGLPlatform.h"
#include <vector>
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<GLXPbuffer> mPBuffers;
};
} // namespace filament

View File

@@ -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);

View File

@@ -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;

View File

@@ -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 {
}

View File

@@ -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;

View File

@@ -283,6 +283,10 @@ void VulkanDriver::flush(int) {
// Todo: equivalent of glFlush()
}
void VulkanDriver::finish(int) {
// Todo: equivalent of glFinish()
}
void VulkanDriver::createSamplerGroupR(Handle<HwSamplerGroup> sbh, size_t count) {
construct_handle<VulkanSamplerGroup>(mHandleMap, sbh, mContext, count);
}
@@ -436,6 +440,12 @@ void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
mContext.currentSurface = &sc;
}
void VulkanDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch,
uint32_t width, uint32_t height, uint64_t flags) {
//auto* swapChain = construct_handle<VulkanSwapChain>(mHandleMap, sch);
// TODO: implement headless swapchain
}
void VulkanDriver::createStreamFromTextureIdR(Handle<HwStream> sh, intptr_t externalTextureId,
uint32_t width, uint32_t height) {
}
@@ -484,6 +494,10 @@ Handle<HwSwapChain> VulkanDriver::createSwapChainS() noexcept {
return alloc_handle<VulkanSwapChain, HwSwapChain>();
}
Handle<HwSwapChain> VulkanDriver::createSwapChainHeadlessS() noexcept {
return alloc_handle<VulkanSwapChain, HwSwapChain>();
}
Handle<HwStream> VulkanDriver::createStreamFromTextureIdS() noexcept {
return {};
}

View File

@@ -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.
*

View File

@@ -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<FSwapChain>(*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));
}

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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})

View File

@@ -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 <gtest/gtest.h>
#include <filament/Engine.h>
#include <filament/Renderer.h>
#include <filament/View.h>
#include <backend/PixelBufferDescriptor.h>
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(uint8_t const* rgba, uint32_t width, uint32_t height)>;
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);
});
}