mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-25 15:15:40 +00:00
Common: Add DRM display helper class and GBM GL context
This commit is contained in:
parent
b267020d07
commit
b09da307b5
|
@ -36,6 +36,9 @@ endif()
|
||||||
if(LINUX OR ANDROID)
|
if(LINUX OR ANDROID)
|
||||||
option(USE_EGL "Support EGL OpenGL context creation" ON)
|
option(USE_EGL "Support EGL OpenGL context creation" ON)
|
||||||
endif()
|
endif()
|
||||||
|
if(LINUX AND NOT ANDROID)
|
||||||
|
option(USE_DRMKMS "Support DRM/KMS display and contexts" OFF)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Force EGL when using Wayland
|
# Force EGL when using Wayland
|
||||||
if(USE_WAYLAND)
|
if(USE_WAYLAND)
|
||||||
|
@ -112,6 +115,11 @@ if(USE_WAYLAND)
|
||||||
find_package(Wayland REQUIRED Egl)
|
find_package(Wayland REQUIRED Egl)
|
||||||
message(STATUS "Wayland support enabled")
|
message(STATUS "Wayland support enabled")
|
||||||
endif()
|
endif()
|
||||||
|
if(USE_DRMKMS)
|
||||||
|
find_package(GBM REQUIRED)
|
||||||
|
find_package(Libdrm REQUIRED)
|
||||||
|
message(STATUS "DRM/KMS support enabled")
|
||||||
|
endif()
|
||||||
|
|
||||||
# Set _DEBUG macro for Debug builds.
|
# Set _DEBUG macro for Debug builds.
|
||||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_DEBUG")
|
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_DEBUG")
|
||||||
|
|
70
CMakeModules/FindGBM.cmake
Normal file
70
CMakeModules/FindGBM.cmake
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
# https://fossies.org/linux/misc/xbmc-18.9-Leia.tar.gz/xbmc-18.9-Leia/cmake/modules/FindGBM.cmake?m=t
|
||||||
|
|
||||||
|
# FindGBM
|
||||||
|
# ----------
|
||||||
|
# Finds the GBM library
|
||||||
|
#
|
||||||
|
# This will define the following variables::
|
||||||
|
#
|
||||||
|
# GBM_FOUND - system has GBM
|
||||||
|
# GBM_INCLUDE_DIRS - the GBM include directory
|
||||||
|
# GBM_LIBRARIES - the GBM libraries
|
||||||
|
# GBM_DEFINITIONS - the GBM definitions
|
||||||
|
#
|
||||||
|
# and the following imported targets::
|
||||||
|
#
|
||||||
|
# GBM::GBM - The GBM library
|
||||||
|
|
||||||
|
if(PKG_CONFIG_FOUND)
|
||||||
|
pkg_check_modules(PC_GBM gbm QUIET)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_path(GBM_INCLUDE_DIR NAMES gbm.h
|
||||||
|
PATHS ${PC_GBM_INCLUDEDIR})
|
||||||
|
find_library(GBM_LIBRARY NAMES gbm
|
||||||
|
PATHS ${PC_GBM_LIBDIR})
|
||||||
|
|
||||||
|
set(GBM_VERSION ${PC_GBM_VERSION})
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(GBM
|
||||||
|
REQUIRED_VARS GBM_LIBRARY GBM_INCLUDE_DIR
|
||||||
|
VERSION_VAR GBM_VERSION)
|
||||||
|
|
||||||
|
include(CheckCSourceCompiles)
|
||||||
|
set(CMAKE_REQUIRED_LIBRARIES ${GBM_LIBRARY})
|
||||||
|
check_c_source_compiles("#include <gbm.h>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
gbm_bo_map(NULL, 0, 0, 0, 0, GBM_BO_TRANSFER_WRITE, NULL, NULL);
|
||||||
|
}
|
||||||
|
" GBM_HAS_BO_MAP)
|
||||||
|
|
||||||
|
check_c_source_compiles("#include <gbm.h>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
gbm_surface_create_with_modifiers(NULL, 0, 0, 0, NULL, 0);
|
||||||
|
}
|
||||||
|
" GBM_HAS_MODIFIERS)
|
||||||
|
|
||||||
|
if(GBM_FOUND)
|
||||||
|
set(GBM_LIBRARIES ${GBM_LIBRARY})
|
||||||
|
set(GBM_INCLUDE_DIRS ${GBM_INCLUDE_DIR})
|
||||||
|
set(GBM_DEFINITIONS -DHAVE_GBM=1)
|
||||||
|
if(GBM_HAS_BO_MAP)
|
||||||
|
list(APPEND GBM_DEFINITIONS -DHAS_GBM_BO_MAP=1)
|
||||||
|
endif()
|
||||||
|
if(GBM_HAS_MODIFIERS)
|
||||||
|
list(APPEND GBM_DEFINITIONS -DHAS_GBM_MODIFIERS=1)
|
||||||
|
endif()
|
||||||
|
if(NOT TARGET GBM::GBM)
|
||||||
|
add_library(GBM::GBM UNKNOWN IMPORTED)
|
||||||
|
set_target_properties(GBM::GBM PROPERTIES
|
||||||
|
IMPORTED_LOCATION "${GBM_LIBRARY}"
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES "${GBM_INCLUDE_DIR}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
mark_as_advanced(GBM_INCLUDE_DIR GBM_LIBRARY)
|
33
CMakeModules/FindLIBEVDEV.cmake
Normal file
33
CMakeModules/FindLIBEVDEV.cmake
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
# - Try to find libevdev
|
||||||
|
# Once done this will define
|
||||||
|
# LIBEVDEV_FOUND - System has libevdev
|
||||||
|
# LIBEVDEV_INCLUDE_DIRS - The libevdev include directories
|
||||||
|
# LIBEVDEV_LIBRARIES - The libraries needed to use libevdev
|
||||||
|
|
||||||
|
find_package(PkgConfig)
|
||||||
|
pkg_check_modules(PC_LIBEVDEV QUIET libevdev)
|
||||||
|
|
||||||
|
FIND_PATH(
|
||||||
|
LIBEVDEV_INCLUDE_DIR libevdev/libevdev.h
|
||||||
|
HINTS ${PC_LIBEVDEV_INCLUDEDIR} ${PC_LIBEVDEV_INCLUDE_DIRS}
|
||||||
|
/usr/include
|
||||||
|
/usr/local/include
|
||||||
|
${LIBEVDEV_PATH_INCLUDES}
|
||||||
|
)
|
||||||
|
|
||||||
|
FIND_LIBRARY(
|
||||||
|
LIBEVDEV_LIBRARY
|
||||||
|
NAMES evdev libevdev
|
||||||
|
HINTS ${PC_LIBEVDEV_LIBDIR} ${PC_LIBEVDEV_LIBRARY_DIRS}
|
||||||
|
PATHS ${ADDITIONAL_LIBRARY_PATHS}
|
||||||
|
${LIBEVDEV_PATH_LIB}
|
||||||
|
)
|
||||||
|
|
||||||
|
set(LIBEVDEV_LIBRARIES ${LIBEVDEV_LIBRARY} )
|
||||||
|
set(LIBEVDEV_INCLUDE_DIRS ${LIBEVDEV_INCLUDE_DIR} )
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(LIBEVDEV DEFAULT_MSG
|
||||||
|
LIBEVDEV_LIBRARY LIBEVDEV_INCLUDE_DIR)
|
||||||
|
|
||||||
|
mark_as_advanced(LIBEVDEV_INCLUDE_DIR LIBEVDEV_LIBRARY )
|
107
CMakeModules/FindLibdrm.cmake
Normal file
107
CMakeModules/FindLibdrm.cmake
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
# https://raw.githubusercontent.com/KDE/kwin/master/cmake/modules/FindLibdrm.cmake
|
||||||
|
|
||||||
|
#.rst:
|
||||||
|
# FindLibdrm
|
||||||
|
# -------
|
||||||
|
#
|
||||||
|
# Try to find libdrm on a Unix system.
|
||||||
|
#
|
||||||
|
# This will define the following variables:
|
||||||
|
#
|
||||||
|
# ``Libdrm_FOUND``
|
||||||
|
# True if (the requested version of) libdrm is available
|
||||||
|
# ``Libdrm_VERSION``
|
||||||
|
# The version of libdrm
|
||||||
|
# ``Libdrm_LIBRARIES``
|
||||||
|
# This can be passed to target_link_libraries() instead of the ``Libdrm::Libdrm``
|
||||||
|
# target
|
||||||
|
# ``Libdrm_INCLUDE_DIRS``
|
||||||
|
# This should be passed to target_include_directories() if the target is not
|
||||||
|
# used for linking
|
||||||
|
# ``Libdrm_DEFINITIONS``
|
||||||
|
# This should be passed to target_compile_options() if the target is not
|
||||||
|
# used for linking
|
||||||
|
#
|
||||||
|
# If ``Libdrm_FOUND`` is TRUE, it will also define the following imported target:
|
||||||
|
#
|
||||||
|
# ``Libdrm::Libdrm``
|
||||||
|
# The libdrm library
|
||||||
|
#
|
||||||
|
# In general we recommend using the imported target, as it is easier to use.
|
||||||
|
# Bear in mind, however, that if the target is in the link interface of an
|
||||||
|
# exported library, it must be made available by the package config file.
|
||||||
|
|
||||||
|
#=============================================================================
|
||||||
|
# SPDX-FileCopyrightText: 2014 Alex Merry <alex.merry@kde.org>
|
||||||
|
# SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
#=============================================================================
|
||||||
|
|
||||||
|
if(CMAKE_VERSION VERSION_LESS 2.8.12)
|
||||||
|
message(FATAL_ERROR "CMake 2.8.12 is required by FindLibdrm.cmake")
|
||||||
|
endif()
|
||||||
|
if(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 2.8.12)
|
||||||
|
message(AUTHOR_WARNING "Your project should require at least CMake 2.8.12 to use FindLibdrm.cmake")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT WIN32)
|
||||||
|
# Use pkg-config to get the directories and then use these values
|
||||||
|
# in the FIND_PATH() and FIND_LIBRARY() calls
|
||||||
|
find_package(PkgConfig)
|
||||||
|
pkg_check_modules(PKG_Libdrm QUIET libdrm)
|
||||||
|
|
||||||
|
set(Libdrm_DEFINITIONS ${PKG_Libdrm_CFLAGS_OTHER})
|
||||||
|
set(Libdrm_VERSION ${PKG_Libdrm_VERSION})
|
||||||
|
|
||||||
|
find_path(Libdrm_INCLUDE_DIR
|
||||||
|
NAMES
|
||||||
|
xf86drm.h
|
||||||
|
HINTS
|
||||||
|
${PKG_Libdrm_INCLUDE_DIRS}
|
||||||
|
)
|
||||||
|
find_library(Libdrm_LIBRARY
|
||||||
|
NAMES
|
||||||
|
drm
|
||||||
|
HINTS
|
||||||
|
${PKG_Libdrm_LIBRARY_DIRS}
|
||||||
|
)
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(Libdrm
|
||||||
|
FOUND_VAR
|
||||||
|
Libdrm_FOUND
|
||||||
|
REQUIRED_VARS
|
||||||
|
Libdrm_LIBRARY
|
||||||
|
Libdrm_INCLUDE_DIR
|
||||||
|
VERSION_VAR
|
||||||
|
Libdrm_VERSION
|
||||||
|
)
|
||||||
|
|
||||||
|
if(Libdrm_FOUND AND NOT TARGET Libdrm::Libdrm)
|
||||||
|
add_library(Libdrm::Libdrm UNKNOWN IMPORTED)
|
||||||
|
set_target_properties(Libdrm::Libdrm PROPERTIES
|
||||||
|
IMPORTED_LOCATION "${Libdrm_LIBRARY}"
|
||||||
|
INTERFACE_COMPILE_OPTIONS "${Libdrm_DEFINITIONS}"
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES "${Libdrm_INCLUDE_DIR}"
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES "${Libdrm_INCLUDE_DIR}/libdrm"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
mark_as_advanced(Libdrm_LIBRARY Libdrm_INCLUDE_DIR)
|
||||||
|
|
||||||
|
# compatibility variables
|
||||||
|
set(Libdrm_LIBRARIES ${Libdrm_LIBRARY})
|
||||||
|
set(Libdrm_INCLUDE_DIRS ${Libdrm_INCLUDE_DIR} "${Libdrm_INCLUDE_DIR}/libdrm")
|
||||||
|
set(Libdrm_VERSION_STRING ${Libdrm_VERSION})
|
||||||
|
|
||||||
|
else()
|
||||||
|
message(STATUS "FindLibdrm.cmake cannot find libdrm on Windows systems.")
|
||||||
|
set(Libdrm_FOUND FALSE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(FeatureSummary)
|
||||||
|
set_package_properties(Libdrm PROPERTIES
|
||||||
|
URL "https://wiki.freedesktop.org/dri/"
|
||||||
|
DESCRIPTION "Userspace interface to kernel DRM services."
|
||||||
|
)
|
|
@ -142,6 +142,14 @@ if(USE_X11)
|
||||||
target_link_libraries(common PRIVATE "${X11_LIBRARIES}")
|
target_link_libraries(common PRIVATE "${X11_LIBRARIES}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(USE_DRMKMS)
|
||||||
|
target_sources(common PRIVATE
|
||||||
|
drm_display.cpp
|
||||||
|
drm_display.h
|
||||||
|
)
|
||||||
|
target_link_libraries(common PUBLIC Libdrm::Libdrm)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(USE_EGL)
|
if(USE_EGL)
|
||||||
target_sources(common PRIVATE
|
target_sources(common PRIVATE
|
||||||
gl/context_egl.cpp
|
gl/context_egl.cpp
|
||||||
|
@ -161,6 +169,14 @@ if(USE_EGL)
|
||||||
gl/context_egl_android.h
|
gl/context_egl_android.h
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
if(USE_DRMKMS)
|
||||||
|
target_compile_definitions(common PRIVATE "-DUSE_GBM=1")
|
||||||
|
target_sources(common PRIVATE
|
||||||
|
gl/context_egl_gbm.cpp
|
||||||
|
gl/context_egl_gbm.h
|
||||||
|
)
|
||||||
|
target_link_libraries(common PUBLIC GBM::GBM)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(USE_X11)
|
if(USE_X11)
|
||||||
|
|
249
src/common/drm_display.cpp
Normal file
249
src/common/drm_display.cpp
Normal file
|
@ -0,0 +1,249 @@
|
||||||
|
#include "drm_display.h"
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/log.h"
|
||||||
|
#include "common/string.h"
|
||||||
|
#include "file_system.h"
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
Log_SetChannel(DRMDisplay);
|
||||||
|
|
||||||
|
DRMDisplay::DRMDisplay(int card /*= 1*/) : m_card_id(card) {}
|
||||||
|
|
||||||
|
DRMDisplay::~DRMDisplay()
|
||||||
|
{
|
||||||
|
if (m_connector)
|
||||||
|
drmModeFreeConnector(m_connector);
|
||||||
|
|
||||||
|
if (m_card_fd >= 0)
|
||||||
|
close(m_card_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://gist.github.com/Miouyouyou/89e9fe56a2c59bce7d4a18a858f389ef
|
||||||
|
|
||||||
|
static uint32_t find_crtc_for_encoder(const drmModeRes* resources, const drmModeEncoder* encoder)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < resources->count_crtcs; i++)
|
||||||
|
{
|
||||||
|
/* possible_crtcs is a bitmask as described here:
|
||||||
|
* https://dvdhrm.wordpress.com/2012/09/13/linux-drm-mode-setting-api
|
||||||
|
*/
|
||||||
|
const uint32_t crtc_mask = 1 << i;
|
||||||
|
const uint32_t crtc_id = resources->crtcs[i];
|
||||||
|
if (encoder->possible_crtcs & crtc_mask)
|
||||||
|
{
|
||||||
|
return crtc_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* no match found */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t find_crtc_for_connector(int card_fd, const drmModeRes* resources, const drmModeConnector* connector)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < connector->count_encoders; i++)
|
||||||
|
{
|
||||||
|
const uint32_t encoder_id = connector->encoders[i];
|
||||||
|
drmModeEncoder* encoder = drmModeGetEncoder(card_fd, encoder_id);
|
||||||
|
|
||||||
|
if (encoder)
|
||||||
|
{
|
||||||
|
const uint32_t crtc_id = find_crtc_for_encoder(resources, encoder);
|
||||||
|
|
||||||
|
drmModeFreeEncoder(encoder);
|
||||||
|
if (crtc_id != 0)
|
||||||
|
{
|
||||||
|
return crtc_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* no match found */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DRMDisplay::Initialize()
|
||||||
|
{
|
||||||
|
if (m_card_id < 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
if (TryOpeningCard(i))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TryOpeningCard(m_card_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DRMDisplay::TryOpeningCard(int card)
|
||||||
|
{
|
||||||
|
if (m_card_fd >= 0)
|
||||||
|
close(m_card_fd);
|
||||||
|
|
||||||
|
m_card_fd = open(TinyString::FromFormat("/dev/dri/card%d", card), O_RDWR);
|
||||||
|
if (m_card_fd < 0)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("open(/dev/dri/card%d) failed: %d (%s)", card, errno, strerror(errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
drmModeRes* resources = drmModeGetResources(m_card_fd);
|
||||||
|
if (!resources)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("drmModeGetResources() failed: %d (%s)", errno, strerror(errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert(!m_connector);
|
||||||
|
|
||||||
|
for (int i = 0; i < resources->count_connectors; i++)
|
||||||
|
{
|
||||||
|
drmModeConnector* next_connector = drmModeGetConnector(m_card_fd, resources->connectors[i]);
|
||||||
|
if (next_connector->connection == DRM_MODE_CONNECTED)
|
||||||
|
{
|
||||||
|
m_connector = next_connector;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
drmModeFreeConnector(next_connector);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_connector)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("No connector found");
|
||||||
|
drmModeFreeResources(resources);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < m_connector->count_modes; i++)
|
||||||
|
{
|
||||||
|
drmModeModeInfo* next_mode = &m_connector->modes[i];
|
||||||
|
if (next_mode->type & DRM_MODE_TYPE_PREFERRED)
|
||||||
|
{
|
||||||
|
m_mode = next_mode;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_mode || (next_mode->hdisplay * next_mode->vdisplay) > (m_mode->hdisplay * m_mode->vdisplay))
|
||||||
|
{
|
||||||
|
m_mode = next_mode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_mode)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("No mode found");
|
||||||
|
drmModeFreeResources(resources);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
drmModeEncoder* encoder = nullptr;
|
||||||
|
for (int i = 0; i < resources->count_encoders; i++)
|
||||||
|
{
|
||||||
|
drmModeEncoder* next_encoder = drmModeGetEncoder(m_card_fd, resources->encoders[i]);
|
||||||
|
if (next_encoder->encoder_id == m_connector->encoder_id)
|
||||||
|
{
|
||||||
|
encoder = next_encoder;
|
||||||
|
m_crtc_id = encoder->crtc_id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
drmModeFreeEncoder(next_encoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (encoder)
|
||||||
|
{
|
||||||
|
drmModeFreeEncoder(encoder);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_crtc_id = find_crtc_for_connector(m_card_fd, resources, m_connector);
|
||||||
|
if (m_crtc_id == 0)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("No CRTC found");
|
||||||
|
drmModeFreeResources(resources);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drmModeFreeResources(resources);
|
||||||
|
|
||||||
|
m_card_id = card;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<u32> DRMDisplay::AddBuffer(u32 width, u32 height, u32 format, u32 handle, u32 pitch, u32 offset)
|
||||||
|
{
|
||||||
|
uint32_t bo_handles[4] = {handle, 0, 0, 0};
|
||||||
|
uint32_t pitches[4] = {pitch, 0, 0, 0};
|
||||||
|
uint32_t offsets[4] = {offset, 0, 0, 0};
|
||||||
|
|
||||||
|
u32 fb_id;
|
||||||
|
int res = drmModeAddFB2(m_card_fd, width, height, format, bo_handles, pitches, offsets, &fb_id, 0);
|
||||||
|
if (res != 0)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("drmModeAddFB2() failed: %d", res);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fb_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DRMDisplay::RemoveBuffer(u32 fb_id)
|
||||||
|
{
|
||||||
|
drmModeRmFB(m_card_fd, fb_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DRMDisplay::PresentBuffer(u32 fb_id, bool wait_for_vsync)
|
||||||
|
{
|
||||||
|
if (!wait_for_vsync)
|
||||||
|
{
|
||||||
|
u32 connector_id = m_connector->connector_id;
|
||||||
|
int res = drmModeSetCrtc(m_card_fd, m_crtc_id, fb_id, 0, 0, &connector_id, 1, m_mode);
|
||||||
|
if (res != 0)
|
||||||
|
Log_ErrorPrintf("drmModeSetCrtc() failed: %d", res);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool waiting_for_flip = true;
|
||||||
|
drmEventContext event_ctx = {};
|
||||||
|
event_ctx.version = DRM_EVENT_CONTEXT_VERSION;
|
||||||
|
event_ctx.page_flip_handler = [](int fd, unsigned int frame, unsigned int sec, unsigned int usec, void* data) {
|
||||||
|
*reinterpret_cast<bool*>(data) = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
int res = drmModePageFlip(m_card_fd, m_crtc_id, fb_id, DRM_MODE_PAGE_FLIP_EVENT, &waiting_for_flip);
|
||||||
|
if (res != 0)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("drmModePageFlip() failed: %d", res);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (waiting_for_flip)
|
||||||
|
{
|
||||||
|
fd_set fds;
|
||||||
|
FD_ZERO(&fds);
|
||||||
|
FD_SET(m_card_fd, &fds);
|
||||||
|
int res = select(m_card_fd + 1, &fds, nullptr, nullptr, nullptr);
|
||||||
|
if (res < 0)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("select() failed: %d", errno);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (res == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
drmHandleEvent(m_card_fd, &event_ctx);
|
||||||
|
}
|
||||||
|
}
|
38
src/common/drm_display.h
Normal file
38
src/common/drm_display.h
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
#pragma once
|
||||||
|
#include "core/types.h"
|
||||||
|
#include <array>
|
||||||
|
#include <optional>
|
||||||
|
#include <xf86drm.h>
|
||||||
|
#include <xf86drmMode.h>
|
||||||
|
|
||||||
|
class DRMDisplay
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DRMDisplay(int card = -1);
|
||||||
|
~DRMDisplay();
|
||||||
|
|
||||||
|
bool Initialize();
|
||||||
|
|
||||||
|
int GetCardID() const { return m_card_id; }
|
||||||
|
int GetCardFD() const { return m_card_fd; }
|
||||||
|
u32 GetWidth() const { return m_mode->hdisplay; }
|
||||||
|
u32 GetHeight() const { return m_mode->vdisplay; }
|
||||||
|
|
||||||
|
std::optional<u32> AddBuffer(u32 width, u32 height, u32 format, u32 handle, u32 pitch, u32 offset);
|
||||||
|
void RemoveBuffer(u32 fb_id);
|
||||||
|
void PresentBuffer(u32 fb_id, bool wait_for_vsync);
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum : u32
|
||||||
|
{
|
||||||
|
MAX_BUFFERS = 5
|
||||||
|
};
|
||||||
|
|
||||||
|
bool TryOpeningCard(int card);
|
||||||
|
|
||||||
|
int m_card_id = 0;
|
||||||
|
int m_card_fd = -1;
|
||||||
|
u32 m_crtc_id = 0;
|
||||||
|
drmModeConnector* m_connector = nullptr;
|
||||||
|
drmModeModeInfo* m_mode = nullptr;
|
||||||
|
};
|
|
@ -11,18 +11,21 @@ Log_SetChannel(GL::Context);
|
||||||
|
|
||||||
#if defined(WIN32) && !defined(_M_ARM64)
|
#if defined(WIN32) && !defined(_M_ARM64)
|
||||||
#include "context_wgl.h"
|
#include "context_wgl.h"
|
||||||
#elif defined(__APPLE__)
|
#elif defined(__APPLE__) && !defined(LIBERTRO)
|
||||||
#include "context_agl.h"
|
#include "context_agl.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_EGL
|
#ifdef USE_EGL
|
||||||
#if defined(USE_X11) || defined(USE_WAYLAND)
|
#if defined(USE_X11) || defined(USE_WAYLAND) || defined(USE_GBM)
|
||||||
#if defined(USE_X11)
|
#if defined(USE_X11)
|
||||||
#include "context_egl_x11.h"
|
#include "context_egl_x11.h"
|
||||||
#endif
|
#endif
|
||||||
#if defined(USE_WAYLAND)
|
#if defined(USE_WAYLAND)
|
||||||
#include "context_egl_wayland.h"
|
#include "context_egl_wayland.h"
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(USE_GBM)
|
||||||
|
#include "context_egl_gbm.h"
|
||||||
|
#endif
|
||||||
#elif defined(ANDROID)
|
#elif defined(ANDROID)
|
||||||
#include "context_egl_android.h"
|
#include "context_egl_android.h"
|
||||||
#else
|
#else
|
||||||
|
@ -77,7 +80,7 @@ std::unique_ptr<GL::Context> Context::Create(const WindowInfo& wi, const Version
|
||||||
std::unique_ptr<Context> context;
|
std::unique_ptr<Context> context;
|
||||||
#if defined(WIN32) && !defined(_M_ARM64)
|
#if defined(WIN32) && !defined(_M_ARM64)
|
||||||
context = ContextWGL::Create(wi, versions_to_try, num_versions_to_try);
|
context = ContextWGL::Create(wi, versions_to_try, num_versions_to_try);
|
||||||
#elif defined(__APPLE__)
|
#elif defined(__APPLE__) && !defined(LIBRETRO)
|
||||||
context = ContextAGL::Create(wi, versions_to_try, num_versions_to_try);
|
context = ContextAGL::Create(wi, versions_to_try, num_versions_to_try);
|
||||||
#elif defined(ANDROID)
|
#elif defined(ANDROID)
|
||||||
#ifdef USE_EGL
|
#ifdef USE_EGL
|
||||||
|
@ -105,6 +108,11 @@ std::unique_ptr<GL::Context> Context::Create(const WindowInfo& wi, const Version
|
||||||
context = ContextEGLWayland::Create(wi, versions_to_try, num_versions_to_try);
|
context = ContextEGLWayland::Create(wi, versions_to_try, num_versions_to_try);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(USE_GBM)
|
||||||
|
if (wi.type == WindowInfo::Type::DRM)
|
||||||
|
context = ContextEGLGBM::Create(wi, versions_to_try, num_versions_to_try);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!context)
|
if (!context)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#include "context_egl.h"
|
#include "context_egl.h"
|
||||||
#include "../assert.h"
|
#include "../assert.h"
|
||||||
#include "../log.h"
|
#include "../log.h"
|
||||||
|
#include <optional>
|
||||||
|
#include <vector>
|
||||||
Log_SetChannel(GL::ContextEGL);
|
Log_SetChannel(GL::ContextEGL);
|
||||||
|
|
||||||
namespace GL {
|
namespace GL {
|
||||||
|
@ -33,12 +35,8 @@ bool ContextEGL::Initialize(const Version* versions_to_try, size_t num_versions_
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_display = eglGetDisplay(static_cast<EGLNativeDisplayType>(m_wi.display_connection));
|
if (!SetDisplay())
|
||||||
if (!m_display)
|
|
||||||
{
|
|
||||||
Log_ErrorPrintf("eglGetDisplay() failed: %d", eglGetError());
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
int egl_major, egl_minor;
|
int egl_major, egl_minor;
|
||||||
if (!eglInitialize(m_display, &egl_major, &egl_minor))
|
if (!eglInitialize(m_display, &egl_major, &egl_minor))
|
||||||
|
@ -66,6 +64,18 @@ bool ContextEGL::Initialize(const Version* versions_to_try, size_t num_versions_
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ContextEGL::SetDisplay()
|
||||||
|
{
|
||||||
|
m_display = eglGetDisplay(static_cast<EGLNativeDisplayType>(m_wi.display_connection));
|
||||||
|
if (!m_display)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("eglGetDisplay() failed: %d", eglGetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void* ContextEGL::GetProcAddress(const char* name)
|
void* ContextEGL::GetProcAddress(const char* name)
|
||||||
{
|
{
|
||||||
return reinterpret_cast<void*>(eglGetProcAddress(name));
|
return reinterpret_cast<void*>(eglGetProcAddress(name));
|
||||||
|
@ -216,6 +226,36 @@ bool ContextEGL::CreatePBufferSurface()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ContextEGL::CheckConfigSurfaceFormat(EGLConfig config, WindowInfo::SurfaceFormat format) const
|
||||||
|
{
|
||||||
|
int red_size, green_size, blue_size, alpha_size;
|
||||||
|
if (!eglGetConfigAttrib(m_display, config, EGL_RED_SIZE, &red_size) ||
|
||||||
|
!eglGetConfigAttrib(m_display, config, EGL_GREEN_SIZE, &green_size) ||
|
||||||
|
!eglGetConfigAttrib(m_display, config, EGL_BLUE_SIZE, &blue_size) ||
|
||||||
|
!eglGetConfigAttrib(m_display, config, EGL_ALPHA_SIZE, &alpha_size))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case WindowInfo::SurfaceFormat::Auto:
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case WindowInfo::SurfaceFormat::RGB8:
|
||||||
|
return (red_size == 8 && green_size == 8 && blue_size == 8);
|
||||||
|
|
||||||
|
case WindowInfo::SurfaceFormat::RGBA8:
|
||||||
|
return (red_size == 8 && green_size == 8 && blue_size == 8 && alpha_size == 8);
|
||||||
|
|
||||||
|
case WindowInfo::SurfaceFormat::RGB565:
|
||||||
|
return (red_size == 5 && green_size == 6 && blue_size == 5);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool ContextEGL::CreateContext(const Version& version, EGLContext share_context)
|
bool ContextEGL::CreateContext(const Version& version, EGLContext share_context)
|
||||||
{
|
{
|
||||||
Log_DevPrintf(
|
Log_DevPrintf(
|
||||||
|
@ -263,6 +303,9 @@ bool ContextEGL::CreateContext(const Version& version, EGLContext share_context)
|
||||||
surface_attribs[nsurface_attribs++] = 5;
|
surface_attribs[nsurface_attribs++] = 5;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case WindowInfo::SurfaceFormat::Auto:
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
UnreachableCode();
|
UnreachableCode();
|
||||||
break;
|
break;
|
||||||
|
@ -272,13 +315,36 @@ bool ContextEGL::CreateContext(const Version& version, EGLContext share_context)
|
||||||
surface_attribs[nsurface_attribs++] = 0;
|
surface_attribs[nsurface_attribs++] = 0;
|
||||||
|
|
||||||
EGLint num_configs;
|
EGLint num_configs;
|
||||||
EGLConfig config;
|
if (!eglChooseConfig(m_display, surface_attribs, nullptr, 0, &num_configs) || num_configs == 0)
|
||||||
if (!eglChooseConfig(m_display, surface_attribs, &config, 1, &num_configs) || num_configs == 0)
|
|
||||||
{
|
{
|
||||||
Log_ErrorPrintf("eglChooseConfig() failed: %d", eglGetError());
|
Log_ErrorPrintf("eglChooseConfig() failed: %d", eglGetError());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<EGLConfig> configs(static_cast<u32>(num_configs));
|
||||||
|
if (!eglChooseConfig(m_display, surface_attribs, configs.data(), num_configs, &num_configs))
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("eglChooseConfig() failed: %d", eglGetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
configs.resize(static_cast<u32>(num_configs));
|
||||||
|
|
||||||
|
std::optional<EGLConfig> config;
|
||||||
|
for (EGLConfig check_config : configs)
|
||||||
|
{
|
||||||
|
if (CheckConfigSurfaceFormat(check_config, m_wi.surface_format))
|
||||||
|
{
|
||||||
|
config = check_config;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!config.has_value())
|
||||||
|
{
|
||||||
|
Log_WarningPrintf("No EGL configs matched exactly, using first.");
|
||||||
|
config = configs.front();
|
||||||
|
}
|
||||||
|
|
||||||
int attribs[8];
|
int attribs[8];
|
||||||
int nattribs = 0;
|
int nattribs = 0;
|
||||||
if (version.profile != Profile::NoProfile)
|
if (version.profile != Profile::NoProfile)
|
||||||
|
@ -297,7 +363,7 @@ bool ContextEGL::CreateContext(const Version& version, EGLContext share_context)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_context = eglCreateContext(m_display, config, share_context, attribs);
|
m_context = eglCreateContext(m_display, config.value(), share_context, attribs);
|
||||||
if (!m_context)
|
if (!m_context)
|
||||||
{
|
{
|
||||||
Log_ErrorPrintf("eglCreateContext() failed: %d", eglGetError());
|
Log_ErrorPrintf("eglCreateContext() failed: %d", eglGetError());
|
||||||
|
@ -308,7 +374,7 @@ bool ContextEGL::CreateContext(const Version& version, EGLContext share_context)
|
||||||
"Got version %u.%u (%s)", version.major_version, version.minor_version,
|
"Got version %u.%u (%s)", version.major_version, version.minor_version,
|
||||||
version.profile == Context::Profile::ES ? "ES" : (version.profile == Context::Profile::Core ? "Core" : "None"));
|
version.profile == Context::Profile::ES ? "ES" : (version.profile == Context::Profile::Core ? "Core" : "None"));
|
||||||
|
|
||||||
m_config = config;
|
m_config = config.value();
|
||||||
m_version = version;
|
m_version = version;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ public:
|
||||||
virtual std::unique_ptr<Context> CreateSharedContext(const WindowInfo& wi) override;
|
virtual std::unique_ptr<Context> CreateSharedContext(const WindowInfo& wi) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
virtual bool SetDisplay();
|
||||||
virtual EGLNativeWindowType GetNativeWindow(EGLConfig config);
|
virtual EGLNativeWindowType GetNativeWindow(EGLConfig config);
|
||||||
|
|
||||||
bool Initialize(const Version* versions_to_try, size_t num_versions_to_try);
|
bool Initialize(const Version* versions_to_try, size_t num_versions_to_try);
|
||||||
|
@ -31,6 +32,7 @@ protected:
|
||||||
bool CreateContextAndSurface(const Version& version, EGLContext share_context, bool make_current);
|
bool CreateContextAndSurface(const Version& version, EGLContext share_context, bool make_current);
|
||||||
bool CreateSurface();
|
bool CreateSurface();
|
||||||
bool CreatePBufferSurface();
|
bool CreatePBufferSurface();
|
||||||
|
bool CheckConfigSurfaceFormat(EGLConfig config, WindowInfo::SurfaceFormat format) const;
|
||||||
|
|
||||||
EGLDisplay m_display = EGL_NO_DISPLAY;
|
EGLDisplay m_display = EGL_NO_DISPLAY;
|
||||||
EGLSurface m_surface = EGL_NO_SURFACE;
|
EGLSurface m_surface = EGL_NO_SURFACE;
|
||||||
|
|
253
src/common/gl/context_egl_gbm.cpp
Normal file
253
src/common/gl/context_egl_gbm.cpp
Normal file
|
@ -0,0 +1,253 @@
|
||||||
|
#include "context_egl_gbm.h"
|
||||||
|
#include "../assert.h"
|
||||||
|
#include "../log.h"
|
||||||
|
#include <drm.h>
|
||||||
|
#include <drm_fourcc.h>
|
||||||
|
#include <gbm.h>
|
||||||
|
Log_SetChannel(GL::ContextEGLGBM);
|
||||||
|
|
||||||
|
namespace GL {
|
||||||
|
ContextEGLGBM::ContextEGLGBM(const WindowInfo& wi) : ContextEGL(wi)
|
||||||
|
{
|
||||||
|
#ifdef CONTEXT_EGL_GBM_USE_PRESENT_THREAD
|
||||||
|
StartPresentThread();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
ContextEGLGBM::~ContextEGLGBM()
|
||||||
|
{
|
||||||
|
#ifdef CONTEXT_EGL_GBM_USE_PRESENT_THREAD
|
||||||
|
StopPresentThread();
|
||||||
|
Assert(!m_current_present_buffer);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
while (m_num_buffers > 0)
|
||||||
|
{
|
||||||
|
Buffer& buffer = m_buffers[--m_num_buffers];
|
||||||
|
GetDisplay()->RemoveBuffer(buffer.fb_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_fb_surface)
|
||||||
|
gbm_surface_destroy(m_fb_surface);
|
||||||
|
|
||||||
|
if (m_gbm_device)
|
||||||
|
gbm_device_destroy(m_gbm_device);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Context> ContextEGLGBM::Create(const WindowInfo& wi, const Version* versions_to_try,
|
||||||
|
size_t num_versions_to_try)
|
||||||
|
{
|
||||||
|
std::unique_ptr<ContextEGLGBM> context = std::make_unique<ContextEGLGBM>(wi);
|
||||||
|
if (!context->CreateGBMDevice() || !context->Initialize(versions_to_try, num_versions_to_try))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Context> ContextEGLGBM::CreateSharedContext(const WindowInfo& wi)
|
||||||
|
{
|
||||||
|
std::unique_ptr<ContextEGLGBM> context = std::make_unique<ContextEGLGBM>(wi);
|
||||||
|
context->m_display = m_display;
|
||||||
|
|
||||||
|
if (!context->CreateContextAndSurface(m_version, m_context, false))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContextEGLGBM::ResizeSurface(u32 new_surface_width, u32 new_surface_height)
|
||||||
|
{
|
||||||
|
ContextEGL::ResizeSurface(new_surface_width, new_surface_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ContextEGLGBM::CreateGBMDevice()
|
||||||
|
{
|
||||||
|
Assert(!m_gbm_device);
|
||||||
|
m_gbm_device = gbm_create_device(GetDisplay()->GetCardFD());
|
||||||
|
if (!m_gbm_device)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("gbm_create_device() failed: %d", errno);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ContextEGLGBM::SetDisplay()
|
||||||
|
{
|
||||||
|
if (!eglGetPlatformDisplayEXT)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("eglGetPlatformDisplayEXT() not loaded");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_display = eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR, m_gbm_device, nullptr);
|
||||||
|
if (!m_display)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("eglGetPlatformDisplayEXT() failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLNativeWindowType ContextEGLGBM::GetNativeWindow(EGLConfig config)
|
||||||
|
{
|
||||||
|
EGLint visual_id;
|
||||||
|
eglGetConfigAttrib(m_display, config, EGL_NATIVE_VISUAL_ID, &visual_id);
|
||||||
|
|
||||||
|
Assert(!m_fb_surface);
|
||||||
|
m_fb_surface = gbm_surface_create(m_gbm_device, GetDisplay()->GetWidth(), GetDisplay()->GetHeight(),
|
||||||
|
static_cast<u32>(visual_id), GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT);
|
||||||
|
if (!m_fb_surface)
|
||||||
|
{
|
||||||
|
Log_ErrorPrintf("gbm_surface_create() failed: %d", errno);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return (EGLNativeWindowType)((void*)m_fb_surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
ContextEGLGBM::Buffer* ContextEGLGBM::LockFrontBuffer()
|
||||||
|
{
|
||||||
|
struct gbm_bo* bo = gbm_surface_lock_front_buffer(m_fb_surface);
|
||||||
|
|
||||||
|
Buffer* buffer = nullptr;
|
||||||
|
for (u32 i = 0; i < m_num_buffers; i++)
|
||||||
|
{
|
||||||
|
if (m_buffers[i].bo == bo)
|
||||||
|
{
|
||||||
|
buffer = &m_buffers[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!buffer)
|
||||||
|
{
|
||||||
|
// haven't tracked this buffer yet
|
||||||
|
Assert(m_num_buffers < MAX_BUFFERS);
|
||||||
|
|
||||||
|
const u32 width = gbm_bo_get_width(bo);
|
||||||
|
const u32 height = gbm_bo_get_height(bo);
|
||||||
|
const u32 stride = gbm_bo_get_stride(bo);
|
||||||
|
const u32 format = gbm_bo_get_format(bo);
|
||||||
|
const u32 handle = gbm_bo_get_handle(bo).u32;
|
||||||
|
|
||||||
|
std::optional<u32> fb_id = GetDisplay()->AddBuffer(width, height, format, handle, stride, 0);
|
||||||
|
if (!fb_id.has_value())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
buffer = &m_buffers[m_num_buffers];
|
||||||
|
buffer->bo = bo;
|
||||||
|
buffer->fb_id = fb_id.value();
|
||||||
|
m_num_buffers++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContextEGLGBM::ReleaseBuffer(Buffer* buffer)
|
||||||
|
{
|
||||||
|
gbm_surface_release_buffer(m_fb_surface, buffer->bo);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContextEGLGBM::PresentBuffer(Buffer* buffer, bool wait_for_vsync)
|
||||||
|
{
|
||||||
|
GetDisplay()->PresentBuffer(buffer->fb_id, wait_for_vsync);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ContextEGLGBM::SwapBuffers()
|
||||||
|
{
|
||||||
|
if (!ContextEGL::SwapBuffers())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
#ifdef CONTEXT_EGL_GBM_USE_PRESENT_THREAD
|
||||||
|
std::unique_lock lock(m_present_mutex);
|
||||||
|
m_present_pending.store(true);
|
||||||
|
m_present_cv.notify_one();
|
||||||
|
if (m_vsync)
|
||||||
|
m_present_done_cv.wait(lock, [this]() { return !m_present_pending.load(); });
|
||||||
|
#else
|
||||||
|
Buffer* front_buffer = LockFrontBuffer();
|
||||||
|
if (!front_buffer)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
PresentSurface(front_buffer, m_vsync && m_last_front_buffer);
|
||||||
|
|
||||||
|
if (m_last_front_buffer)
|
||||||
|
ReleaseBuffer(m_last_front_buffer);
|
||||||
|
|
||||||
|
m_last_front_buffer = front_buffer;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ContextEGLGBM::SetSwapInterval(s32 interval)
|
||||||
|
{
|
||||||
|
if (interval < 0 || interval > 1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::unique_lock lock(m_present_mutex);
|
||||||
|
m_vsync = (interval > 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONTEXT_EGL_GBM_USE_PRESENT_THREAD
|
||||||
|
|
||||||
|
void ContextEGLGBM::StartPresentThread()
|
||||||
|
{
|
||||||
|
m_present_thread_shutdown.store(false);
|
||||||
|
m_present_thread = std::thread(&ContextEGLGBM::PresentThread, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContextEGLGBM::StopPresentThread()
|
||||||
|
{
|
||||||
|
if (!m_present_thread.joinable())
|
||||||
|
return;
|
||||||
|
|
||||||
|
{
|
||||||
|
std::unique_lock lock(m_present_mutex);
|
||||||
|
m_present_thread_shutdown.store(true);
|
||||||
|
m_present_cv.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_present_thread.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContextEGLGBM::PresentThread()
|
||||||
|
{
|
||||||
|
std::unique_lock lock(m_present_mutex);
|
||||||
|
|
||||||
|
while (!m_present_thread_shutdown.load())
|
||||||
|
{
|
||||||
|
m_present_cv.wait(lock);
|
||||||
|
|
||||||
|
if (!m_present_pending.load())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Buffer* next_buffer = LockFrontBuffer();
|
||||||
|
const bool wait_for_vsync = m_vsync && m_current_present_buffer;
|
||||||
|
|
||||||
|
lock.unlock();
|
||||||
|
PresentBuffer(next_buffer, wait_for_vsync);
|
||||||
|
lock.lock();
|
||||||
|
|
||||||
|
if (m_current_present_buffer)
|
||||||
|
ReleaseBuffer(m_current_present_buffer);
|
||||||
|
|
||||||
|
m_current_present_buffer = next_buffer;
|
||||||
|
m_present_pending.store(false);
|
||||||
|
m_present_done_cv.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_current_present_buffer)
|
||||||
|
{
|
||||||
|
ReleaseBuffer(m_current_present_buffer);
|
||||||
|
m_current_present_buffer = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace GL
|
76
src/common/gl/context_egl_gbm.h
Normal file
76
src/common/gl/context_egl_gbm.h
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
#pragma once
|
||||||
|
#include "../drm_display.h"
|
||||||
|
#include "context_egl.h"
|
||||||
|
#include <atomic>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <gbm.h>
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#define CONTEXT_EGL_GBM_USE_PRESENT_THREAD 1
|
||||||
|
|
||||||
|
namespace GL {
|
||||||
|
|
||||||
|
class ContextEGLGBM final : public ContextEGL
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ContextEGLGBM(const WindowInfo& wi);
|
||||||
|
~ContextEGLGBM() override;
|
||||||
|
|
||||||
|
static std::unique_ptr<Context> Create(const WindowInfo& wi, const Version* versions_to_try,
|
||||||
|
size_t num_versions_to_try);
|
||||||
|
|
||||||
|
std::unique_ptr<Context> CreateSharedContext(const WindowInfo& wi) override;
|
||||||
|
void ResizeSurface(u32 new_surface_width = 0, u32 new_surface_height = 0) override;
|
||||||
|
|
||||||
|
bool SwapBuffers() override;
|
||||||
|
bool SetSwapInterval(s32 interval) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool SetDisplay() override;
|
||||||
|
EGLNativeWindowType GetNativeWindow(EGLConfig config) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum : u32
|
||||||
|
{
|
||||||
|
MAX_BUFFERS = 5
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Buffer
|
||||||
|
{
|
||||||
|
struct gbm_bo* bo;
|
||||||
|
u32 fb_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
DRMDisplay* GetDisplay() { return static_cast<DRMDisplay*>(m_wi.display_connection); }
|
||||||
|
|
||||||
|
bool CreateGBMDevice();
|
||||||
|
Buffer* LockFrontBuffer();
|
||||||
|
void ReleaseBuffer(Buffer* buffer);
|
||||||
|
void PresentBuffer(Buffer* buffer, bool wait_for_vsync);
|
||||||
|
|
||||||
|
void StartPresentThread();
|
||||||
|
void StopPresentThread();
|
||||||
|
void PresentThread();
|
||||||
|
|
||||||
|
bool m_vsync = true;
|
||||||
|
|
||||||
|
struct gbm_device* m_gbm_device = nullptr;
|
||||||
|
struct gbm_surface* m_fb_surface = nullptr;
|
||||||
|
|
||||||
|
#ifdef CONTEXT_EGL_GBM_USE_PRESENT_THREAD
|
||||||
|
std::thread m_present_thread;
|
||||||
|
std::mutex m_present_mutex;
|
||||||
|
std::condition_variable m_present_cv;
|
||||||
|
std::atomic_bool m_present_pending{false};
|
||||||
|
std::atomic_bool m_present_thread_shutdown{false};
|
||||||
|
std::condition_variable m_present_done_cv;
|
||||||
|
|
||||||
|
Buffer* m_current_present_buffer = nullptr;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
u32 m_num_buffers = 0;
|
||||||
|
std::array<Buffer, MAX_BUFFERS> m_buffers{};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace GL
|
|
@ -221,6 +221,9 @@ bool ContextGLX::CreateWindow(int screen)
|
||||||
attribs[nattribs++] = 5;
|
attribs[nattribs++] = 5;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case WindowInfo::SurfaceFormat::Auto:
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
UnreachableCode();
|
UnreachableCode();
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -204,6 +204,9 @@ bool ContextWGL::InitializeDC()
|
||||||
pfd.cBlueBits = 5;
|
pfd.cBlueBits = 5;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case WindowInfo::SurfaceFormat::Auto:
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
UnreachableCode();
|
UnreachableCode();
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -12,11 +12,13 @@ struct WindowInfo
|
||||||
Wayland,
|
Wayland,
|
||||||
MacOS,
|
MacOS,
|
||||||
Android,
|
Android,
|
||||||
|
DRM,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class SurfaceFormat
|
enum class SurfaceFormat
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
|
Auto,
|
||||||
RGB8,
|
RGB8,
|
||||||
RGBA8,
|
RGBA8,
|
||||||
RGB565,
|
RGB565,
|
||||||
|
|
Loading…
Reference in a new issue