[drm] Fix DRM mode setting to use SFML window dimensions

- actually use the dimensions of the SFML window as the DRM mode
      dimensions when setting a DRM mode.
    - If SFML window dimensions don't match a valid DRM mode, show
      the window contents on screen in the current mode instead of
      simply failing to show anything.
    - With these change the SFML examples are now working for me
This commit is contained in:
Andrew Mickelson 2024-07-17 20:15:09 -07:00 committed by Lukas Dürrenberger
parent 40fc524b8d
commit 34b6bda233
3 changed files with 109 additions and 104 deletions

View File

@ -297,11 +297,13 @@ namespace
if (fileDescriptor < 0) if (fileDescriptor < 0)
continue; continue;
result = getResources(fileDescriptor, resources); result = getResources(fileDescriptor, resources);
if (!result && hasMonitorConnected(fileDescriptor, *resources) != 0)
{
#ifdef SFML_DEBUG #ifdef SFML_DEBUG
sf::err() << "DRM device used: " << i << std::endl; sf::err() << "DRM device used: " << device->nodes[DRM_NODE_PRIMARY] << std::endl;
#endif #endif
if(!result && hasMonitorConnected(fileDescriptor, *resources) != 0)
break; break;
}
close(fileDescriptor); close(fileDescriptor);
fileDescriptor = -1; fileDescriptor = -1;
} }
@ -312,23 +314,28 @@ namespace
return fileDescriptor; return fileDescriptor;
} }
int initDrm(sf::priv::Drm& drm, const char* device, const char* modeStr, unsigned int vrefresh) int initDrm()
{ {
if (initialized)
return 0;
drmModeResPtr resources; drmModeResPtr resources;
if (device) // Use environment variable "SFML_DRM_DEVICE" (or NULL if not set)
char* deviceStr = std::getenv("SFML_DRM_DEVICE");
if (deviceStr && *deviceStr)
{ {
drm.fileDescriptor = open(device, O_RDWR); drmNode.fileDescriptor = open(deviceStr, O_RDWR);
const int ret = getResources(drm.fileDescriptor, resources); const int ret = getResources(drmNode.fileDescriptor, resources);
if (ret < 0 && errno == EOPNOTSUPP) if (ret < 0 && errno == EOPNOTSUPP)
sf::err() << device << " does not look like a modeset device" << std::endl; sf::err() << deviceStr << " does not look like a modeset device" << std::endl;
} }
else else
{ {
drm.fileDescriptor = findDrmDevice(resources); drmNode.fileDescriptor = findDrmDevice(resources);
} }
if (drm.fileDescriptor < 0) if (drmNode.fileDescriptor < 0)
{ {
sf::err() << "Could not open drm device" << std::endl; sf::err() << "Could not open drm device" << std::endl;
return -1; return -1;
@ -344,7 +351,7 @@ namespace
drmModeConnectorPtr connector = NULL; drmModeConnectorPtr connector = NULL;
for (int i = 0; i < resources->count_connectors; ++i) for (int i = 0; i < resources->count_connectors; ++i)
{ {
connector = drmModeGetConnector(drm.fileDescriptor, resources->connectors[i]); connector = drmModeGetConnector(drmNode.fileDescriptor, resources->connectors[i]);
if (connector->connection == DRM_MODE_CONNECTED) if (connector->connection == DRM_MODE_CONNECTED)
{ {
// It's connected, let's use this! // It's connected, let's use this!
@ -361,31 +368,11 @@ namespace
return -1; return -1;
} }
// Find user requested mode:
if (modeStr && *modeStr)
{
for (int i = 0; i < connector->count_modes; ++i)
{
drmModeModeInfoPtr currentMode = &connector->modes[i];
if (std::strcmp(currentMode->name, modeStr) == 0)
{
if (vrefresh == 0 || currentMode->vrefresh == vrefresh)
{
drm.mode = currentMode;
break;
}
}
}
if (!drm.mode)
sf::err() << "Requested mode not found, using default mode!" << std::endl;
}
// Find encoder: // Find encoder:
drmModeEncoderPtr encoder = NULL; drmModeEncoderPtr encoder = NULL;
for (int i = 0; i < resources->count_encoders; ++i) for (int i = 0; i < resources->count_encoders; ++i)
{ {
encoder = drmModeGetEncoder(drm.fileDescriptor, resources->encoders[i]); encoder = drmModeGetEncoder(drmNode.fileDescriptor, resources->encoders[i]);
if (encoder->encoder_id == connector->encoder_id) if (encoder->encoder_id == connector->encoder_id)
break; break;
drmModeFreeEncoder(encoder); drmModeFreeEncoder(encoder);
@ -394,56 +381,56 @@ namespace
if (encoder) if (encoder)
{ {
drm.crtcId = encoder->crtc_id; drmNode.crtcId = encoder->crtc_id;
} }
else else
{ {
const sf::Uint32 crtcId = findCrtcForConnector(drm, *resources, *connector); const sf::Uint32 crtcId = findCrtcForConnector(drmNode, *resources, *connector);
if (crtcId == 0) if (crtcId == 0)
{ {
sf::err() << "No crtc found!" << std::endl; sf::err() << "No crtc found!" << std::endl;
return -1; return -1;
} }
drm.crtcId = crtcId; drmNode.crtcId = crtcId;
} }
drmModeFreeResources(resources); drmModeFreeResources(resources);
drm.connectorId = connector->connector_id; drmNode.connectorId = connector->connector_id;
drm.savedConnector = connector; drmNode.savedConnector = connector;
drm.savedEncoder = encoder; drmNode.savedEncoder = encoder;
// Get original display mode so we can restore display mode after program exits // Get original display mode so we can restore display mode after program exits
drm.originalCrtc = drmModeGetCrtc(drm.fileDescriptor, drm.crtcId); drmNode.originalCrtc = drmModeGetCrtc(drmNode.fileDescriptor, drmNode.crtcId);
// Let's use the current mode rather than the preferred one if the user didn't specify a mode with env vars gbmDevice = gbm_create_device(drmNode.fileDescriptor);
if (!drm.mode)
{ std::atexit(cleanup);
#ifdef SFML_DEBUG initialized = true;
sf::err() << "DRM using the current mode" << std::endl;
#endif pollFD.fd = drmNode.fileDescriptor;
drm.mode = &(drm.originalCrtc->mode); pollFD.events = POLLIN;
drmEventCtx.version = 2;
drmEventCtx.page_flip_handler = pageFlipHandler;
drmNode.mode = 0;
return 1;
} }
#ifdef SFML_DEBUG void setDrmMode( unsigned int width=0, unsigned int height=0 )
sf::err() << "DRM Mode used: " << drm.mode->name << "@" << drm.mode->vrefresh << std::endl;
#endif
return 0;
}
void checkInit()
{ {
if (initialized) // don't do anything if supplied width and height are 0 and we already have a drm mode
if (( width == 0 ) && drmNode.mode )
return; return;
// Use environment variable "SFML_DRM_DEVICE" (or NULL if not set) drmModeConnectorPtr connector = drmNode.savedConnector;
char* deviceString = std::getenv("SFML_DRM_DEVICE"); if (!connector)
if (deviceString && !*deviceString) return;
deviceString = NULL;
// Find user requested mode:
// Use environment variable "SFML_DRM_MODE" (or NULL if not set) // Use environment variable "SFML_DRM_MODE" (or NULL if not set)
char* modeString = std::getenv("SFML_DRM_MODE"); char* modeString = std::getenv("SFML_DRM_MODE");
@ -456,30 +443,41 @@ namespace
if (refreshString) if (refreshString)
refreshRate = static_cast<unsigned int>(atoi(refreshString)); refreshRate = static_cast<unsigned int>(atoi(refreshString));
if (initDrm(drmNode, bool matched = false;
deviceString, // device for (int i = 0; i < connector->count_modes; ++i)
modeString, // requested mode
refreshRate) < 0) // screen refresh rate
{ {
sf::err() << "Error initializing DRM" << std::endl; drmModeModeInfoPtr currentMode = &connector->modes[i];
return;
if (refreshRate == 0 || currentMode->vrefresh == refreshRate)
{
// prefer SFML_DRM_MODE setting if matched
if ((modeString && *modeString) && (std::strcmp(currentMode->name, modeString) == 0))
{
drmNode.mode = currentMode;
break;
} }
// otherwise match to program supplied width and height
if (!matched && ( currentMode->hdisplay == width ) && ( currentMode->vdisplay == height ))
{
drmNode.mode = currentMode;
matched=true;
}
}
}
if ((modeString && *modeString) && !drmNode.mode)
sf::err() << "SFML_DRM_MODE (" << modeString << ") not found, using default mode!" << std::endl;
gbmDevice = gbm_create_device(drmNode.fileDescriptor); // Let's use the current mode rather than the preferred one if the user didn't specify a mode with env vars
if (!drmNode.mode)
drmNode.mode = &(drmNode.originalCrtc->mode);
std::atexit(cleanup); #ifdef SFML_DEBUG
initialized = true; sf::err() << "DRM Mode used: " << drmNode.mode->name << "@" << drmNode.mode->vrefresh << std::endl;
#endif
pollFD.fd = drmNode.fileDescriptor;
pollFD.events = POLLIN;
drmEventCtx.version = 2;
drmEventCtx.page_flip_handler = pageFlipHandler;
} }
EGLDisplay getInitializedDisplay() EGLDisplay getInitializedDisplay()
{ {
checkInit();
if (display == EGL_NO_DISPLAY) if (display == EGL_NO_DISPLAY)
{ {
gladLoaderLoadEGL(EGL_NO_DISPLAY); gladLoaderLoadEGL(EGL_NO_DISPLAY);
@ -522,32 +520,34 @@ m_config (NULL),
m_currentBO (NULL), m_currentBO (NULL),
m_nextBO (NULL), m_nextBO (NULL),
m_gbmSurface (NULL), m_gbmSurface (NULL),
m_width (0),
m_height (0),
m_shown (false), m_shown (false),
m_scanOut (false) m_scanOut (false)
{ {
contextCount++; contextCount++;
if (initDrm() < 0)
return;
setDrmMode();
// Get the initialized EGL display // Get the initialized EGL display
m_display = getInitializedDisplay(); m_display = getInitializedDisplay();
// Get the best EGL config matching the default video settings // Get the best EGL config matching the default video settings
m_config = getBestConfig(m_display, VideoMode::getDesktopMode().bitsPerPixel, ContextSettings()); m_config = getBestConfig(m_display, ContextSettings());
updateSettings(); updateSettings();
// Create EGL context // Create EGL context
createContext(shared); createContext(shared);
if (shared) if (shared)
createSurface(shared->m_width, shared->m_height, VideoMode::getDesktopMode().bitsPerPixel, false); createSurface(drmNode.mode->hdisplay, drmNode.mode->vdisplay, false);
else // create a surface to force the GL to initialize (seems to be required for glGetString() etc ) else // create a surface to force the GL to initialize (seems to be required for glGetString() etc )
createSurface(1, 1, VideoMode::getDesktopMode().bitsPerPixel, false); createSurface(1, 1, false);
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
DRMContext::DRMContext(DRMContext* shared, const ContextSettings& settings, const WindowImpl* owner, unsigned int bitsPerPixel) : DRMContext::DRMContext(DRMContext* shared, const ContextSettings& settings, const WindowImpl* owner, unsigned int /*bitsPerPixel*/) :
m_display (EGL_NO_DISPLAY), m_display (EGL_NO_DISPLAY),
m_context (EGL_NO_CONTEXT), m_context (EGL_NO_CONTEXT),
m_surface (EGL_NO_SURFACE), m_surface (EGL_NO_SURFACE),
@ -555,33 +555,37 @@ m_config (NULL),
m_currentBO (NULL), m_currentBO (NULL),
m_nextBO (NULL), m_nextBO (NULL),
m_gbmSurface (NULL), m_gbmSurface (NULL),
m_width (0),
m_height (0),
m_shown (false), m_shown (false),
m_scanOut (false) m_scanOut (false)
{ {
contextCount++; contextCount++;
if (initDrm() < 0)
return;
Vector2u size;
if (owner)
size = owner->getSize();
setDrmMode(size.x, size.y);
// Get the initialized EGL display // Get the initialized EGL display
m_display = getInitializedDisplay(); m_display = getInitializedDisplay();
// Get the best EGL config matching the requested video settings // Get the best EGL config matching the requested video settings
m_config = getBestConfig(m_display, bitsPerPixel, settings); m_config = getBestConfig(m_display, settings);
updateSettings(); updateSettings();
// Create EGL context // Create EGL context
createContext(shared); createContext(shared);
if (owner) if (owner)
{ createSurface(drmNode.mode->hdisplay, drmNode.mode->vdisplay, true);
Vector2u size = owner->getSize();
createSurface(size.x, size.y, bitsPerPixel, true);
}
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
DRMContext::DRMContext(DRMContext* shared, const ContextSettings& settings, unsigned int width, unsigned int height) : DRMContext::DRMContext(DRMContext* shared, const ContextSettings& settings, unsigned int /*width*/, unsigned int /*height*/) :
m_display (EGL_NO_DISPLAY), m_display (EGL_NO_DISPLAY),
m_context (EGL_NO_CONTEXT), m_context (EGL_NO_CONTEXT),
m_surface (EGL_NO_SURFACE), m_surface (EGL_NO_SURFACE),
@ -589,23 +593,25 @@ m_config (NULL),
m_currentBO (NULL), m_currentBO (NULL),
m_nextBO (NULL), m_nextBO (NULL),
m_gbmSurface (NULL), m_gbmSurface (NULL),
m_width (0),
m_height (0),
m_shown (false), m_shown (false),
m_scanOut (false) m_scanOut (false)
{ {
contextCount++; contextCount++;
if (initDrm() < 0)
return;
setDrmMode();
// Get the initialized EGL display // Get the initialized EGL display
m_display = getInitializedDisplay(); m_display = getInitializedDisplay();
// Get the best EGL config matching the requested video settings // Get the best EGL config matching the requested video settings
m_config = getBestConfig(m_display, VideoMode::getDesktopMode().bitsPerPixel, settings); m_config = getBestConfig(m_display, settings);
updateSettings(); updateSettings();
// Create EGL context // Create EGL context
createContext(shared); createContext(shared);
createSurface(width, height, VideoMode::getDesktopMode().bitsPerPixel, false); createSurface(drmNode.mode->hdisplay, drmNode.mode->vdisplay, false);
} }
@ -751,19 +757,23 @@ void DRMContext::createContext(DRMContext* shared)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void DRMContext::createSurface(unsigned int width, unsigned int height, unsigned int /*bpp*/, bool scanout) void DRMContext::createSurface(unsigned int width, unsigned int height, bool scanout)
{ {
sf::Uint32 flags = GBM_BO_USE_RENDERING; sf::Uint32 flags = GBM_BO_USE_RENDERING;
uint32_t fmt = GBM_FORMAT_ARGB8888;
m_scanOut = scanout; m_scanOut = scanout;
if (m_scanOut) if (m_scanOut)
flags |= GBM_BO_USE_SCANOUT; flags |= GBM_BO_USE_SCANOUT;
if (!gbm_device_is_format_supported(gbmDevice, fmt, flags))
err() << "Warning: GBM surface format not supported." << std::endl;
m_gbmSurface = gbm_surface_create( m_gbmSurface = gbm_surface_create(
gbmDevice, gbmDevice,
width, width,
height, height,
GBM_FORMAT_ARGB8888, fmt,
flags); flags);
if (!m_gbmSurface) if (!m_gbmSurface)
@ -772,9 +782,6 @@ void DRMContext::createSurface(unsigned int width, unsigned int height, unsigned
return; return;
} }
m_width = width;
m_height = height;
eglCheck(m_surface = eglCreateWindowSurface(m_display, m_config, reinterpret_cast<EGLNativeWindowType>(m_gbmSurface), NULL)); eglCheck(m_surface = eglCreateWindowSurface(m_display, m_config, reinterpret_cast<EGLNativeWindowType>(m_gbmSurface), NULL));
if (m_surface == EGL_NO_SURFACE) if (m_surface == EGL_NO_SURFACE)
@ -799,12 +806,11 @@ void DRMContext::destroySurface()
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
EGLConfig DRMContext::getBestConfig(EGLDisplay display, unsigned int bitsPerPixel, const ContextSettings& settings) EGLConfig DRMContext::getBestConfig(EGLDisplay display, const ContextSettings& settings)
{ {
// Set our video settings constraint // Set our video settings constraint
const EGLint attributes[] = const EGLint attributes[] =
{ {
EGL_BUFFER_SIZE, static_cast<EGLint>(bitsPerPixel),
EGL_DEPTH_SIZE, static_cast<EGLint>(settings.depthBits), EGL_DEPTH_SIZE, static_cast<EGLint>(settings.depthBits),
EGL_STENCIL_SIZE, static_cast<EGLint>(settings.stencilBits), EGL_STENCIL_SIZE, static_cast<EGLint>(settings.stencilBits),
EGL_SAMPLE_BUFFERS, static_cast<EGLint>(settings.antialiasingLevel), EGL_SAMPLE_BUFFERS, static_cast<EGLint>(settings.antialiasingLevel),
@ -863,7 +869,7 @@ GlFunctionPointer DRMContext::getFunction(const char* name)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
Drm& DRMContext::getDRM() Drm& DRMContext::getDRM()
{ {
checkInit(); initDrm();
return drmNode; return drmNode;
} }

View File

@ -143,11 +143,10 @@ public:
/// ///
/// \param width Back buffer width, in pixels /// \param width Back buffer width, in pixels
/// \param height Back buffer height, in pixels /// \param height Back buffer height, in pixels
/// \param bpp Pixel depth, in bits per pixel
/// \param scanout True to present the surface to the screen /// \param scanout True to present the surface to the screen
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void createSurface(unsigned int width, unsigned int height, unsigned int bpp, bool scanout); void createSurface(unsigned int width, unsigned int height, bool scanout);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Destroy the EGL surface /// \brief Destroy the EGL surface
@ -162,13 +161,12 @@ public:
/// \brief Get the best EGL visual for a given set of video settings /// \brief Get the best EGL visual for a given set of video settings
/// ///
/// \param display EGL display /// \param display EGL display
/// \param bitsPerPixel Pixel depth, in bits per pixel
/// \param settings Requested context settings /// \param settings Requested context settings
/// ///
/// \return The best EGL config /// \return The best EGL config
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
static EGLConfig getBestConfig(EGLDisplay display, unsigned int bitsPerPixel, const ContextSettings& settings); static EGLConfig getBestConfig(EGLDisplay display, const ContextSettings& settings);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Get the address of an OpenGL function /// \brief Get the address of an OpenGL function
@ -210,8 +208,6 @@ private:
gbm_bo* m_currentBO; gbm_bo* m_currentBO;
gbm_bo* m_nextBO; gbm_bo* m_nextBO;
gbm_surface* m_gbmSurface; gbm_surface* m_gbmSurface;
unsigned int m_width;
unsigned int m_height;
bool m_shown; bool m_shown;
bool m_scanOut; bool m_scanOut;
}; };

View File

@ -61,6 +61,9 @@ VideoMode VideoModeImpl::getDesktopMode()
{ {
Drm& drm = sf::priv::DRMContext::getDRM(); Drm& drm = sf::priv::DRMContext::getDRM();
drmModeModeInfoPtr ptr = drm.mode; drmModeModeInfoPtr ptr = drm.mode;
if (!ptr) // if no mode has been set, return the original mode we started with
ptr = &drm.originalCrtc->mode;
if (ptr) if (ptr)
return VideoMode(ptr->hdisplay, ptr->vdisplay); return VideoMode(ptr->hdisplay, ptr->vdisplay);
else else