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:
committed by
Mathias Agopian
parent
dd45703d73
commit
bf72adbb57
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {};
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 = ≻
|
||||
}
|
||||
|
||||
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 {};
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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})
|
||||
|
||||
|
||||
113
filament/test/filament_rendering_test.cpp
Normal file
113
filament/test/filament_rendering_test.cpp
Normal 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);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user