WebXRWebGLLayer.cpp   [plain text]


/*
 * Copyright (C) 2020 Igalia S.L. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "WebXRWebGLLayer.h"

#if ENABLE(WEBXR)

#include "IntSize.h"
#include "WebGLFramebuffer.h"
#include "WebGLRenderingContext.h"
#if ENABLE(WEBGL2)
#include "WebGL2RenderingContext.h"
#endif
#include "WebGLRenderingContextBase.h"
#include "WebXRSession.h"
#include "WebXRViewport.h"
#include "XRWebGLLayerInit.h"
#include <wtf/IsoMallocInlines.h>
#include <wtf/Scope.h>

namespace WebCore {

WTF_MAKE_ISO_ALLOCATED_IMPL(WebXRWebGLLayer);

// https://immersive-web.github.io/webxr/#dom-xrwebgllayer-xrwebgllayer
ExceptionOr<Ref<WebXRWebGLLayer>> WebXRWebGLLayer::create(Ref<WebXRSession>&& session, WebXRRenderingContext&& context, const XRWebGLLayerInit& init)
{
    // 1. Let layer be a new XRWebGLLayer
    // 2. If session’s ended value is true, throw an InvalidStateError and abort these steps.
    if (session->ended())
        return Exception { InvalidStateError };

    // 3. If context is lost, throw an InvalidStateError and abort these steps.
    // 4. If session is an immersive session and context’s XR compatible boolean is false, throw
    //    an InvalidStateError and abort these steps.
    return WTF::switchOn(context,
        [&](const RefPtr<WebGLRenderingContextBase>& baseContext) -> ExceptionOr<Ref<WebXRWebGLLayer>>
        {
            if (baseContext->isContextLost())
                return Exception { InvalidStateError };

            auto mode = session->mode();
            if ((mode == XRSessionMode::ImmersiveAr || mode == XRSessionMode::ImmersiveVr) && !baseContext->isXRCompatible())
                return Exception { InvalidStateError };


            // 5. Initialize layer’s context to context. (see constructor)
            // 6. Initialize layer’s session to session. (see constructor)
            // 7. Initialize layer’s ignoreDepthValues as follows. (see constructor)
            // 8. Initialize layer’s composition disabled boolean as follows. (see constructor)
            // 9. (see constructor except for the resources initialization step which is handled in the if block below)
            auto layer = adoptRef(*new WebXRWebGLLayer(WTFMove(session), WTFMove(context), init));

            if (layer->m_isCompositionDisabled) {
                // 9.4. Allocate and initialize resources compatible with session’s XR device, including GPU accessible memory buffers,
                //      as required to support the compositing of layer.
                // 9.5. If layer’s resources were unable to be created for any reason, throw an OperationError and abort these steps.
                // TODO: Initialize layer's resources or issue an OperationError.
            }

            // 10. Return layer.
            return layer;
        },
        [](WTF::Monostate) {
            ASSERT_NOT_REACHED();
            return Exception { InvalidStateError };
        }
    );
}

WebXRWebGLLayer::WebXRWebGLLayer(Ref<WebXRSession>&& session, WebXRRenderingContext&& context, const XRWebGLLayerInit& init)
    : WebXRLayer(session->scriptExecutionContext())
    , m_session(WTFMove(session))
    , m_context(WTFMove(context))
{
    // 7. Initialize layer’s ignoreDepthValues as follows:
    //   7.1 If layerInit’s ignoreDepthValues value is false and the XR Compositor will make use of depth values,
    //       Initialize layer’s ignoreDepthValues to false.
    //   7.2. Else Initialize layer’s ignoreDepthValues to true
    // TODO: ask XR compositor for depth value usages
    m_ignoreDepthValues = init.ignoreDepthValues;

    // 8. Initialize layer's composition disabled boolean as follows:
    //  If session is an inline session -> Initialize layer's composition disabled to true
    //  Otherwise -> Initialize layer's composition disabled boolean to false
    m_isCompositionDisabled = m_session->mode() == XRSessionMode::Inline;

    // 9. If layer’s composition disabled boolean is false:
    if (m_isCompositionDisabled) {
        //  1. Initialize layer’s antialias to layerInit’s antialias value.
        m_antialias = init.antialias;

        //  2. Let framebufferSize be the recommended WebGL framebuffer resolution multiplied by layerInit's framebufferScaleFactor.
        IntSize recommendedSize = m_session->recommendedWebGLFramebufferResolution();
        m_framebuffer.width = recommendedSize.width() * init.framebufferScaleFactor;
        m_framebuffer.height = recommendedSize.height() * init.framebufferScaleFactor;

        //  3. Initialize layer’s framebuffer to a new opaque framebuffer with the dimensions framebufferSize
        //       created with context, session initialized to session, and layerInit’s depth, stencil, and alpha values.
        // For steps 4 & 5 see the create() method as resources initialization is handled outside the constructor as it might trigger
        // an OperationError.
        // FIXME: create a proper opaque framebuffer.
        m_framebuffer.object = WTF::switchOn(m_context,
            [](const RefPtr<WebGLRenderingContextBase>& baseContext)
            {
                return WebGLFramebuffer::create(*baseContext);
            }
        );
    } else {
        // 1. Initialize layer’s antialias to layer’s context's actual context parameters antialias value.
        m_antialias = WTF::switchOn(m_context,
            [](const RefPtr<WebGLRenderingContextBase>& context)
            {
                if (auto attributes = context->getContextAttributes())
                    return attributes.value().antialias;
                return false;
            }
        );
        // 2. Initialize layer’s framebuffer to null.
        m_framebuffer.object = nullptr;
    }
}

WebXRWebGLLayer::~WebXRWebGLLayer() = default;

bool WebXRWebGLLayer::antialias() const
{
    return m_antialias;
}

bool WebXRWebGLLayer::ignoreDepthValues() const
{
    return m_ignoreDepthValues;
}

WebGLFramebuffer* WebXRWebGLLayer::framebuffer() const
{
    return m_framebuffer.object.get();
}

unsigned WebXRWebGLLayer::framebufferWidth() const
{
    if (m_framebuffer.object)
        return m_framebuffer.width;
    return WTF::switchOn(m_context,
        [&](const RefPtr<WebGLRenderingContextBase>& baseContext) {
            return baseContext->drawingBufferWidth();
        });
}

unsigned WebXRWebGLLayer::framebufferHeight() const
{
    if (m_framebuffer.object)
        return m_framebuffer.height;
    return WTF::switchOn(m_context,
        [&](const RefPtr<WebGLRenderingContextBase>& baseContext) {
            return baseContext->drawingBufferHeight();
        });
}

RefPtr<WebXRViewport> WebXRWebGLLayer::getViewport(const WebXRView&)
{
    return { };
}

double WebXRWebGLLayer::getNativeFramebufferScaleFactor(const WebXRSession& session)
{
    if (session.ended())
        return 0.0;

    IntSize nativeSize = session.nativeWebGLFramebufferResolution();
    IntSize recommendedSize = session.recommendedWebGLFramebufferResolution();

    RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!recommendedSize.isZero());
    return (nativeSize / recommendedSize).width();
}

} // namespace WebCore

#endif // ENABLE(WEBXR)