11#include "config-kwin.h"
26#include <drm_fourcc.h>
34static const QList<uint64_t> linearModifier = {DRM_FORMAT_MOD_LINEAR};
35static const QList<uint64_t> implicitModifier = {DRM_FORMAT_MOD_INVALID};
36static const QList<uint32_t> cpuCopyFormats = {DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB8888};
38static const bool bufferAgeEnabled = qEnvironmentVariable(
"KWIN_USE_BUFFER_AGE") != QStringLiteral(
"0");
40static gbm_format_name_desc formatName(uint32_t
format)
42 gbm_format_name_desc ret;
43 gbm_format_get_name(
format, &ret);
49 , m_eglBackend(eglBackend)
50 , m_requestedBufferTarget(target)
51 , m_formatOption(formatOption)
57EglGbmLayerSurface::Surface::~Surface()
60 importContext->makeCurrent();
61 importGbmSwapchain.reset();
62 importedTextureCache.clear();
63 importContext.reset();
66 context->makeCurrent();
78 if (!checkSurface(bufferSize, formats)) {
86 auto slot = m_surface->gbmSwapchain->acquire();
91 if (slot->framebuffer()->colorAttachment()->contentTransform() != transformation) {
92 m_surface->damageJournal.clear();
94 slot->framebuffer()->colorAttachment()->setContentTransform(transformation);
95 m_surface->currentSlot = slot;
97 if (m_surface->targetColorDescription !=
colorDescription || m_surface->channelFactors != channelFactors
98 || m_surface->colormanagementEnabled != enableColormanagement || m_surface->iccProfile != iccProfile) {
99 m_surface->damageJournal.clear();
100 m_surface->colormanagementEnabled = enableColormanagement;
102 m_surface->channelFactors = channelFactors;
103 m_surface->iccProfile = iccProfile;
105 if (!m_surface->iccShader) {
106 m_surface->iccShader = std::make_unique<IccShader>();
109 m_surface->iccShader.reset();
111 if (enableColormanagement) {
122 if (enableColormanagement) {
123 if (!m_surface->shadowBuffer || m_surface->shadowTexture->size() != m_surface->gbmSwapchain->size()) {
124 m_surface->shadowTexture =
GLTexture::allocate(GL_RGBA16F, m_surface->gbmSwapchain->size());
125 if (!m_surface->shadowTexture) {
128 m_surface->shadowBuffer = std::make_unique<GLFramebuffer>(m_surface->shadowTexture.get());
130 m_surface->shadowTexture->setContentTransform(m_surface->currentSlot->framebuffer()->colorAttachment()->contentTransform());
131 m_surface->renderStart = std::chrono::steady_clock::now();
132 m_surface->timeQuery->begin();
138 m_surface->shadowTexture.reset();
139 m_surface->shadowBuffer.reset();
140 m_surface->renderStart = std::chrono::steady_clock::now();
141 m_surface->timeQuery->begin();
151 if (m_surface->colormanagementEnabled) {
155 if (m_surface->iccShader) {
156 m_surface->iccShader->setUniforms(m_surface->iccProfile, m_surface->intermediaryColorDescription.sdrBrightness(), m_surface->channelFactors);
159 ctm(0, 0) = m_surface->channelFactors.x();
160 ctm(1, 1) = m_surface->channelFactors.y();
161 ctm(2, 2) = m_surface->channelFactors.z();
172 mat.ortho(QRectF(QPointF(), fbo->
size()));
175 m_surface->shadowTexture->render(m_surface->gbmSwapchain->size());
178 m_surface->damageJournal.add(damagedRegion);
179 m_surface->gbmSwapchain->release(m_surface->currentSlot);
180 m_surface->timeQuery->end();
188 const auto buffer = importBuffer(m_surface.get(), m_surface->currentSlot.get(), sourceFence.
fileDescriptor());
189 m_surface->renderEnd = std::chrono::steady_clock::now();
191 m_surface->currentFramebuffer = buffer;
201 return std::chrono::nanoseconds::zero();
203 const auto cpuTime = m_surface->renderEnd - m_surface->renderStart;
204 if (m_surface->timeQuery) {
206 auto gpuTime = m_surface->timeQuery->result();
207 if (m_surface->importTimeQuery && m_eglBackend->
contextForGpu(m_gpu)->makeCurrent()) {
208 gpuTime += m_surface->importTimeQuery->result();
210 return std::max(gpuTime, cpuTime);
223 return m_surface ? m_surface->currentFramebuffer :
nullptr;
229 return m_surface->shadowTexture ? m_surface->intermediaryColorDescription : m_surface->targetColorDescription;
243 return m_surface->shadowTexture ? m_surface->shadowTexture : m_surface->currentSlot->texture();
251 if (checkSurface(bufferSize, formats)) {
252 return m_surface->currentFramebuffer;
261 m_surface->damageJournal.clear();
265bool EglGbmLayerSurface::checkSurface(
const QSize &size,
const QMap<uint32_t, QList<uint64_t>> &formats)
271 m_surface = std::move(m_oldSurface);
274 if (
auto newSurface = createSurface(size, formats)) {
275 m_oldSurface = std::move(m_surface);
277 m_oldSurface->damageJournal.clear();
279 m_surface = std::move(newSurface);
287 if (!surface || !surface->gbmSwapchain || surface->gbmSwapchain->size() != size) {
290 switch (surface->importMode) {
291 case MultiGpuImportMode::None:
292 case MultiGpuImportMode::Dmabuf:
293 case MultiGpuImportMode::LinearDmabuf: {
294 const auto format = surface->gbmSwapchain->format();
295 return formats.contains(
format) && (surface->gbmSwapchain->modifier() == DRM_FORMAT_MOD_INVALID || formats[
format].contains(surface->gbmSwapchain->modifier()));
297 case MultiGpuImportMode::DumbBuffer:
298 return formats.contains(surface->importDumbSwapchain->format());
299 case MultiGpuImportMode::Egl: {
300 const auto format = surface->importGbmSwapchain->format();
301 return formats.contains(
format) && (surface->importGbmSwapchain->modifier() == DRM_FORMAT_MOD_INVALID || formats[
format].contains(surface->importGbmSwapchain->modifier()));
307std::unique_ptr<EglGbmLayerSurface::Surface> EglGbmLayerSurface::createSurface(
const QSize &size,
const QMap<uint32_t, QList<uint64_t>> &formats)
const
309 QList<FormatInfo> preferredFormats;
310 QList<FormatInfo> fallbackFormats;
311 for (
auto it = formats.begin(); it != formats.end(); it++) {
314 if (
format->bitsPerPixel <= 32) {
315 preferredFormats.push_back(
format.value());
317 fallbackFormats.push_back(
format.value());
323 auto bufferTarget = m_requestedBufferTarget;
324 if (m_gpu == m_eglBackend->
gpu()) {
325 const auto checkSurfaceNeedsLinear = [&formats](
const FormatInfo &fmt) {
326 const auto &mods = formats[fmt.drmFormat];
327 return std::all_of(mods.cbegin(), mods.cend(), [](
const auto &mod) {
328 return mod == DRM_FORMAT_MOD_LINEAR;
331 const bool needsLinear =
332 std::all_of(preferredFormats.cbegin(), preferredFormats.cend(), checkSurfaceNeedsLinear) && std::all_of(fallbackFormats.cbegin(), fallbackFormats.cend(), checkSurfaceNeedsLinear);
335 const auto checkFormatSupportsLinearRender = [&renderFormats](
const auto &formatInfo) {
336 const auto it = renderFormats.constFind(formatInfo.drmFormat);
337 return it != renderFormats.cend() && it->nonExternalOnlyModifiers.contains(DRM_FORMAT_MOD_LINEAR);
339 const bool noLinearSupport =
340 std::none_of(preferredFormats.cbegin(), preferredFormats.cend(), checkFormatSupportsLinearRender) && std::none_of(fallbackFormats.cbegin(), fallbackFormats.cend(), checkFormatSupportsLinearRender);
341 if (noLinearSupport) {
347 const auto sort = [
this](
const auto &lhs,
const auto &rhs) {
348 if (lhs.drmFormat == rhs.drmFormat) {
350 return lhs.alphaBits > rhs.alphaBits;
351 }
else if (m_eglBackend->
prefer10bpc() && ((lhs.bitsPerColor == 10) != (rhs.bitsPerColor == 10))) {
353 return lhs.bitsPerColor == 10;
356 return lhs.bitsPerPixel < rhs.bitsPerPixel;
359 const auto doTestFormats = [
this, &size, &formats, bufferTarget](
const QList<FormatInfo> &gbmFormats, MultiGpuImportMode importMode) -> std::unique_ptr<Surface> {
360 for (
const auto &
format : gbmFormats) {
364 auto surface = createSurface(size,
format.drmFormat, formats[
format.drmFormat], importMode, bufferTarget);
371 const auto testFormats = [
this, &sort, &doTestFormats](QList<FormatInfo> &formats) -> std::unique_ptr<Surface> {
372 std::sort(formats.begin(), formats.end(), sort);
373 if (m_gpu == m_eglBackend->
gpu()) {
374 return doTestFormats(formats, MultiGpuImportMode::None);
376 if (
auto surface = doTestFormats(formats, MultiGpuImportMode::Egl)) {
377 qCDebug(KWIN_DRM) <<
"chose egl import with format" << formatName(surface->gbmSwapchain->format()).name <<
"and modifier" << surface->gbmSwapchain->modifier();
380 if (
auto surface = doTestFormats(formats, MultiGpuImportMode::Dmabuf)) {
381 qCDebug(KWIN_DRM) <<
"chose dmabuf import with format" << formatName(surface->gbmSwapchain->format()).name <<
"and modifier" << surface->gbmSwapchain->modifier();
384 if (
auto surface = doTestFormats(formats, MultiGpuImportMode::LinearDmabuf)) {
385 qCDebug(KWIN_DRM) <<
"chose linear dmabuf import with format" << formatName(surface->gbmSwapchain->format()).name <<
"and modifier" << surface->gbmSwapchain->modifier();
388 if (
auto surface = doTestFormats(formats, MultiGpuImportMode::DumbBuffer)) {
389 qCDebug(KWIN_DRM) <<
"chose cpu import with format" << formatName(surface->gbmSwapchain->format()).name <<
"and modifier" << surface->gbmSwapchain->modifier();
394 if (
auto ret = testFormats(preferredFormats)) {
396 }
else if (
auto ret = testFormats(fallbackFormats)) {
403static QList<uint64_t> filterModifiers(
const QList<uint64_t> &one,
const QList<uint64_t> &two)
405 QList<uint64_t> ret = one;
406 ret.erase(std::remove_if(ret.begin(), ret.end(), [&two](uint64_t mod) {
407 return !two.contains(mod);
413std::unique_ptr<EglGbmLayerSurface::Surface> EglGbmLayerSurface::createSurface(
const QSize &size, uint32_t
format,
const QList<uint64_t> &modifiers, MultiGpuImportMode importMode,
BufferTarget bufferTarget)
const
415 const bool cpuCopy = importMode == MultiGpuImportMode::DumbBuffer || bufferTarget ==
BufferTarget::Dumb;
416 QList<uint64_t> renderModifiers;
417 auto ret = std::make_unique<Surface>();
419 if (importMode == MultiGpuImportMode::Egl) {
421 if (!ret->importContext || ret->importContext->isSoftwareRenderer()) {
424 const auto importDrmFormat = ret->importContext->displayObject()->allSupportedDrmFormats()[
format];
425 renderModifiers = filterModifiers(importDrmFormat.allModifiers,
426 drmFormat.nonExternalOnlyModifiers);
428 renderModifiers.removeAll(DRM_FORMAT_MOD_INVALID);
429 }
else if (cpuCopy) {
430 if (!cpuCopyFormats.contains(
format)) {
433 renderModifiers = drmFormat.nonExternalOnlyModifiers;
435 renderModifiers = filterModifiers(modifiers, drmFormat.nonExternalOnlyModifiers);
437 if (renderModifiers.empty()) {
441 ret->bufferTarget = bufferTarget;
442 ret->importMode = importMode;
443 ret->gbmSwapchain = createGbmSwapchain(m_eglBackend->
gpu(), m_eglBackend->
contextObject(), size,
format, renderModifiers, importMode, bufferTarget);
444 if (!ret->gbmSwapchain) {
449 }
else if (importMode == MultiGpuImportMode::Egl) {
450 ret->importGbmSwapchain = createGbmSwapchain(m_gpu, ret->importContext.get(), size,
format, modifiers, MultiGpuImportMode::None,
BufferTarget::Normal);
451 if (!ret->importGbmSwapchain) {
454 ret->importTimeQuery = std::make_unique<GLRenderTimeQuery>();
456 ret->timeQuery = std::make_unique<GLRenderTimeQuery>();
457 if (!doRenderTestBuffer(ret.get())) {
463std::shared_ptr<EglSwapchain> EglGbmLayerSurface::createGbmSwapchain(DrmGpu *gpu, EglContext *context,
const QSize &size, uint32_t
format,
const QList<uint64_t> &modifiers, MultiGpuImportMode importMode,
BufferTarget bufferTarget)
const
465 static bool modifiersEnvSet =
false;
466 static const bool modifiersEnv = qEnvironmentVariableIntValue(
"KWIN_DRM_USE_MODIFIERS", &modifiersEnvSet) != 0;
467 bool allowModifiers = (m_gpu->
addFB2ModifiersSupported() || importMode == MultiGpuImportMode::Egl || importMode == MultiGpuImportMode::DumbBuffer) && (!modifiersEnvSet || (modifiersEnvSet && modifiersEnv)) && modifiers != implicitModifier;
468#if !HAVE_GBM_BO_GET_FD_FOR_PLANE
469 allowModifiers &= m_gpu == gpu;
471 const bool linearSupported = modifiers.contains(DRM_FORMAT_MOD_LINEAR);
472 const bool preferLinear = importMode == MultiGpuImportMode::DumbBuffer || bufferTarget ==
BufferTarget::Linear;
473 const bool forceLinear = importMode == MultiGpuImportMode::LinearDmabuf || (importMode != MultiGpuImportMode::None && importMode != MultiGpuImportMode::DumbBuffer && !allowModifiers);
474 if (forceLinear && !linearSupported) {
477 if (linearSupported && (preferLinear || forceLinear)) {
480 }
else if (forceLinear) {
485 if (allowModifiers) {
494std::shared_ptr<DrmFramebuffer> EglGbmLayerSurface::doRenderTestBuffer(Surface *surface)
const
496 auto slot = surface->gbmSwapchain->acquire();
500 if (
const auto ret = importBuffer(surface, slot.get(), FileDescriptor{})) {
501 surface->currentSlot = slot;
502 surface->currentFramebuffer = ret;
509std::shared_ptr<DrmFramebuffer> EglGbmLayerSurface::importBuffer(Surface *surface, EglSwapchainSlot *slot,
const FileDescriptor &readFence)
const
511 if (surface->bufferTarget ==
BufferTarget::Dumb || surface->importMode == MultiGpuImportMode::DumbBuffer) {
512 return importWithCpu(surface, slot);
513 }
else if (surface->importMode == MultiGpuImportMode::Egl) {
514 return importWithEgl(surface, slot->buffer(), readFence);
516 const auto ret = m_gpu->
importBuffer(slot->buffer(), readFence.duplicate());
518 qCWarning(KWIN_DRM,
"Failed to create framebuffer: %s", strerror(errno));
524std::shared_ptr<DrmFramebuffer> EglGbmLayerSurface::importWithEgl(Surface *surface, GraphicsBuffer *sourceBuffer,
const FileDescriptor &readFence)
const
526 Q_ASSERT(surface->importGbmSwapchain);
530 if (!readFence.isValid() || !display->supportsNativeFence()) {
534 if (!surface->importContext->makeCurrent()) {
537 surface->importTimeQuery->begin();
539 if (readFence.isValid()) {
541 destinationFence.waitSync();
544 auto &sourceTexture = surface->importedTextureCache[sourceBuffer];
545 if (!sourceTexture) {
546 sourceTexture = surface->importContext->importDmaBufAsTexture(*sourceBuffer->dmabufAttributes());
548 if (!sourceTexture) {
549 qCWarning(KWIN_DRM,
"failed to import the source texture!");
552 auto slot = surface->importGbmSwapchain->acquire();
554 qCWarning(KWIN_DRM,
"failed to import the local texture!");
558 GLFramebuffer *fbo = slot->framebuffer();
559 glBindFramebuffer(GL_FRAMEBUFFER, fbo->handle());
560 glViewport(0, 0, fbo->size().width(), fbo->size().height());
565 mat.ortho(QRect(QPoint(), fbo->size()));
568 sourceTexture->bind();
569 sourceTexture->render(fbo->size());
570 sourceTexture->unbind();
572 glBindFramebuffer(GL_FRAMEBUFFER, 0);
574 surface->importContext->shaderManager()->popShader();
576 EGLNativeFence endFence(display);
577 if (!endFence.isValid()) {
580 surface->importGbmSwapchain->release(slot);
581 surface->importTimeQuery->end();
585 return m_gpu->
importBuffer(slot->buffer(), endFence.fileDescriptor().duplicate());
588std::shared_ptr<DrmFramebuffer> EglGbmLayerSurface::importWithCpu(Surface *surface, EglSwapchainSlot *source)
const
590 Q_ASSERT(surface->importDumbSwapchain);
591 const auto slot = surface->importDumbSwapchain->acquire();
593 qCWarning(KWIN_DRM) <<
"EglGbmLayerSurface::importWithCpu: failed to get a target dumb buffer";
596 const auto size = source->buffer()->size();
597 const qsizetype srcStride = 4 * size.width();
599 QImage *
const dst = slot->view()->image();
600 if (dst->bytesPerLine() == srcStride) {
601 glReadPixels(0, 0, dst->width(), dst->height(), GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, dst->bits());
604 if (surface->cpuCopyCache.size() != dst->size()) {
605 surface->cpuCopyCache = QImage(dst->size(), QImage::Format_RGBA8888);
607 glReadPixels(0, 0, dst->width(), dst->height(), GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, surface->cpuCopyCache.bits());
608 for (
int i = 0; i < dst->height(); i++) {
609 std::memcpy(dst->scanLine(i), surface->cpuCopyCache.scanLine(i), srcStride);
614 const auto ret = m_gpu->
importBuffer(slot->buffer(), FileDescriptor{});
616 qCWarning(KWIN_DRM,
"Failed to create a framebuffer: %s", strerror(errno));
618 surface->importDumbSwapchain->release(slot);
623#include "moc_drm_egl_layer_surface.cpp"
bool makeCurrent() override
EglDisplay * eglDisplayObject() const
EglContext * contextObject()
double minHdrBrightness() const
static const ColorDescription sRGB
double maxHdrHighlightBrightness() const
const Colorimetry & colorimetry() const
double maxFrameAverageBrightness() const
const Colorimetry & sdrColorimetry() const
double sdrBrightness() const
bool addFB2ModifiersSupported() const
std::shared_ptr< DrmFramebuffer > importBuffer(GraphicsBuffer *buffer, FileDescriptor &&explicitFence)
GraphicsBufferAllocator * graphicsBufferAllocator() const
static EGLNativeFence importFence(EglDisplay *display, FileDescriptor &&fd)
const FileDescriptor & fileDescriptor() const
bool makeCurrent(EGLSurface surface=EGL_NO_SURFACE) const
QHash< uint32_t, DrmFormatInfo > allSupportedDrmFormats() const
OpenGL Backend using Egl on a GBM surface.
EglDisplay * displayForGpu(DrmGpu *gpu)
bool prefer10bpc() const override
std::shared_ptr< EglContext > contextForGpu(DrmGpu *gpu)
std::optional< OutputLayerBeginFrameInfo > startRendering(const QSize &bufferSize, OutputTransform transformation, const QMap< uint32_t, QList< uint64_t > > &formats, const ColorDescription &colorDescription, const QVector3D &channelFactors, const std::shared_ptr< IccProfile > &iccProfile, bool enableColormanagement)
std::shared_ptr< GLTexture > texture() const
std::chrono::nanoseconds queryRenderTime() const
EglGbmBackend * eglBackend() const
EglGbmLayerSurface(DrmGpu *gpu, EglGbmBackend *eglBackend, BufferTarget target=BufferTarget::Normal, FormatOption formatOption=FormatOption::PreferAlpha)
const ColorDescription & colorDescription() const
bool endRendering(const QRegion &damagedRegion)
bool doesSurfaceFit(const QSize &size, const QMap< uint32_t, QList< uint64_t > > &formats) const
std::shared_ptr< DrmFramebuffer > currentBuffer() const
std::shared_ptr< DrmFramebuffer > renderTestBuffer(const QSize &bufferSize, const QMap< uint32_t, QList< uint64_t > > &formats)
static std::shared_ptr< EglSwapchain > create(GraphicsBufferAllocator *allocator, EglContext *context, const QSize &size, uint32_t format, const QList< uint64_t > &modifiers)
OpenGL framebuffer object.
static GLFramebuffer * popFramebuffer()
static void pushFramebuffer(GLFramebuffer *fbo)
GLTexture * colorAttachment() const
@ ModelViewProjectionMatrix
@ ColorimetryTransformation
@ DestinationNamedTransferFunction
@ SourceNamedTransferFunction
bool setUniform(const char *name, float value)
static std::unique_ptr< GLTexture > allocate(GLenum internalFormat, const QSize &size, int levels=1)
OutputTransform contentTransform() const
KWIN_EXPORT QRect infiniteRegion()
RenderTarget renderTarget