You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
513 lines
21 KiB
513 lines
21 KiB
/* |
|
KWin - the KDE window manager |
|
This file is part of the KDE project. |
|
|
|
SPDX-FileCopyrightText: 2022 Xaver Hugl <xaver.hugl@gmail.com> |
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later |
|
*/ |
|
#include "drm_egl_layer_surface.h" |
|
|
|
#include "config-kwin.h" |
|
#include "core/graphicsbufferview.h" |
|
#include "drm_egl_backend.h" |
|
#include "drm_gpu.h" |
|
#include "drm_logging.h" |
|
#include "platformsupport/scenes/opengl/eglnativefence.h" |
|
#include "platformsupport/scenes/opengl/eglswapchain.h" |
|
#include "platformsupport/scenes/opengl/glrendertimequery.h" |
|
#include "platformsupport/scenes/qpainter/qpainterswapchain.h" |
|
#include "utils/drm_format_helper.h" |
|
|
|
#include <drm_fourcc.h> |
|
#include <errno.h> |
|
#include <gbm.h> |
|
#include <unistd.h> |
|
|
|
namespace KWin |
|
{ |
|
|
|
static const QVector<uint64_t> linearModifier = {DRM_FORMAT_MOD_LINEAR}; |
|
static const QVector<uint64_t> implicitModifier = {DRM_FORMAT_MOD_INVALID}; |
|
static const QVector<uint32_t> cpuCopyFormats = {DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB8888}; |
|
|
|
static const bool bufferAgeEnabled = qEnvironmentVariable("KWIN_USE_BUFFER_AGE") != QStringLiteral("0"); |
|
|
|
static gbm_format_name_desc formatName(uint32_t format) |
|
{ |
|
gbm_format_name_desc ret; |
|
gbm_format_get_name(format, &ret); |
|
return ret; |
|
} |
|
|
|
EglGbmLayerSurface::EglGbmLayerSurface(DrmGpu *gpu, EglGbmBackend *eglBackend, BufferTarget target, FormatOption formatOption) |
|
: m_gpu(gpu) |
|
, m_eglBackend(eglBackend) |
|
, m_bufferTarget(target) |
|
, m_formatOption(formatOption) |
|
{ |
|
} |
|
|
|
EglGbmLayerSurface::~EglGbmLayerSurface() |
|
{ |
|
destroyResources(); |
|
} |
|
|
|
void EglGbmLayerSurface::destroyResources() |
|
{ |
|
m_eglBackend->makeCurrent(); |
|
m_surface = {}; |
|
m_oldSurface = {}; |
|
} |
|
|
|
std::optional<OutputLayerBeginFrameInfo> EglGbmLayerSurface::startRendering(const QSize &bufferSize, TextureTransforms transformation, const QMap<uint32_t, QVector<uint64_t>> &formats, const ColorDescription &colorDescription, const QVector3D &channelFactors, bool enableColormanagement) |
|
{ |
|
if (!checkSurface(bufferSize, formats)) { |
|
return std::nullopt; |
|
} |
|
|
|
if (!m_eglBackend->contextObject()->makeCurrent()) { |
|
return std::nullopt; |
|
} |
|
|
|
auto slot = m_surface.gbmSwapchain->acquire(); |
|
if (!slot) { |
|
return std::nullopt; |
|
} |
|
|
|
slot->framebuffer()->colorAttachment()->setContentTransform(transformation); |
|
m_surface.currentSlot = slot; |
|
|
|
if (m_surface.targetColorDescription != colorDescription || m_surface.channelFactors != channelFactors || m_surface.colormanagementEnabled != enableColormanagement) { |
|
m_surface.damageJournal.clear(); |
|
m_surface.colormanagementEnabled = enableColormanagement; |
|
m_surface.targetColorDescription = colorDescription; |
|
m_surface.channelFactors = channelFactors; |
|
if (enableColormanagement) { |
|
m_surface.intermediaryColorDescription = ColorDescription(colorDescription.colorimetry(), NamedTransferFunction::linear, |
|
colorDescription.sdrBrightness(), colorDescription.minHdrBrightness(), |
|
colorDescription.maxHdrBrightness(), colorDescription.maxHdrHighlightBrightness()); |
|
} else { |
|
m_surface.intermediaryColorDescription = colorDescription; |
|
} |
|
} |
|
|
|
const QRegion repaint = bufferAgeEnabled ? m_surface.damageJournal.accumulate(slot->age(), infiniteRegion()) : infiniteRegion(); |
|
if (enableColormanagement) { |
|
if (!m_surface.shadowBuffer || m_surface.shadowTexture->size() != m_surface.gbmSwapchain->size()) { |
|
m_surface.shadowTexture = GLTexture::allocate(GL_RGBA16F, m_surface.gbmSwapchain->size()); |
|
if (!m_surface.shadowTexture) { |
|
return std::nullopt; |
|
} |
|
m_surface.shadowBuffer = std::make_shared<GLFramebuffer>(m_surface.shadowTexture.get()); |
|
} |
|
m_surface.renderStart = std::chrono::steady_clock::now(); |
|
m_surface.timeQuery->begin(); |
|
return OutputLayerBeginFrameInfo{ |
|
.renderTarget = RenderTarget(m_surface.shadowBuffer.get(), m_surface.intermediaryColorDescription), |
|
.repaint = repaint, |
|
}; |
|
} else { |
|
m_surface.shadowTexture.reset(); |
|
m_surface.shadowBuffer.reset(); |
|
m_surface.renderStart = std::chrono::steady_clock::now(); |
|
m_surface.timeQuery->begin(); |
|
return OutputLayerBeginFrameInfo{ |
|
.renderTarget = RenderTarget(m_surface.currentSlot->framebuffer()), |
|
.repaint = repaint, |
|
}; |
|
} |
|
} |
|
|
|
bool EglGbmLayerSurface::endRendering(const QRegion &damagedRegion) |
|
{ |
|
if (m_surface.colormanagementEnabled) { |
|
GLFramebuffer *fbo = m_surface.currentSlot->framebuffer(); |
|
GLTexture *texture = fbo->colorAttachment(); |
|
GLFramebuffer::pushFramebuffer(fbo); |
|
ShaderBinder binder(ShaderTrait::MapTexture | ShaderTrait::TransformColorspace); |
|
QMatrix4x4 mat = texture->contentTransformMatrix(); |
|
mat.ortho(QRectF(QPointF(), fbo->size())); |
|
binder.shader()->setUniform(GLShader::MatrixUniform::ModelViewProjectionMatrix, mat); |
|
QMatrix3x3 ctm; |
|
ctm(0, 0) = m_surface.channelFactors.x(); |
|
ctm(1, 1) = m_surface.channelFactors.y(); |
|
ctm(2, 2) = m_surface.channelFactors.z(); |
|
binder.shader()->setUniform(GLShader::MatrixUniform::ColorimetryTransformation, ctm); |
|
binder.shader()->setUniform(GLShader::IntUniform::SourceNamedTransferFunction, int(m_surface.intermediaryColorDescription.transferFunction())); |
|
binder.shader()->setUniform(GLShader::IntUniform::DestinationNamedTransferFunction, int(m_surface.targetColorDescription.transferFunction())); |
|
m_surface.shadowTexture->render(m_surface.gbmSwapchain->size(), 1); |
|
GLFramebuffer::popFramebuffer(); |
|
} |
|
m_surface.damageJournal.add(damagedRegion); |
|
m_surface.gbmSwapchain->release(m_surface.currentSlot); |
|
m_surface.timeQuery->end(); |
|
glFlush(); |
|
const auto buffer = importBuffer(m_surface, m_surface.currentSlot.get()); |
|
m_surface.renderEnd = std::chrono::steady_clock::now(); |
|
if (buffer) { |
|
m_surface.currentFramebuffer = buffer; |
|
return true; |
|
} else { |
|
return false; |
|
} |
|
} |
|
|
|
std::chrono::nanoseconds EglGbmLayerSurface::queryRenderTime() const |
|
{ |
|
const auto cpuTime = m_surface.renderEnd - m_surface.renderStart; |
|
if (m_surface.timeQuery) { |
|
m_eglBackend->makeCurrent(); |
|
auto gpuTime = m_surface.timeQuery->result(); |
|
if (m_surface.importTimeQuery && m_eglBackend->contextForGpu(m_gpu)->makeCurrent()) { |
|
gpuTime += m_surface.importTimeQuery->result(); |
|
} |
|
return std::max(gpuTime, cpuTime); |
|
} else { |
|
return cpuTime; |
|
} |
|
} |
|
|
|
EglGbmBackend *EglGbmLayerSurface::eglBackend() const |
|
{ |
|
return m_eglBackend; |
|
} |
|
|
|
std::shared_ptr<DrmFramebuffer> EglGbmLayerSurface::currentBuffer() const |
|
{ |
|
return m_surface.currentFramebuffer; |
|
} |
|
|
|
const ColorDescription &EglGbmLayerSurface::colorDescription() const |
|
{ |
|
return m_surface.shadowTexture ? m_surface.intermediaryColorDescription : m_surface.targetColorDescription; |
|
} |
|
|
|
bool EglGbmLayerSurface::doesSurfaceFit(const QSize &size, const QMap<uint32_t, QVector<uint64_t>> &formats) const |
|
{ |
|
return doesSurfaceFit(m_surface, size, formats); |
|
} |
|
|
|
std::shared_ptr<GLTexture> EglGbmLayerSurface::texture() const |
|
{ |
|
return m_surface.shadowTexture ? m_surface.shadowTexture : m_surface.currentSlot->texture(); |
|
} |
|
|
|
std::shared_ptr<DrmFramebuffer> EglGbmLayerSurface::renderTestBuffer(const QSize &bufferSize, const QMap<uint32_t, QVector<uint64_t>> &formats) |
|
{ |
|
if (checkSurface(bufferSize, formats)) { |
|
return m_surface.currentFramebuffer; |
|
} else { |
|
return nullptr; |
|
} |
|
} |
|
|
|
bool EglGbmLayerSurface::checkSurface(const QSize &size, const QMap<uint32_t, QVector<uint64_t>> &formats) |
|
{ |
|
if (doesSurfaceFit(m_surface, size, formats)) { |
|
return true; |
|
} |
|
if (doesSurfaceFit(m_oldSurface, size, formats)) { |
|
m_surface = m_oldSurface; |
|
return true; |
|
} |
|
if (const auto newSurface = createSurface(size, formats)) { |
|
m_oldSurface = m_surface; |
|
m_surface = newSurface.value(); |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
bool EglGbmLayerSurface::doesSurfaceFit(const Surface &surface, const QSize &size, const QMap<uint32_t, QVector<uint64_t>> &formats) const |
|
{ |
|
const auto &swapchain = surface.gbmSwapchain; |
|
return swapchain |
|
&& swapchain->size() == size |
|
&& formats.contains(swapchain->format()) |
|
&& (surface.forceLinear || swapchain->modifier() == DRM_FORMAT_MOD_INVALID || formats[swapchain->format()].contains(swapchain->modifier())); |
|
} |
|
|
|
std::optional<EglGbmLayerSurface::Surface> EglGbmLayerSurface::createSurface(const QSize &size, const QMap<uint32_t, QVector<uint64_t>> &formats) const |
|
{ |
|
QVector<FormatInfo> preferredFormats; |
|
QVector<FormatInfo> fallbackFormats; |
|
for (auto it = formats.begin(); it != formats.end(); it++) { |
|
const auto format = formatInfo(it.key()); |
|
if (format.has_value() && format->bitsPerColor >= 8) { |
|
if (format->bitsPerPixel <= 32) { |
|
preferredFormats.push_back(format.value()); |
|
} else { |
|
fallbackFormats.push_back(format.value()); |
|
} |
|
} |
|
} |
|
const auto sort = [this](const auto &lhs, const auto &rhs) { |
|
if (lhs.drmFormat == rhs.drmFormat) { |
|
// prefer having an alpha channel |
|
return lhs.alphaBits > rhs.alphaBits; |
|
} else if (m_eglBackend->prefer10bpc() && ((lhs.bitsPerColor == 10) != (rhs.bitsPerColor == 10))) { |
|
// prefer 10bpc / 30bpp formats |
|
return lhs.bitsPerColor == 10; |
|
} else { |
|
// fallback: prefer formats with lower bandwidth requirements |
|
return lhs.bitsPerPixel < rhs.bitsPerPixel; |
|
} |
|
}; |
|
const auto doTestFormats = [this, &size, &formats](const QVector<FormatInfo> &gbmFormats, MultiGpuImportMode importMode) -> std::optional<Surface> { |
|
for (const auto &format : gbmFormats) { |
|
if (m_formatOption == FormatOption::RequireAlpha && format.alphaBits == 0) { |
|
continue; |
|
} |
|
const auto surface = createSurface(size, format.drmFormat, formats[format.drmFormat], importMode); |
|
if (surface.has_value()) { |
|
return surface; |
|
} |
|
} |
|
return std::nullopt; |
|
}; |
|
const auto testFormats = [this, &sort, &doTestFormats](QVector<FormatInfo> &formats) -> std::optional<Surface> { |
|
std::sort(formats.begin(), formats.end(), sort); |
|
if (m_gpu == m_eglBackend->gpu()) { |
|
return doTestFormats(formats, MultiGpuImportMode::None); |
|
} |
|
if (const auto surface = doTestFormats(formats, MultiGpuImportMode::Egl)) { |
|
qCDebug(KWIN_DRM) << "chose egl import with format" << formatName(surface->gbmSwapchain->format()).name << "and modifier" << surface->gbmSwapchain->modifier(); |
|
return surface; |
|
} |
|
if (const auto surface = doTestFormats(formats, MultiGpuImportMode::Dmabuf)) { |
|
qCDebug(KWIN_DRM) << "chose dmabuf import with format" << formatName(surface->gbmSwapchain->format()).name << "and modifier" << surface->gbmSwapchain->modifier(); |
|
return surface; |
|
} |
|
if (const auto surface = doTestFormats(formats, MultiGpuImportMode::LinearDmabuf)) { |
|
qCDebug(KWIN_DRM) << "chose linear dmabuf import with format" << formatName(surface->gbmSwapchain->format()).name << "and modifier" << surface->gbmSwapchain->modifier(); |
|
return surface; |
|
} |
|
if (const auto surface = doTestFormats(formats, MultiGpuImportMode::DumbBuffer)) { |
|
qCDebug(KWIN_DRM) << "chose cpu import with format" << formatName(surface->gbmSwapchain->format()).name << "and modifier" << surface->gbmSwapchain->modifier(); |
|
return surface; |
|
} |
|
return std::nullopt; |
|
}; |
|
if (const auto ret = testFormats(preferredFormats)) { |
|
return ret; |
|
} else if (const auto ret = testFormats(fallbackFormats)) { |
|
return ret; |
|
} else { |
|
return std::nullopt; |
|
} |
|
} |
|
|
|
static QVector<uint64_t> filterModifiers(const QVector<uint64_t> &one, const QVector<uint64_t> &two) |
|
{ |
|
QVector<uint64_t> ret = one; |
|
ret.erase(std::remove_if(ret.begin(), ret.end(), [&two](uint64_t mod) { |
|
return !two.contains(mod); |
|
}), |
|
ret.end()); |
|
return ret; |
|
} |
|
|
|
std::optional<EglGbmLayerSurface::Surface> EglGbmLayerSurface::createSurface(const QSize &size, uint32_t format, const QVector<uint64_t> &modifiers, MultiGpuImportMode importMode) const |
|
{ |
|
const bool cpuCopy = importMode == MultiGpuImportMode::DumbBuffer || m_bufferTarget == BufferTarget::Dumb; |
|
QVector<uint64_t> renderModifiers; |
|
if (importMode == MultiGpuImportMode::Egl) { |
|
const auto context = m_eglBackend->contextForGpu(m_gpu); |
|
if (!context || context->isSoftwareRenderer()) { |
|
return std::nullopt; |
|
} |
|
renderModifiers = filterModifiers(context->displayObject()->allSupportedDrmFormats()[format], |
|
m_eglBackend->eglDisplayObject()->supportedDrmFormats().value(format)); |
|
} else if (cpuCopy) { |
|
if (!cpuCopyFormats.contains(format)) { |
|
return std::nullopt; |
|
} |
|
renderModifiers = m_eglBackend->eglDisplayObject()->supportedDrmFormats().value(format); |
|
} else { |
|
renderModifiers = filterModifiers(modifiers, m_eglBackend->eglDisplayObject()->supportedDrmFormats().value(format)); |
|
} |
|
if (renderModifiers.empty()) { |
|
return std::nullopt; |
|
} |
|
Surface ret; |
|
ret.importMode = importMode; |
|
ret.forceLinear = importMode == MultiGpuImportMode::DumbBuffer || importMode == MultiGpuImportMode::LinearDmabuf || m_bufferTarget != BufferTarget::Normal; |
|
ret.gbmSwapchain = createGbmSwapchain(m_eglBackend->gpu(), m_eglBackend->contextObject(), size, format, renderModifiers, ret.forceLinear); |
|
if (!ret.gbmSwapchain) { |
|
return std::nullopt; |
|
} |
|
if (cpuCopy) { |
|
ret.importDumbSwapchain = std::make_shared<QPainterSwapchain>(m_gpu->graphicsBufferAllocator(), size, format); |
|
} else if (importMode == MultiGpuImportMode::Egl) { |
|
ret.importGbmSwapchain = createGbmSwapchain(m_gpu, m_eglBackend->contextForGpu(m_gpu), size, format, modifiers, false); |
|
if (!ret.importGbmSwapchain) { |
|
return std::nullopt; |
|
} |
|
ret.importTimeQuery = std::make_shared<GLRenderTimeQuery>(); |
|
} |
|
ret.timeQuery = std::make_shared<GLRenderTimeQuery>(); |
|
if (!doRenderTestBuffer(ret)) { |
|
return std::nullopt; |
|
} |
|
return ret; |
|
} |
|
|
|
std::shared_ptr<EglSwapchain> EglGbmLayerSurface::createGbmSwapchain(DrmGpu *gpu, EglContext *context, const QSize &size, uint32_t format, const QVector<uint64_t> &modifiers, bool preferLinear) const |
|
{ |
|
static bool modifiersEnvSet = false; |
|
static const bool modifiersEnv = qEnvironmentVariableIntValue("KWIN_DRM_USE_MODIFIERS", &modifiersEnvSet) != 0; |
|
bool allowModifiers = gpu->addFB2ModifiersSupported() && (!modifiersEnvSet || (modifiersEnvSet && modifiersEnv)) && modifiers != implicitModifier; |
|
#if !HAVE_GBM_BO_GET_FD_FOR_PLANE |
|
allowModifiers &= m_gpu == gpu; |
|
#endif |
|
const bool linearSupported = modifiers.contains(DRM_FORMAT_MOD_LINEAR); |
|
const bool forceLinear = m_gpu != gpu && !allowModifiers; |
|
if (forceLinear && !linearSupported) { |
|
return nullptr; |
|
} |
|
if (linearSupported && (preferLinear || forceLinear)) { |
|
if (const auto swapchain = EglSwapchain::create(gpu->graphicsBufferAllocator(), context, size, format, linearModifier)) { |
|
return swapchain; |
|
} else if (forceLinear) { |
|
return nullptr; |
|
} |
|
} |
|
|
|
if (allowModifiers) { |
|
if (auto swapchain = EglSwapchain::create(gpu->graphicsBufferAllocator(), context, size, format, modifiers)) { |
|
return swapchain; |
|
} |
|
} |
|
|
|
return EglSwapchain::create(gpu->graphicsBufferAllocator(), context, size, format, implicitModifier); |
|
} |
|
|
|
std::shared_ptr<DrmFramebuffer> EglGbmLayerSurface::doRenderTestBuffer(Surface &surface) const |
|
{ |
|
auto slot = surface.gbmSwapchain->acquire(); |
|
if (!slot) { |
|
return nullptr; |
|
} |
|
if (const auto ret = importBuffer(surface, slot.get())) { |
|
surface.currentSlot = slot; |
|
surface.currentFramebuffer = ret; |
|
return ret; |
|
} else { |
|
return nullptr; |
|
} |
|
} |
|
|
|
std::shared_ptr<DrmFramebuffer> EglGbmLayerSurface::importBuffer(Surface &surface, EglSwapchainSlot *slot) const |
|
{ |
|
if (m_bufferTarget == BufferTarget::Dumb || surface.importMode == MultiGpuImportMode::DumbBuffer) { |
|
return importWithCpu(surface, slot); |
|
} else if (surface.importMode == MultiGpuImportMode::Egl) { |
|
return importWithEgl(surface, slot->buffer()); |
|
} else { |
|
const auto ret = m_gpu->importBuffer(slot->buffer()); |
|
if (!ret) { |
|
qCWarning(KWIN_DRM, "Failed to create framebuffer: %s", strerror(errno)); |
|
} |
|
return ret; |
|
} |
|
} |
|
|
|
std::shared_ptr<DrmFramebuffer> EglGbmLayerSurface::importWithEgl(Surface &surface, GraphicsBuffer *sourceBuffer) const |
|
{ |
|
Q_ASSERT(surface.importGbmSwapchain); |
|
|
|
EGLNativeFence sourceFence(m_eglBackend->eglDisplayObject()); |
|
|
|
const auto display = m_eglBackend->displayForGpu(m_gpu); |
|
// the NVidia proprietary driver supports neither implicit sync nor EGL_ANDROID_native_fence_sync |
|
if (!sourceFence.isValid() || !display->supportsNativeFence()) { |
|
glFinish(); |
|
} |
|
const auto context = m_eglBackend->contextForGpu(m_gpu); |
|
if (!context || context->isSoftwareRenderer() || !context->makeCurrent()) { |
|
return nullptr; |
|
} |
|
surface.importTimeQuery->begin(); |
|
|
|
if (sourceFence.isValid()) { |
|
const auto destinationFence = EGLNativeFence::importFence(context->displayObject(), sourceFence.fileDescriptor().duplicate()); |
|
destinationFence.waitSync(); |
|
} |
|
|
|
auto &sourceTexture = surface.importedTextureCache[sourceBuffer]; |
|
if (!sourceTexture) { |
|
sourceTexture = context->importDmaBufAsTexture(*sourceBuffer->dmabufAttributes()); |
|
} |
|
if (!sourceTexture) { |
|
qCWarning(KWIN_DRM, "failed to import the source texture!"); |
|
return nullptr; |
|
} |
|
auto slot = surface.importGbmSwapchain->acquire(); |
|
if (!slot) { |
|
qCWarning(KWIN_DRM, "failed to import the local texture!"); |
|
return nullptr; |
|
} |
|
|
|
GLFramebuffer *fbo = slot->framebuffer(); |
|
glBindFramebuffer(GL_FRAMEBUFFER, fbo->handle()); |
|
glViewport(0, 0, fbo->size().width(), fbo->size().height()); |
|
|
|
const auto shader = context->shaderManager()->pushShader(sourceTexture->target() == GL_TEXTURE_EXTERNAL_OES ? ShaderTrait::MapExternalTexture : ShaderTrait::MapTexture); |
|
QMatrix4x4 mat; |
|
mat.scale(1, -1); |
|
mat.ortho(QRect(QPoint(), fbo->size())); |
|
shader->setUniform(GLShader::ModelViewProjectionMatrix, mat); |
|
|
|
sourceTexture->bind(); |
|
sourceTexture->render(fbo->size(), 1); |
|
sourceTexture->unbind(); |
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0); |
|
|
|
context->shaderManager()->popShader(); |
|
glFlush(); |
|
surface.importGbmSwapchain->release(slot); |
|
surface.importTimeQuery->end(); |
|
|
|
// restore the old context |
|
m_eglBackend->makeCurrent(); |
|
return m_gpu->importBuffer(slot->buffer()); |
|
} |
|
|
|
std::shared_ptr<DrmFramebuffer> EglGbmLayerSurface::importWithCpu(Surface &surface, EglSwapchainSlot *source) const |
|
{ |
|
Q_ASSERT(surface.importDumbSwapchain); |
|
const auto slot = surface.importDumbSwapchain->acquire(); |
|
if (!slot) { |
|
qCWarning(KWIN_DRM) << "EglGbmLayerSurface::importWithCpu: failed to get a target dumb buffer"; |
|
return nullptr; |
|
} |
|
const auto size = source->buffer()->size(); |
|
const qsizetype srcStride = 4 * size.width(); |
|
GLFramebuffer::pushFramebuffer(source->framebuffer()); |
|
QImage *const dst = slot->view()->image(); |
|
if (dst->bytesPerLine() == srcStride) { |
|
glReadPixels(0, 0, dst->width(), dst->height(), GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, dst->bits()); |
|
} else { |
|
// there's padding, need to copy line by line |
|
if (surface.cpuCopyCache.size() != dst->size()) { |
|
surface.cpuCopyCache = QImage(dst->size(), QImage::Format_RGBA8888); |
|
} |
|
glReadPixels(0, 0, dst->width(), dst->height(), GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, surface.cpuCopyCache.bits()); |
|
for (int i = 0; i < dst->height(); i++) { |
|
std::memcpy(dst->scanLine(i), surface.cpuCopyCache.scanLine(i), srcStride); |
|
} |
|
} |
|
GLFramebuffer::popFramebuffer(); |
|
|
|
const auto ret = m_gpu->importBuffer(slot->buffer()); |
|
if (!ret) { |
|
qCWarning(KWIN_DRM, "Failed to create a framebuffer: %s", strerror(errno)); |
|
} |
|
surface.importDumbSwapchain->release(slot); |
|
return ret; |
|
} |
|
} |
|
|
|
#include "moc_drm_egl_layer_surface.cpp"
|
|
|