Fix incorrect assumptions of native GraphicBuffer ctor/dtor/lifetime.
authorJarkko Pöyry <jpoyry@google.com>
Thu, 16 Apr 2015 20:35:48 +0000 (13:35 -0700)
committerJarkko Pöyry <jpoyry@google.com>
Fri, 17 Apr 2015 00:17:23 +0000 (17:17 -0700)
- Do not expect C1 ctor to return this on x86 and ARM64.
- Use initCheck() to check if ctor failed.
- Use native object refcounting methods to handle object lifetime.

Bug: 20218384
Change-Id: Ia5f22c841291655df4970988bde1fe142c85bff5

framework/platform/android/tcuAndroidInternals.cpp
framework/platform/android/tcuAndroidInternals.hpp

index d825b85..368f3e0 100644 (file)
@@ -22,6 +22,8 @@
  *//*--------------------------------------------------------------------*/
 
 #include "tcuAndroidInternals.hpp"
+#include "deMemory.h"
+#include "deStringUtil.hpp"
 
 namespace tcu
 {
@@ -51,20 +53,200 @@ LibUI::LibUI (void)
        setFuncPtr(gb.getNativeBuffer,  m_library,      "_ZNK7android13GraphicBuffer15getNativeBufferEv");
        setFuncPtr(gb.lock,                             m_library,      "_ZN7android13GraphicBuffer4lockEjPPv");
        setFuncPtr(gb.unlock,                   m_library,      "_ZN7android13GraphicBuffer6unlockEv");
+       setFuncPtr(gb.initCheck,                m_library,      "_ZNK7android13GraphicBuffer9initCheckEv");
 }
 
 #define GRAPHICBUFFER_SIZE 1024 // Hopefully enough
 
+typedef void (*GenericFptr)();
+
+//! call constructor with 4 arguments
+template <typename RT, typename T1, typename T2, typename T3, typename T4>
+RT* callConstructor4 (GenericFptr fptr, void* memory, size_t memorySize, T1 param1, T2 param2, T3 param3, T4 param4)
+{
+       DE_UNREF(memorySize);
+
+#if (DE_CPU == DE_CPU_ARM)
+       // C1 constructors return pointer
+       typedef RT* (*ABIFptr)(void*, T1, T2, T3, T4);
+       (void)((ABIFptr)fptr)(memory, param1, param2, param3, param4);
+       return reinterpret_cast<RT*>(memory);
+#elif (DE_CPU == DE_CPU_ARM_64)
+       // C1 constructors return void
+       typedef void (*ABIFptr)(void*, T1, T2, T3, T4);
+       ((ABIFptr)fptr)(memory, param1, param2, param3, param4);
+       return reinterpret_cast<RT*>(memory);
+#elif (DE_CPU == DE_CPU_X86)
+       // ctor returns void
+       typedef void (*ABIFptr)(void*, T1, T2, T3, T4);
+       ((ABIFptr)fptr)(memory, param1, param2, param3, param4);
+       return reinterpret_cast<RT*>(memory);
+#elif (DE_CPU == DE_CPU_X86_64)
+       // ctor returns void
+       typedef void (*ABIFptr)(void*, T1, T2, T3, T4);
+       ((ABIFptr)fptr)(memory, param1, param2, param3, param4);
+       return reinterpret_cast<RT*>(memory);
+#else
+       TCU_THROW(NotSupportedError, "ABI not supported");
+       return DE_NULL;
+#endif
+}
+
+template <typename T>
+void callDestructor (GenericFptr fptr, T* obj)
+{
+#if (DE_CPU == DE_CPU_ARM)
+       // D1 destructor returns ptr
+       typedef void* (*ABIFptr)(T* obj);
+       (void)((ABIFptr)fptr)(obj);
+#elif (DE_CPU == DE_CPU_ARM_64)
+       // D1 destructor returns void
+       typedef void (*ABIFptr)(T* obj);
+       ((ABIFptr)fptr)(obj);
+#elif (DE_CPU == DE_CPU_X86)
+       // dtor returns void
+       typedef void (*ABIFptr)(T* obj);
+       ((ABIFptr)fptr)(obj);
+#elif (DE_CPU == DE_CPU_X86_64)
+       // dtor returns void
+       typedef void (*ABIFptr)(T* obj);
+       ((ABIFptr)fptr)(obj);
+#else
+       TCU_THROW(NotSupportedError, "ABI not supported");
+#endif
+}
+
+template<typename T1, typename T2>
+T1* pointerToOffset (T2* ptr, size_t bytes)
+{
+       return reinterpret_cast<T1*>((deUint8*)ptr + bytes);
+}
+
+static android::android_native_base_t* getAndroidNativeBase (android::GraphicBuffer* gb)
+{
+       // \note: assuming Itanium ABI
+       return pointerToOffset<android::android_native_base_t>(gb, 2 * DE_PTR_SIZE);
+}
+
+//! android_native_base_t::magic for ANativeWindowBuffer
+static deInt32 getExpectedNativeBufferVersion (void)
+{
+#if (DE_PTR_SIZE == 4)
+       return 96;
+#elif (DE_PTR_SIZE == 8)
+       return 168;
+#else
+#      error Invalid DE_PTR_SIZE
+#endif
+}
+
+//! access android_native_base_t::magic
+static deUint32 getNativeBaseMagic (android::android_native_base_t* base)
+{
+       return *pointerToOffset<deUint32>(base, 0);
+}
+
+//! access android_native_base_t::version
+static deUint32 getNativeBaseVersion (android::android_native_base_t* base)
+{
+       return *pointerToOffset<deInt32>(base, 4);
+}
+
+//! access android_native_base_t::incRef
+static NativeBaseFunctions::incRefFunc getNativeBaseIncRefFunc (android::android_native_base_t* base)
+{
+       return *pointerToOffset<NativeBaseFunctions::incRefFunc>(base, 8 + DE_PTR_SIZE*4);
+}
+
+//! access android_native_base_t::decRef
+static NativeBaseFunctions::decRefFunc getNativeBaseDecRefFunc (android::android_native_base_t* base)
+{
+       return *pointerToOffset<NativeBaseFunctions::decRefFunc>(base, 8 + DE_PTR_SIZE*5);
+}
+
+static android::GraphicBuffer* createGraphicBuffer (const GraphicBufferFunctions& functions, NativeBaseFunctions& baseFunctions, deUint32 w, deUint32 h, PixelFormat format, deUint32 usage)
+{
+       // \note: Hopefully uses the same allocator as libui
+       void* const memory = deMalloc(GRAPHICBUFFER_SIZE);
+       if (memory == DE_NULL)
+               TCU_THROW(ResourceError, "Could not alloc for GraphicBuffer");
+       else
+       {
+               try
+               {
+                       android::GraphicBuffer* const                   gb                      = callConstructor4<android::GraphicBuffer, deUint32, deUint32, PixelFormat, deUint32>(functions.constructor,
+                                                                                                                                                                                                                                                                                                         memory,
+                                                                                                                                                                                                                                                                                                         GRAPHICBUFFER_SIZE,
+                                                                                                                                                                                                                                                                                                         w,
+                                                                                                                                                                                                                                                                                                         h,
+                                                                                                                                                                                                                                                                                                         format,
+                                                                                                                                                                                                                                                                                                         usage);
+                       android::android_native_base_t* const   base            = getAndroidNativeBase(gb);
+                       status_t                                                                ctorStatus      = functions.initCheck(gb);
+
+                       if (ctorStatus)
+                       {
+                               // ctor failed
+                               callDestructor<android::GraphicBuffer>(functions.destructor, gb);
+                               TCU_THROW(NotSupportedError, ("GraphicBuffer ctor failed, initCheck returned " + de::toString(ctorStatus)).c_str());
+                       }
+
+                       // check object layout
+                       {
+                               const deUint32 magic            = getNativeBaseMagic(base);
+                               const deUint32 bufferMagic      = 0x5f626672u; // "_bfr"
+
+                               if (magic != bufferMagic)
+                                       TCU_THROW(NotSupportedError, "GraphicBuffer layout unexpected");
+                       }
+
+                       // check object version
+                       {
+                               const deInt32 version                   = getNativeBaseVersion(base);
+                               const deInt32 expectedVersion   = getExpectedNativeBufferVersion();
+
+                               if (version != expectedVersion)
+                                       TCU_THROW(NotSupportedError, "GraphicBuffer version unexpected");
+                       }
+
+                       // locate refcounting functions
+
+                       if (!baseFunctions.incRef || !baseFunctions.decRef)
+                       {
+                               baseFunctions.incRef = getNativeBaseIncRefFunc(base);
+                               baseFunctions.decRef = getNativeBaseDecRefFunc(base);
+                       }
+
+                       // take the initial reference and return
+                       baseFunctions.incRef(base);
+                       return gb;
+               }
+               catch (...)
+               {
+                       deFree(memory);
+                       throw;
+               }
+       }
+}
+
 GraphicBuffer::GraphicBuffer (const LibUI& lib, deUint32 width, deUint32 height, PixelFormat format, deUint32 usage)
        : m_functions   (lib.getFunctions().graphicBuffer)
-       , m_memory              (GRAPHICBUFFER_SIZE) // vector<char> (new char[]) is max-aligned
-       , m_impl                (m_functions.constructor(&m_memory.front(), width, height, format, usage))
+       , m_impl                (DE_NULL)
 {
+       m_baseFunctions.incRef = DE_NULL;
+       m_baseFunctions.decRef = DE_NULL;
+
+       // \note createGraphicBuffer updates m_baseFunctions
+       m_impl = createGraphicBuffer(m_functions, m_baseFunctions, width, height, format, usage);
 }
 
 GraphicBuffer::~GraphicBuffer (void)
 {
-       m_functions.destructor(m_impl);
+       if (m_impl && m_baseFunctions.decRef)
+       {
+               m_baseFunctions.decRef(getAndroidNativeBase(m_impl));
+               m_impl = DE_NULL;
+       }
 }
 
 status_t GraphicBuffer::lock (deUint32 usage, void** vaddr)
index e6754c6..1d7d231 100644 (file)
@@ -35,6 +35,7 @@ struct ANativeWindowBuffer;
 namespace android
 {
 class GraphicBuffer;
+class android_native_base_t;
 }
 
 namespace tcu
@@ -96,17 +97,28 @@ typedef deInt32 PixelFormat;
 // ui/GraphicBuffer.h
 struct GraphicBufferFunctions
 {
-       typedef android::GraphicBuffer* (*constructorFunc)              (void* memory, deUint32 w, deUint32 h, PixelFormat format, deUint32 usage);
-       typedef void*                                   (*destructorFunc)               (android::GraphicBuffer* buffer);
+       typedef void                                    (*genericFunc)                  ();
+       typedef status_t                                (*initCheckFunc)                (android::GraphicBuffer* buffer);
        typedef status_t                                (*lockFunc)                             (android::GraphicBuffer* buffer, deUint32 usage, void** vaddr);
        typedef status_t                                (*unlockFunc)                   (android::GraphicBuffer* buffer);
        typedef ANativeWindowBuffer*    (*getNativeBufferFunc)  (const android::GraphicBuffer* buffer);
 
-       constructorFunc                                 constructor;
-       destructorFunc                                  destructor;
+       genericFunc                                             constructor;
+       genericFunc                                             destructor;
        lockFunc                                                lock;
        unlockFunc                                              unlock;
        getNativeBufferFunc                             getNativeBuffer;
+       initCheckFunc                                   initCheck;
+};
+
+// system/window.h
+struct NativeBaseFunctions
+{
+       typedef void    (*incRefFunc)                   (android::android_native_base_t* base);
+       typedef void    (*decRefFunc)                   (android::android_native_base_t* base);
+
+       incRefFunc              incRef;
+       decRefFunc              decRef;
 };
 
 struct LibUIFunctions
@@ -166,7 +178,7 @@ public:
 
 private:
        const GraphicBufferFunctions&   m_functions;
-       std::vector<deUint8>                    m_memory;
+       NativeBaseFunctions                             m_baseFunctions;
        android::GraphicBuffer*                 m_impl;
 };