/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "EGLUtils.h"

#include "GLContextEGL.h"

namespace mozilla {
namespace gl {

bool
DoesEGLContextSupportSharingWithEGLImage(GLContext* gl)
{
    return sEGLLibrary.HasKHRImageBase() &&
           sEGLLibrary.HasKHRImageTexture2D() &&
           gl->IsExtensionSupported(GLContext::OES_EGL_image);
}

EGLImage
CreateEGLImage(GLContext* gl, GLuint tex)
{
    MOZ_ASSERT(DoesEGLContextSupportSharingWithEGLImage(gl));

    EGLClientBuffer clientBuffer = (EGLClientBuffer)((uint64_t)tex);
    EGLContext eglContext = GLContextEGL::Cast(gl)->mContext;
    EGLImage image = sEGLLibrary.fCreateImage(EGL_DISPLAY(),
                                              eglContext,
                                              LOCAL_EGL_GL_TEXTURE_2D,
                                              clientBuffer,
                                              nullptr);
    return image;
}

////////////////////////////////////////////////////////////////////////
// EGLImageWrapper

/*static*/ EGLImageWrapper*
EGLImageWrapper::Create(GLContext* gl, GLuint tex)
{
    MOZ_ASSERT(DoesEGLContextSupportSharingWithEGLImage(gl));

    GLLibraryEGL& library = sEGLLibrary;
    EGLDisplay display = EGL_DISPLAY();
    EGLContext eglContext = GLContextEGL::Cast(gl)->mContext;
    EGLClientBuffer clientBuffer = (EGLClientBuffer)((uint64_t)tex);
    EGLImage image = library.fCreateImage(display,
                                          eglContext,
                                          LOCAL_EGL_GL_TEXTURE_2D,
                                          clientBuffer,
                                          nullptr);
    if (!image) {
#ifdef DEBUG
        printf_stderr("Could not create EGL images: ERROR (0x%04x)\n",
                      sEGLLibrary.fGetError());
#endif
        return nullptr;
    }

    return new EGLImageWrapper(library, display, image);
}

EGLImageWrapper::~EGLImageWrapper()
{
    mLibrary.fDestroyImage(mDisplay, mImage);
}

bool
EGLImageWrapper::FenceSync(GLContext* gl)
{
    MOZ_ASSERT(!mSync);

    if (mLibrary.IsExtensionSupported(GLLibraryEGL::KHR_fence_sync)) {
        mSync = mLibrary.fCreateSync(mDisplay,
                                     LOCAL_EGL_SYNC_FENCE,
                                     nullptr);
        // We need to flush to make sure the sync object enters the command stream;
        // we can't use EGL_SYNC_FLUSH_COMMANDS_BIT at wait time, because the wait
        // happens on a different thread/context.
        gl->fFlush();
    }

    if (!mSync) {
        // we failed to create one, so just do a finish
        gl->fFinish();
    }

    return true;
}

bool
EGLImageWrapper::ClientWaitSync()
{
    if (!mSync) {
        // if we have no sync object, then we did a Finish() earlier
        return true;
    }

    // wait at most 1 second; this should really be never/rarely hit
    const uint64_t ns_per_ms = 1000 * 1000;
    EGLTime timeout = 1000 * ns_per_ms;

    EGLint result = mLibrary.fClientWaitSync(mDisplay,
                                             mSync,
                                             0,
                                             timeout);
    mLibrary.fDestroySync(mDisplay, mSync);
    mSync = nullptr;

    return result == LOCAL_EGL_CONDITION_SATISFIED;
}

} // namespace gl
} // namespace mozilla