blob: da29aeb0fdecef014cd609daeea471a37a2a2f67 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/modules/xr/xr_webgl_binding.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_union_webgl2renderingcontext_webglrenderingcontext.h"
#include "third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h"
#include "third_party/blink/renderer/modules/webgl/webgl_texture.h"
#include "third_party/blink/renderer/modules/webgl/webgl_unowned_texture.h"
#include "third_party/blink/renderer/modules/xr/xr_camera.h"
#include "third_party/blink/renderer/modules/xr/xr_cube_map.h"
#include "third_party/blink/renderer/modules/xr/xr_frame.h"
#include "third_party/blink/renderer/modules/xr/xr_light_probe.h"
#include "third_party/blink/renderer/modules/xr/xr_render_state.h"
#include "third_party/blink/renderer/modules/xr/xr_session.h"
#include "third_party/blink/renderer/modules/xr/xr_utils.h"
#include "third_party/blink/renderer/modules/xr/xr_viewer_pose.h"
#include "third_party/blink/renderer/modules/xr/xr_webgl_layer.h"
#include "third_party/blink/renderer/modules/xr/xr_webgl_rendering_context.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.h"
namespace blink {
XRWebGLBinding* XRWebGLBinding::Create(XRSession* session,
#if defined(USE_BLINK_V8_BINDING_NEW_IDL_UNION)
const V8XRWebGLRenderingContext* context,
#else // defined(USE_BLINK_V8_BINDING_NEW_IDL_UNION)
const XRWebGLRenderingContext& context,
#endif // defined(USE_BLINK_V8_BINDING_NEW_IDL_UNION)
ExceptionState& exception_state) {
if (session->ended()) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
"Cannot create an XRWebGLBinding for an "
"XRSession which has already ended.");
return nullptr;
}
if (!session->immersive()) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
"Cannot create an XRWebGLBinding for an "
"inline XRSession.");
return nullptr;
}
WebGLRenderingContextBase* webgl_context =
webglRenderingContextBaseFromUnion(context);
if (webgl_context->isContextLost()) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
"Cannot create an XRWebGLBinding with a "
"lost WebGL context.");
return nullptr;
}
if (!webgl_context->IsXRCompatible()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidStateError,
"WebGL context must be marked as XR compatible in order to "
"use with an immersive XRSession");
return nullptr;
}
#if defined(USE_BLINK_V8_BINDING_NEW_IDL_UNION)
return MakeGarbageCollected<XRWebGLBinding>(
session, webgl_context, context->IsWebGL2RenderingContext());
#else // defined(USE_BLINK_V8_BINDING_NEW_IDL_UNION)
return MakeGarbageCollected<XRWebGLBinding>(
session, webgl_context, context.IsWebGL2RenderingContext());
#endif // defined(USE_BLINK_V8_BINDING_NEW_IDL_UNION)
}
XRWebGLBinding::XRWebGLBinding(XRSession* session,
WebGLRenderingContextBase* webgl_context,
bool webgl2)
: session_(session), webgl_context_(webgl_context), webgl2_(webgl2) {}
WebGLTexture* XRWebGLBinding::getReflectionCubeMap(
XRLightProbe* light_probe,
ExceptionState& exception_state) {
GLenum internal_format, format, type;
if (webgl_context_->isContextLost()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidStateError,
"Cannot get reflection cube map with a lost context.");
return nullptr;
}
if (session_->ended()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidStateError,
"Cannot get a reflection cube map for a session which has ended.");
return nullptr;
}
if (session_ != light_probe->session()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidStateError,
"LightProbe comes from a different session than this binding");
return nullptr;
}
// Determine the internal_format, format, and type that will be passed to
// glTexImage2D for each possible light probe reflection format. The formats
// will differ depending on whether we're using WebGL 2 or WebGL 1 with
// extensions.
// Note that at this point, since we know we have a valid lightProbe, we also
// know that we support whatever reflectionFormat it was created with, as it
// would not have been created otherwise.
switch (light_probe->ReflectionFormat()) {
case XRLightProbe::kReflectionFormatRGBA16F:
if (!webgl2_ && !webgl_context_->ExtensionsUtil()->IsExtensionEnabled(
"GL_OES_texture_half_float")) {
exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidStateError,
"WebGL contexts must have the OES_texture_half_float extension "
"enabled "
"prior to calling getReflectionCubeMap with a format of "
"\"rgba16f\". "
"This restriction does not apply to WebGL 2.0 contexts.");
return nullptr;
}
internal_format = webgl2_ ? GL_RGBA16F : GL_RGBA;
format = GL_RGBA;
// Surprisingly GL_HALF_FLOAT and GL_HALF_FLOAT_OES have different values.
type = webgl2_ ? GL_HALF_FLOAT : GL_HALF_FLOAT_OES;
break;
case XRLightProbe::kReflectionFormatSRGBA8:
bool use_srgb =
webgl2_ ||
webgl_context_->ExtensionsUtil()->IsExtensionEnabled("GL_EXT_sRGB");
if (use_srgb) {
internal_format = webgl2_ ? GL_SRGB8_ALPHA8 : GL_SRGB_ALPHA_EXT;
} else {
internal_format = GL_RGBA;
}
format = webgl2_ ? GL_RGBA : internal_format;
type = GL_UNSIGNED_BYTE;
break;
}
XRCubeMap* cube_map = light_probe->getReflectionCubeMap();
if (!cube_map) {
return nullptr;
}
WebGLTexture* texture = MakeGarbageCollected<WebGLTexture>(webgl_context_);
cube_map->updateWebGLEnvironmentCube(webgl_context_, texture, internal_format,
format, type);
return texture;
}
WebGLTexture* XRWebGLBinding::getCameraImage(XRCamera* camera,
ExceptionState& exception_state) {
XRFrame* frame = camera->Frame();
DCHECK(frame);
XRSession* session = frame->session();
DCHECK(session);
if (!session->IsFeatureEnabled(
device::mojom::XRSessionFeature::CAMERA_ACCESS)) {
DVLOG(2) << __func__ << ": raw camera access is not enabled on a session";
exception_state.ThrowDOMException(
DOMExceptionCode::kNotSupportedError,
XRSession::kRawCameraAccessFeatureNotSupported);
return nullptr;
}
if (!frame->IsActive()) {
DVLOG(2) << __func__ << ": frame is not active";
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
XRFrame::kInactiveFrame);
return nullptr;
}
if (!frame->IsAnimationFrame()) {
DVLOG(2) << __func__ << ": frame is not animating";
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
XRFrame::kNonAnimationFrame);
return nullptr;
}
if (session_ != session) {
exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidStateError,
"Camera comes from a different session than this binding");
return nullptr;
}
XRWebGLLayer* base_layer = session->renderState()->baseLayer();
DCHECK(base_layer);
base::Optional<gpu::MailboxHolder> camera_image_mailbox_holder =
base_layer->CameraImageMailboxHolder();
if (!camera_image_mailbox_holder) {
DVLOG(3) << __func__ << ": camera image mailbox holder is not set";
return nullptr;
}
GLuint texture_id = base_layer->CameraImageTextureId();
// This resource is owned by the XRWebGLLayer, and is freed in OnFrameEnd();
WebGLUnownedTexture* texture = MakeGarbageCollected<WebGLUnownedTexture>(
webgl_context_, texture_id, GL_TEXTURE_2D);
return texture;
}
XRWebGLDepthInformation* XRWebGLBinding::getDepthInformation(
XRView* view,
ExceptionState& exception_state) {
DVLOG(1) << __func__;
XRFrame* frame = view->frame();
if (session_ != frame->session()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidStateError,
"View comes from a different session than this binding");
return nullptr;
}
if (!session_->IsFeatureEnabled(device::mojom::XRSessionFeature::DEPTH)) {
DVLOG(2) << __func__ << ": depth sensing is not enabled on a session";
exception_state.ThrowDOMException(
DOMExceptionCode::kNotSupportedError,
XRSession::kDepthSensingFeatureNotSupported);
return nullptr;
}
if (!frame->IsActive()) {
DVLOG(2) << __func__ << ": frame is not active";
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
XRFrame::kInactiveFrame);
return nullptr;
}
if (!frame->IsAnimationFrame()) {
DVLOG(2) << __func__ << ": frame is not animating";
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
XRFrame::kNonAnimationFrame);
return nullptr;
}
return view->session()->GetWebGLDepthInformation(frame, exception_state);
}
void XRWebGLBinding::Trace(Visitor* visitor) const {
visitor->Trace(session_);
visitor->Trace(webgl_context_);
ScriptWrappable::Trace(visitor);
}
} // namespace blink