Revert "Add support for Linux vulkan backend with VK_KHR_display"

This reverts commit a7bb6a9b15.

Reason for revert: Failing on Ozone builder, see bug.

Bug: angleproject:5289

Original change's description:
> Add support for Linux vulkan backend with VK_KHR_display
>
> Implement Linux simple display mode with vulkan backend
> through VK_KHR_display.
>
> Added value 'EGL_PLATFORM_VULKAN_DISPLAY_MODE_SIMPLE_ANGLE' for
> attribute EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE to identify
> the new simple display mode. Also reserved
> EGL_PLATFORM_VULKAN_DISPLAY_MODE_HEADLESS_ANGLE for headless mode.
>
> How to enable:
> Add
>
> ```
> use_x11=false
> angle_vulkan_display_mode="simple" # default value
> ```
>
> into args.gn, then compile with linux vulkan args.
>
> Bug: angleproject:5214
> Change-Id: I1247585b9de8b55df106aba99322281f1c183203
> Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2494320
> Commit-Queue: Jamie Madill <jmadill@chromium.org>
> Reviewed-by: Jamie Madill <jmadill@chromium.org>
> Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>

TBR=syoussefi@chromium.org,sunny.sun@arm.com,jmadill@chromium.org,xiaoxuan.liu@arm.com

Change-Id: I3e2a2a044c220ed8d25be0d82184e5fba7b9c06a
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: angleproject:5214
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2514637
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Commit-Queue: Jamie Madill <jmadill@chromium.org>
This commit is contained in:
Jamie Madill 2020-11-02 17:15:03 +00:00 коммит произвёл Commit Bot
Родитель 53aef10307
Коммит 807e6b3367
18 изменённых файлов: 8 добавлений и 543 удалений

Просмотреть файл

@ -114,15 +114,6 @@ config("internal_config") {
if (is_ggp) {
defines += [ "__ggp__" ]
}
if (angle_use_vulkan_display) {
defines += [ "ANGLE_USE_VULKAN_DISPLAY" ]
if (angle_vulkan_display_mode == "simple") {
defines += [ "ANGLE_VULKAN_DISPLAY_MODE_SIMPLE" ]
} else if (angle_vulkan_display_mode == "headless") {
defines += [ "ANGLE_VULKAN_DISPLAY_MODE_HEADLESS" ]
}
}
}
config("constructor_and_destructor_warnings") {

Просмотреть файл

@ -71,7 +71,6 @@ New Tokens
EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE 0x3204
EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE 0x3205
EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED 0x3451
EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE 0x348F
Accepted as values for the EGL_PLATFORM_ANGLE_TYPE_ANGLE attribute:

Просмотреть файл

@ -1,95 +0,0 @@
Name
ANGLE_vulkan_display
Name Strings
EGL_ANGLE_vulkan_display
Contributors
Xiaoxuan Liu, Arm Ltd.
Sunny Sun, Arm Ltd.
Contacts
Xiaoxuan Liu, Arm Ltd. (xiaoxuan 'dot' liu 'at' arm 'dot' com)
Status
Draft
Version
Version 1, 2020-10-29
Number
EGL Extension XXX
Extension Type
EGL client extension
Dependencies
Requires EGL_EXT_platform_angle_vulkan.
Overview
This extension allows ANGLE to use simple display mode through
VK_KHR_display without native platform support (e.g. X11, GBM).
The EGLSurfaces to be created from native types only contain the
basic width and height info.
New Types
The basic structure to represent a display window, used by vulkan
backend to create valid display with VK_KHR_display.
typedef struct displayWindow
{
khronos_uint16_t width;
khronos_uint16_t height;
} DisplayWindow;
New Procedures and Functions
None
New Tokens
Accepted as values for the EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE
attribute:
EGL_PLATFORM_VULKAN_DISPLAY_MODE_SIMPLE_ANGLE 0x34A4
EGL_PLATFORM_VULKAN_DISPLAY_MODE_HEADLESS_ANGLE 0x34A5
Additions to the EGL Specification
None
New Behavior
To obtain an simple EGLDisplay backed by a ANGLE vulkan with
VK_KHR_display, request a display that is backed by a Vulkan driver
through EGL_ANGLE_platform_angle_vulkan. And the value of attribute
EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE should be
EGL_PLATFORM_VULKAN_DISPLAY_MODE_SIMPLE_ANGLE or
EGL_PLATFORM_VULKAN_DISPLAY_MODE_HEADLESS_ANGLE.
If an EGLDisplay is backed by ANGLE vulkan VK_KHR_display and the
value of attribute EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE is
set but neither EGL_PLATFORM_VULKAN_DISPLAY_MODE_SIMPLE_ANGLE or
EGL_PLATFORM_VULKAN_DISPLAY_MODE_HEADLESS_ANGLE, then EGL_NO_DISPLAY
should be returned.
Issues
None
Revision History
Version 1, 2020-10-29 (Xiaoxuan Liu)
- Initial draft

Просмотреть файл

@ -9,9 +9,6 @@ declare_args() {
# Extract native libs in ANGLE apk. Useful for flamegraph generation.
angle_extract_native_libs = false
# Display mode for ANGLE vulkan display, could be 'simple' or 'headless', default is 'simple'.
angle_vulkan_display_mode = "simple"
}
if (angle_has_build) {
@ -30,7 +27,6 @@ if (angle_has_build) {
}
angle_use_x11 = use_x11 && !is_ggp
angle_use_vulkan_display = is_linux && !use_x11 && !is_ggp
} else {
declare_args() {
is_ubsan = false
@ -39,7 +35,6 @@ if (angle_has_build) {
build_with_chromium = false
dcheck_always_on = false
angle_use_x11 = (is_linux || is_chromeos) && !is_ggp
angle_use_vulkan_display = is_linux && !angle_use_x11 && !is_ggp
use_ozone = false
ozone_platform_gbm = false
use_fuzzing_engine = false
@ -106,11 +101,10 @@ declare_args() {
# Vulkan Validation Layers compatibility issues, see http://crrev/c/1405714.
# Otherwise, API level 24 would have been enough.
angle_enable_vulkan =
angle_has_build &&
((is_win && !angle_is_winuwp) ||
(is_linux && (angle_use_x11 || angle_use_vulkan_display) &&
!is_chromeos) || (is_android && ndk_api_level_at_least_26) ||
is_fuchsia || is_ggp || is_mac)
angle_has_build && ((is_win && !angle_is_winuwp) ||
(is_linux && angle_use_x11 && !is_chromeos) ||
(is_android && ndk_api_level_at_least_26) ||
is_fuchsia || is_ggp || is_mac)
angle_enable_null = true
angle_enable_essl = true
angle_enable_glsl = true

Просмотреть файл

@ -94,8 +94,6 @@
#ifndef EGL_ANGLE_platform_angle_vulkan
#define EGL_ANGLE_platform_angle_vulkan 1
#define EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE 0x3450
#define EGL_PLATFORM_VULKAN_DISPLAY_MODE_SIMPLE_ANGLE 0x34A4
#define EGL_PLATFORM_VULKAN_DISPLAY_MODE_HEADLESS_ANGLE 0x34A5
#endif /* EGL_ANGLE_platform_angle_vulkan */
#ifndef EGL_ANGLE_platform_angle_metal

Просмотреть файл

@ -27,12 +27,6 @@ enum class ICD
SwiftShader,
};
struct SimpleDisplayWindow
{
uint16_t width;
uint16_t height;
};
class ScopedVkLoaderEnvironment : angle::NonCopyable
{
public:

Просмотреть файл

@ -226,17 +226,11 @@ EGLAttrib GetDeviceTypeFromEnvironment()
EGLAttrib GetPlatformTypeFromEnvironment()
{
#if defined(ANGLE_USE_OZONE)
return 0;
#elif defined(ANGLE_USE_X11)
#if defined(ANGLE_USE_X11) && !defined(ANGLE_USE_OZONE)
return EGL_PLATFORM_X11_EXT;
#elif defined(ANGLE_USE_VULKAN_DISPLAY) && defined(ANGLE_VULKAN_DISPLAY_MODE_SIMPLE)
return EGL_PLATFORM_VULKAN_DISPLAY_MODE_SIMPLE_ANGLE;
#elif defined(ANGLE_USE_VULKAN_DISPLAY) && defined(ANGLE_VULKAN_DISPLAY_MODE_HEADLESS)
return EGL_PLATFORM_VULKAN_DISPLAY_MODE_HEADLESS_ANGLE;
#else
return 0;
#endif // defined(ANGLE_USE_OZONE)
#endif
}
rx::DisplayImpl *CreateDisplayFromAttribs(EGLAttrib displayType,
@ -360,24 +354,6 @@ rx::DisplayImpl *CreateDisplayFromAttribs(EGLAttrib displayType,
impl = rx::CreateVulkanXcbDisplay(state);
break;
}
# elif defined(ANGLE_USE_VULKAN_DISPLAY)
if (platformType == EGL_PLATFORM_VULKAN_DISPLAY_MODE_SIMPLE_ANGLE &&
rx::IsVulkanSimpleDisplayAvailable())
{
impl = rx::CreateVulkanSimpleDisplay(state);
}
else if (platformType == EGL_PLATFORM_VULKAN_DISPLAY_MODE_HEADLESS_ANGLE)
{
// TODO: anglebug.com/5260
// Add support for headless rendering
UNIMPLEMENTED();
}
else
{
// Not supported creation type on vulkan display, fail display creation.
impl = nullptr;
}
break;
# endif
# elif defined(ANGLE_PLATFORM_ANDROID)
if (rx::IsVulkanAndroidDisplayAvailable())

Просмотреть файл

@ -101,15 +101,6 @@ _vulkan_backend_sources = [
"vk_wrapper.h",
]
if (is_linux) {
_vulkan_backend_sources += [
"display/DisplayVkSimple.cpp",
"display/DisplayVkSimple.h",
"display/WindowSurfaceVkSimple.cpp",
"display/WindowSurfaceVkSimple.h",
]
}
if (is_android) {
_vulkan_backend_sources += [
"android/DisplayVkAndroid.cpp",

Просмотреть файл

@ -22,9 +22,6 @@ DisplayImpl *CreateVulkanWin32Display(const egl::DisplayState &state);
#if defined(ANGLE_PLATFORM_LINUX)
bool IsVulkanXcbDisplayAvailable();
DisplayImpl *CreateVulkanXcbDisplay(const egl::DisplayState &state);
bool IsVulkanSimpleDisplayAvailable();
DisplayImpl *CreateVulkanSimpleDisplay(const egl::DisplayState &state);
#endif // defined(ANGLE_PLATFORM_LINUX)
#if defined(ANGLE_PLATFORM_ANDROID)

Просмотреть файл

@ -1,64 +0,0 @@
//
// Copyright 2020 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// DisplayVkSimple.cpp:
// Implements the class methods for DisplayVkSimple.
//
#include "DisplayVkSimple.h"
#include "WindowSurfaceVkSimple.h"
#include "libANGLE/Display.h"
#include "libANGLE/renderer/vulkan/RendererVk.h"
#include "libANGLE/renderer/vulkan/vk_caps_utils.h"
namespace rx
{
DisplayVkSimple::DisplayVkSimple(const egl::DisplayState &state) : DisplayVk(state) {}
void DisplayVkSimple::terminate()
{
DisplayVk::terminate();
}
bool DisplayVkSimple::isValidNativeWindow(EGLNativeWindowType window) const
{
return true;
}
SurfaceImpl *DisplayVkSimple::createWindowSurfaceVk(const egl::SurfaceState &state,
EGLNativeWindowType window)
{
return new WindowSurfaceVkSimple(state, window);
}
egl::ConfigSet DisplayVkSimple::generateConfigs()
{
constexpr GLenum kColorFormats[] = {GL_RGBA8, GL_BGRA8_EXT, GL_RGB565, GL_RGB8};
return egl_vk::GenerateConfigs(kColorFormats, egl_vk::kConfigDepthStencilFormats, this);
}
// TODO: anglebug.com/5214
// Detemine if check is needed.
void DisplayVkSimple::checkConfigSupport(egl::Config *config) {}
const char *DisplayVkSimple::getWSIExtension() const
{
return VK_KHR_DISPLAY_EXTENSION_NAME;
}
bool IsVulkanSimpleDisplayAvailable()
{
return true;
}
DisplayImpl *CreateVulkanSimpleDisplay(const egl::DisplayState &state)
{
return new DisplayVkSimple(state);
}
} // namespace rx

Просмотреть файл

@ -1,41 +0,0 @@
//
// Copyright 2020 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// DisplayVkSimple.h:
// Defines the class interface for DisplayVkSimple, implementing
// DisplayVk for Linux via VK_KHR_display.
//
#ifndef LIBANGLE_RENDERER_VULKAN_DISPLAY_DISPLAYVKSIMPLE_H_
#define LIBANGLE_RENDERER_VULKAN_DISPLAY_DISPLAYVKSIMPLE_H_
#include "libANGLE/renderer/vulkan/DisplayVk.h"
namespace rx
{
class DisplayVkSimple : public DisplayVk
{
public:
DisplayVkSimple(const egl::DisplayState &state);
void terminate() override;
bool isValidNativeWindow(EGLNativeWindowType window) const override;
SurfaceImpl *createWindowSurfaceVk(const egl::SurfaceState &state,
EGLNativeWindowType window) override;
egl::ConfigSet generateConfigs() override;
void checkConfigSupport(egl::Config *config) override;
const char *getWSIExtension() const override;
private:
std::vector<VkSurfaceFormatKHR> mSurfaceFormats;
};
} // namespace rx
#endif // LIBANGLE_RENDERER_VULKAN_DISPLAY_DISPLAYVKSIMPLE_H_

Просмотреть файл

@ -1,82 +0,0 @@
//
// Copyright 2020 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// WindowSurfaceVkSimple.cpp:
// Implements the class methods for WindowSurfaceVkSimple.
//
#include "WindowSurfaceVkSimple.h"
#include "libANGLE/renderer/vulkan/RendererVk.h"
namespace rx
{
WindowSurfaceVkSimple::WindowSurfaceVkSimple(const egl::SurfaceState &surfaceState,
EGLNativeWindowType window)
: WindowSurfaceVk(surfaceState, window)
{}
WindowSurfaceVkSimple::~WindowSurfaceVkSimple() {}
angle::Result WindowSurfaceVkSimple::createSurfaceVk(vk::Context *context, gl::Extents *extentsOut)
{
RendererVk *renderer = context->getRenderer();
ASSERT(renderer != nullptr);
VkInstance instance = renderer->getInstance();
VkPhysicalDevice physicalDevice = renderer->getPhysicalDevice();
// Query if there is a valid display
uint32_t count = 1;
ANGLE_VK_TRY(context, vkGetPhysicalDeviceDisplayPropertiesKHR(physicalDevice, &count, nullptr));
// Get display properties
VkDisplayPropertiesKHR prop = {};
count = 1;
ANGLE_VK_TRY(context, vkGetPhysicalDeviceDisplayPropertiesKHR(physicalDevice, &count, &prop));
// we should have a valid display now
ASSERT(prop.display != VK_NULL_HANDLE);
ANGLE_VK_TRY(context,
vkGetDisplayModePropertiesKHR(physicalDevice, prop.display, &count, nullptr));
ASSERT(count != 0);
std::vector<VkDisplayModePropertiesKHR> modeProperties(count);
ANGLE_VK_TRY(context, vkGetDisplayModePropertiesKHR(physicalDevice, prop.display, &count,
modeProperties.data()));
angle::vk::SimpleDisplayWindow *displayWindow =
reinterpret_cast<angle::vk::SimpleDisplayWindow *>(mNativeWindowType);
VkDisplaySurfaceCreateInfoKHR info = {};
info.sType = VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR;
info.flags = 0;
info.displayMode = modeProperties[0].displayMode;
info.planeIndex = 0;
info.planeStackIndex = 0;
info.transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
info.globalAlpha = 1.0f;
info.alphaMode = VK_DISPLAY_PLANE_ALPHA_GLOBAL_BIT_KHR;
info.imageExtent.width = displayWindow->width;
info.imageExtent.height = displayWindow->height;
ANGLE_VK_TRY(context, vkCreateDisplayPlaneSurfaceKHR(instance, &info, nullptr, &mSurface));
return getCurrentWindowSize(context, extentsOut);
}
angle::Result WindowSurfaceVkSimple::getCurrentWindowSize(vk::Context *context,
gl::Extents *extentsOut)
{
RendererVk *renderer = context->getRenderer();
const VkPhysicalDevice &physicalDevice = renderer->getPhysicalDevice();
ANGLE_VK_TRY(context, vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, mSurface,
&mSurfaceCaps));
*extentsOut =
gl::Extents(mSurfaceCaps.currentExtent.width, mSurfaceCaps.currentExtent.height, 1);
return angle::Result::Continue;
}
} // namespace rx

Просмотреть файл

@ -1,31 +0,0 @@
//
// Copyright 2020 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// WindowSurfaceVkSimple.h:
// Defines the class interface for WindowSurfaceVkSimple, implementing WindowSurfaceVk.
//
#ifndef LIBANGLE_RENDERER_VULKAN_DISPLAY_WINDOWSURFACEVKSIMPLE_H_
#define LIBANGLE_RENDERER_VULKAN_DISPLAY_WINDOWSURFACEVKSIMPLE_H_
#include "libANGLE/renderer/vulkan/SurfaceVk.h"
namespace rx
{
class WindowSurfaceVkSimple : public WindowSurfaceVk
{
public:
WindowSurfaceVkSimple(const egl::SurfaceState &surfaceState, EGLNativeWindowType window);
~WindowSurfaceVkSimple() final;
private:
angle::Result createSurfaceVk(vk::Context *context, gl::Extents *extentsOut) override;
angle::Result getCurrentWindowSize(vk::Context *context, gl::Extents *extentsOut) override;
};
} // namespace rx
#endif // LIBANGLE_RENDERER_VULKAN_DISPLAY_WINDOWSURFACEVKSIMPLE_H_

Просмотреть файл

@ -233,7 +233,7 @@ bool IsARM64()
bool IsOzone()
{
#if defined(USE_OZONE) && (defined(USE_X11) || defined(ANGLE_USE_VULKAN_DISPLAY))
#if defined(USE_OZONE) && defined(USE_X11)
// We do not have a proper support for Ozone/Linux yet. Still, we need to figure out how to
// properly initialize tests and differentiate between X11 and Wayland. Probably, passing a
// command line argument could be sufficient. At the moment, run tests only for X11 backend

Просмотреть файл

@ -40,14 +40,6 @@ if (is_win) {
}
}
if (is_linux) {
_util_sources += [
"display/DisplayPixmap.cpp",
"display/DisplayWindow.cpp",
"display/DisplayWindow.h",
]
}
if (angle_use_x11) {
_util_sources += [
"x11/X11Pixmap.cpp",
@ -64,7 +56,7 @@ if (is_fuchsia) {
"fuchsia/ScenicWindow.cpp",
"fuchsia/ScenicWindow.h",
]
} else if (use_ozone && !angle_use_x11 && !angle_use_vulkan_display) {
} else if (use_ozone && !angle_use_x11) {
# Use X11 impl by default otherwise switch to fake Ozone impl that does not
# seem to do anything real.
# TODO(msisov): probably, we need to have a proper support for different

Просмотреть файл

@ -1,16 +0,0 @@
//
// Copyright 2020 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// DisplayPixmap.cpp: Implementation of OSPixmap for Linux Display
#include "util/OSPixmap.h"
#if defined(ANGLE_USE_VULKAN_DISPLAY)
OSPixmap *CreateOSPixmap()
{
return nullptr;
}
#endif

Просмотреть файл

@ -1,87 +0,0 @@
//
// Copyright 2020 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// DisplayWindow.cpp: Implementation of OSWindow for Linux Display
#include "util/display/DisplayWindow.h"
#include "common/debug.h"
#include "util/Timer.h"
#include "util/test_utils.h"
DisplayWindow::DisplayWindow()
{
mWindow.width = 0;
mWindow.height = 0;
}
DisplayWindow::~DisplayWindow() {}
bool DisplayWindow::initializeImpl(const std::string &name, int width, int height)
{
return resize(width, height);
}
void DisplayWindow::disableErrorMessageDialog() {}
void DisplayWindow::destroy() {}
void DisplayWindow::resetNativeWindow() {}
EGLNativeWindowType DisplayWindow::getNativeWindow() const
{
return (EGLNativeWindowType)&mWindow;
}
EGLNativeDisplayType DisplayWindow::getNativeDisplay() const
{
return NULL;
}
void DisplayWindow::messageLoop() {}
void DisplayWindow::setMousePosition(int x, int y)
{
UNIMPLEMENTED();
}
bool DisplayWindow::setOrientation(int width, int height)
{
UNIMPLEMENTED();
return true;
}
bool DisplayWindow::setPosition(int x, int y)
{
UNIMPLEMENTED();
return true;
}
bool DisplayWindow::resize(int width, int height)
{
mWindow.width = width;
mWindow.height = height;
return true;
}
void DisplayWindow::setVisible(bool isVisible) {}
void DisplayWindow::signalTestEvent()
{
Event event;
event.Type = Event::EVENT_TEST;
event.Move.X = 0;
event.Move.Y = 0;
pushEvent(event);
}
// static
#if defined(ANGLE_USE_VULKAN_DISPLAY)
OSWindow *OSWindow::New()
{
return new DisplayWindow();
}
#endif

Просмотреть файл

@ -1,51 +0,0 @@
//
// Copyright 2020 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// DisplayWindow.h: Definition of the implementation of OSWindow for Linux Display
#ifndef UTIL_DISPLAY_WINDOW_H_
#define UTIL_DISPLAY_WINDOW_H_
#include <string>
#include "util/OSWindow.h"
#include "util/util_export.h"
struct SimpleDisplayWindow
{
uint16_t width;
uint16_t height;
};
class ANGLE_UTIL_EXPORT DisplayWindow : public OSWindow
{
public:
DisplayWindow();
DisplayWindow(int visualId);
~DisplayWindow() override;
void disableErrorMessageDialog() override;
void destroy() override;
void resetNativeWindow() override;
EGLNativeWindowType getNativeWindow() const override;
EGLNativeDisplayType getNativeDisplay() const override;
void messageLoop() override;
void setMousePosition(int x, int y) override;
bool setOrientation(int width, int height) override;
bool setPosition(int x, int y) override;
bool resize(int width, int height) override;
void setVisible(bool isVisible) override;
void signalTestEvent() override;
private:
bool initializeImpl(const std::string &name, int width, int height) override;
SimpleDisplayWindow mWindow;
};
#endif // UTIL_DISPLAY_WINDOW_H_