mirror of
https://github.com/hyprwm/aquamarine.git
synced 2024-11-17 02:35:59 +01:00
drm: properly blit multigpu surfaces
This commit is contained in:
parent
c3bfe3de89
commit
c4a5fafe76
10 changed files with 842 additions and 37 deletions
|
@ -17,6 +17,7 @@ set(INCLUDE ${CMAKE_INSTALL_FULL_INCLUDEDIR})
|
|||
set(LIBDIR ${CMAKE_INSTALL_FULL_LIBDIR})
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
find_package(OpenGL REQUIRED COMPONENTS "GLES2")
|
||||
find_package(hyprwayland-scanner 0.4.0 REQUIRED)
|
||||
pkg_check_modules(deps REQUIRED IMPORTED_TARGET
|
||||
libseat>=0.8.0 libinput>=1.26.0
|
||||
|
@ -51,7 +52,11 @@ set_target_properties(aquamarine PROPERTIES
|
|||
VERSION ${AQUAMARINE_VERSION}
|
||||
SOVERSION 0
|
||||
)
|
||||
target_link_libraries(aquamarine PkgConfig::deps)
|
||||
target_link_libraries(aquamarine
|
||||
OpenGL::EGL
|
||||
OpenGL::GL
|
||||
PkgConfig::deps
|
||||
)
|
||||
|
||||
check_include_file("sys/timerfd.h" HAS_TIMERFD)
|
||||
pkg_check_modules(epoll IMPORTED_TARGET epoll-shim)
|
||||
|
|
|
@ -124,7 +124,6 @@ namespace Aquamarine {
|
|||
} events;
|
||||
|
||||
Hyprutils::Memory::CSharedPointer<IAllocator> primaryAllocator;
|
||||
std::vector<Hyprutils::Memory::CSharedPointer<IAllocator>> allocators;
|
||||
bool ready = false;
|
||||
Hyprutils::Memory::CSharedPointer<CSession> session;
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ namespace Aquamarine {
|
|||
class CDRMFB;
|
||||
class CDRMOutput;
|
||||
struct SDRMConnector;
|
||||
class CDRMRenderer;
|
||||
|
||||
typedef std::function<void(void)> FIdleCallback;
|
||||
|
||||
|
@ -341,7 +342,6 @@ namespace Aquamarine {
|
|||
|
||||
std::vector<FIdleCallback> idleCallbacks;
|
||||
std::string gpuName;
|
||||
Hyprutils::Memory::CWeakPointer<IAllocator> allocator;
|
||||
|
||||
private:
|
||||
CDRMBackend(Hyprutils::Memory::CSharedPointer<CBackend> backend);
|
||||
|
@ -350,7 +350,9 @@ namespace Aquamarine {
|
|||
bool registerGPU(Hyprutils::Memory::CSharedPointer<CSessionDevice> gpu_, Hyprutils::Memory::CSharedPointer<CDRMBackend> primary_ = {});
|
||||
bool checkFeatures();
|
||||
bool initResources();
|
||||
bool initMgpu();
|
||||
bool grabFormats();
|
||||
bool shouldBlit();
|
||||
void scanConnectors();
|
||||
void scanLeases();
|
||||
void restoreAfterVT();
|
||||
|
@ -360,6 +362,13 @@ namespace Aquamarine {
|
|||
Hyprutils::Memory::CSharedPointer<IDRMImplementation> impl;
|
||||
Hyprutils::Memory::CWeakPointer<CDRMBackend> primary;
|
||||
|
||||
// multigpu state, only present if this backend is not primary, aka if this->primary != nullptr
|
||||
struct {
|
||||
Hyprutils::Memory::CSharedPointer<IAllocator> allocator;
|
||||
Hyprutils::Memory::CSharedPointer<CSwapchain> swapchain;
|
||||
Hyprutils::Memory::CSharedPointer<CDRMRenderer> renderer;
|
||||
} mgpu;
|
||||
|
||||
Hyprutils::Memory::CWeakPointer<CBackend> backend;
|
||||
|
||||
std::vector<Hyprutils::Memory::CSharedPointer<SDRMCRTC>> crtcs;
|
||||
|
|
|
@ -139,9 +139,6 @@ namespace Aquamarine {
|
|||
std::vector<Hyprutils::Memory::CSharedPointer<SOutputMode>> modes;
|
||||
Hyprutils::Memory::CSharedPointer<COutputState> state = Hyprutils::Memory::makeShared<COutputState>();
|
||||
|
||||
// please note that for multigpu setups, this swapchain resides on the target gpu,
|
||||
// so it's recommended that if this swapchain's allocator FD doesn't match the primary
|
||||
// drmFD used, you should render to a buffer on the main gpu and only perform the final copy to this swapchain.
|
||||
Hyprutils::Memory::CSharedPointer<CSwapchain> swapchain;
|
||||
|
||||
//
|
||||
|
|
|
@ -144,13 +144,12 @@ bool Aquamarine::CBackend::start() {
|
|||
// TODO: obviously change this when (if) we add different allocators.
|
||||
for (auto& b : implementations) {
|
||||
if (b->drmFD() >= 0) {
|
||||
allocators.emplace_back(CGBMAllocator::create(b->drmFD(), self));
|
||||
if (!primaryAllocator)
|
||||
primaryAllocator = allocators.front();
|
||||
primaryAllocator = CGBMAllocator::create(b->drmFD(), self);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (allocators.empty() || !primaryAllocator)
|
||||
if (!primaryAllocator)
|
||||
return false;
|
||||
|
||||
ready = true;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include <aquamarine/backend/DRM.hpp>
|
||||
#include <aquamarine/backend/drm/Legacy.hpp>
|
||||
#include <aquamarine/backend/drm/Atomic.hpp>
|
||||
#include <aquamarine/allocator/GBM.hpp>
|
||||
#include <hyprutils/string/VarList.hpp>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
@ -24,6 +25,7 @@ extern "C" {
|
|||
#include "FormatUtils.hpp"
|
||||
#include "Shared.hpp"
|
||||
#include "hwdata.hpp"
|
||||
#include "Renderer.hpp"
|
||||
|
||||
using namespace Aquamarine;
|
||||
using namespace Hyprutils::Memory;
|
||||
|
@ -465,6 +467,38 @@ bool Aquamarine::CDRMBackend::initResources() {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Aquamarine::CDRMBackend::shouldBlit() {
|
||||
return primary;
|
||||
}
|
||||
|
||||
bool Aquamarine::CDRMBackend::initMgpu() {
|
||||
if (!primary)
|
||||
return true;
|
||||
|
||||
mgpu.allocator = CGBMAllocator::create(gpu->fd, backend);
|
||||
|
||||
if (!mgpu.allocator) {
|
||||
backend->log(AQ_LOG_ERROR, "drm: initMgpu: no allocator");
|
||||
return false;
|
||||
}
|
||||
|
||||
mgpu.swapchain = CSwapchain::create(mgpu.allocator, self.lock());
|
||||
|
||||
if (!mgpu.swapchain) {
|
||||
backend->log(AQ_LOG_ERROR, "drm: initMgpu: no swapchain");
|
||||
return false;
|
||||
}
|
||||
|
||||
mgpu.renderer = CDRMRenderer::attempt(gpu->fd, backend.lock());
|
||||
|
||||
if (!mgpu.renderer) {
|
||||
backend->log(AQ_LOG_ERROR, "drm: initMgpu: no renderer");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Aquamarine::CDRMBackend::recheckCRTCs() {
|
||||
if (connectors.empty() || crtcs.empty())
|
||||
return;
|
||||
|
@ -760,27 +794,18 @@ void Aquamarine::CDRMBackend::onReady() {
|
|||
|
||||
backend->log(AQ_LOG_DEBUG, std::format("drm: onReady: connector {} has output name {}", c->id, c->output->name));
|
||||
|
||||
// find our allocator, for multigpu setups there will be 2
|
||||
for (auto& alloc : backend->allocators) {
|
||||
if (alloc->drmFD() != gpu->fd)
|
||||
continue;
|
||||
|
||||
allocator = alloc;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!allocator) {
|
||||
backend->log(AQ_LOG_ERROR, std::format("drm: backend for gpu {} doesn't have an allocator?!", gpu->path));
|
||||
return;
|
||||
}
|
||||
|
||||
// swapchain has to be created here because allocator is absent in connect if not ready
|
||||
c->output->swapchain = CSwapchain::create(allocator.lock(), self.lock());
|
||||
c->output->swapchain = CSwapchain::create(backend->primaryAllocator, self.lock());
|
||||
c->output->swapchain->reconfigure(SSwapchainOptions{.length = 0, .scanout = true, .multigpu = !!primary}); // mark the swapchain for scanout
|
||||
c->output->needsFrame = true;
|
||||
|
||||
backend->events.newOutput.emit(SP<IOutput>(c->output));
|
||||
}
|
||||
|
||||
if (!initMgpu()) {
|
||||
backend->log(AQ_LOG_ERROR, "drm: Failed initializing mgpu");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<SDRMFormat> Aquamarine::CDRMBackend::getRenderFormats() {
|
||||
|
@ -852,7 +877,7 @@ int Aquamarine::CDRMBackend::getNonMasterFD() {
|
|||
}
|
||||
|
||||
SP<IAllocator> Aquamarine::CDRMBackend::preferredAllocator() {
|
||||
return allocator.lock();
|
||||
return backend->primaryAllocator;
|
||||
}
|
||||
|
||||
bool Aquamarine::SDRMPlane::init(drmModePlane* plane) {
|
||||
|
@ -1172,7 +1197,7 @@ void Aquamarine::SDRMConnector::connect(drmModeConnector* connector) {
|
|||
if (!backend->backend->ready)
|
||||
return;
|
||||
|
||||
output->swapchain = CSwapchain::create(backend->allocator.lock(), backend->self.lock());
|
||||
output->swapchain = CSwapchain::create(backend->backend->primaryAllocator, backend->self.lock());
|
||||
backend->backend->events.newOutput.emit(output);
|
||||
output->scheduleFrame(IOutput::AQ_SCHEDULE_NEW_CONNECTOR);
|
||||
}
|
||||
|
@ -1327,10 +1352,26 @@ bool Aquamarine::CDRMOutput::commitState(bool onlyTest) {
|
|||
TRACE(backend->backend->log(AQ_LOG_TRACE, "drm: Committed a buffer, updating state"));
|
||||
|
||||
SP<CDRMFB> drmFB;
|
||||
auto buf = STATE.buffer;
|
||||
bool isNew = false;
|
||||
|
||||
drmFB = CDRMFB::create(buf, backend, &isNew); // will return attachment if present
|
||||
if (backend->shouldBlit()) {
|
||||
TRACE(backend->backend->log(AQ_LOG_TRACE, "drm: Backend requires blit, blitting"));
|
||||
|
||||
auto OPTIONS = swapchain->currentOptions();
|
||||
OPTIONS.multigpu = false; // this is not a shared swapchain, and additionally, don't make it linear, nvidia would be mad
|
||||
if (!backend->mgpu.swapchain->reconfigure(OPTIONS)) {
|
||||
backend->backend->log(AQ_LOG_ERROR, "drm: Backend requires blit, but the mgpu swapchain failed reconfiguring");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto NEWAQBUF = backend->mgpu.swapchain->next(nullptr);
|
||||
if (!backend->mgpu.renderer->blit(STATE.buffer, NEWAQBUF)) {
|
||||
backend->backend->log(AQ_LOG_ERROR, "drm: Backend requires blit, but blit failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
drmFB = CDRMFB::create(NEWAQBUF, backend, nullptr); // will return attachment if present
|
||||
} else
|
||||
drmFB = CDRMFB::create(STATE.buffer, backend, nullptr); // will return attachment if present
|
||||
|
||||
if (!drmFB) {
|
||||
backend->backend->log(AQ_LOG_ERROR, "drm: Buffer failed to import to KMS");
|
||||
|
|
207
src/backend/drm/Math.cpp
Normal file
207
src/backend/drm/Math.cpp
Normal file
|
@ -0,0 +1,207 @@
|
|||
#include "Math.hpp"
|
||||
#include <unordered_map>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
|
||||
void matrixIdentity(float mat[9]) {
|
||||
static const float identity[9] = {
|
||||
1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
|
||||
};
|
||||
memcpy(mat, identity, sizeof(identity));
|
||||
}
|
||||
|
||||
void matrixMultiply(float mat[9], const float a[9], const float b[9]) {
|
||||
float product[9];
|
||||
|
||||
product[0] = a[0] * b[0] + a[1] * b[3] + a[2] * b[6];
|
||||
product[1] = a[0] * b[1] + a[1] * b[4] + a[2] * b[7];
|
||||
product[2] = a[0] * b[2] + a[1] * b[5] + a[2] * b[8];
|
||||
|
||||
product[3] = a[3] * b[0] + a[4] * b[3] + a[5] * b[6];
|
||||
product[4] = a[3] * b[1] + a[4] * b[4] + a[5] * b[7];
|
||||
product[5] = a[3] * b[2] + a[4] * b[5] + a[5] * b[8];
|
||||
|
||||
product[6] = a[6] * b[0] + a[7] * b[3] + a[8] * b[6];
|
||||
product[7] = a[6] * b[1] + a[7] * b[4] + a[8] * b[7];
|
||||
product[8] = a[6] * b[2] + a[7] * b[5] + a[8] * b[8];
|
||||
|
||||
memcpy(mat, product, sizeof(product));
|
||||
}
|
||||
|
||||
void matrixTranspose(float mat[9], const float a[9]) {
|
||||
float transposition[9] = {
|
||||
a[0], a[3], a[6], a[1], a[4], a[7], a[2], a[5], a[8],
|
||||
};
|
||||
memcpy(mat, transposition, sizeof(transposition));
|
||||
}
|
||||
|
||||
void matrixTranslate(float mat[9], float x, float y) {
|
||||
float translate[9] = {
|
||||
1.0f, 0.0f, x, 0.0f, 1.0f, y, 0.0f, 0.0f, 1.0f,
|
||||
};
|
||||
matrixMultiply(mat, mat, translate);
|
||||
}
|
||||
|
||||
void matrixScale(float mat[9], float x, float y) {
|
||||
float scale[9] = {
|
||||
x, 0.0f, 0.0f, 0.0f, y, 0.0f, 0.0f, 0.0f, 1.0f,
|
||||
};
|
||||
matrixMultiply(mat, mat, scale);
|
||||
}
|
||||
|
||||
void matrixRotate(float mat[9], float rad) {
|
||||
float rotate[9] = {
|
||||
(float)cos(rad), (float)-sin(rad), 0.0f, (float)sin(rad), (float)cos(rad), 0.0f, 0.0f, 0.0f, 1.0f,
|
||||
};
|
||||
matrixMultiply(mat, mat, rotate);
|
||||
}
|
||||
|
||||
std::unordered_map<eTransform, std::array<float, 9>> transforms = {
|
||||
{HYPRUTILS_TRANSFORM_NORMAL,
|
||||
{
|
||||
1.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
1.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
1.0f,
|
||||
}},
|
||||
{HYPRUTILS_TRANSFORM_90,
|
||||
{
|
||||
0.0f,
|
||||
1.0f,
|
||||
0.0f,
|
||||
-1.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
1.0f,
|
||||
}},
|
||||
{HYPRUTILS_TRANSFORM_180,
|
||||
{
|
||||
-1.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
-1.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
1.0f,
|
||||
}},
|
||||
{HYPRUTILS_TRANSFORM_270,
|
||||
{
|
||||
0.0f,
|
||||
-1.0f,
|
||||
0.0f,
|
||||
1.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
1.0f,
|
||||
}},
|
||||
{HYPRUTILS_TRANSFORM_FLIPPED,
|
||||
{
|
||||
-1.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
1.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
1.0f,
|
||||
}},
|
||||
{HYPRUTILS_TRANSFORM_FLIPPED_90,
|
||||
{
|
||||
0.0f,
|
||||
1.0f,
|
||||
0.0f,
|
||||
1.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
1.0f,
|
||||
}},
|
||||
{HYPRUTILS_TRANSFORM_FLIPPED_180,
|
||||
{
|
||||
1.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
-1.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
1.0f,
|
||||
}},
|
||||
{HYPRUTILS_TRANSFORM_FLIPPED_270,
|
||||
{
|
||||
0.0f,
|
||||
-1.0f,
|
||||
0.0f,
|
||||
-1.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
1.0f,
|
||||
}},
|
||||
};
|
||||
|
||||
void matrixTransform(float mat[9], eTransform transform) {
|
||||
matrixMultiply(mat, mat, transforms.at(transform).data());
|
||||
}
|
||||
|
||||
void matrixProjection(float mat[9], int width, int height, eTransform transform) {
|
||||
memset(mat, 0, sizeof(*mat) * 9);
|
||||
|
||||
const float* t = transforms.at(transform).data();
|
||||
float x = 2.0f / width;
|
||||
float y = 2.0f / height;
|
||||
|
||||
// Rotation + reflection
|
||||
mat[0] = x * t[0];
|
||||
mat[1] = x * t[1];
|
||||
mat[3] = y * -t[3];
|
||||
mat[4] = y * -t[4];
|
||||
|
||||
// Translation
|
||||
mat[2] = -copysign(1.0f, mat[0] + mat[1]);
|
||||
mat[5] = -copysign(1.0f, mat[3] + mat[4]);
|
||||
|
||||
// Identity
|
||||
mat[8] = 1.0f;
|
||||
}
|
||||
|
||||
void projectBox(float mat[9], CBox& box, eTransform transform, float rotation, const float projection[9]) {
|
||||
double x = box.x;
|
||||
double y = box.y;
|
||||
double width = box.width;
|
||||
double height = box.height;
|
||||
|
||||
matrixIdentity(mat);
|
||||
matrixTranslate(mat, x, y);
|
||||
|
||||
if (rotation != 0) {
|
||||
matrixTranslate(mat, width / 2, height / 2);
|
||||
matrixRotate(mat, rotation);
|
||||
matrixTranslate(mat, -width / 2, -height / 2);
|
||||
}
|
||||
|
||||
matrixScale(mat, width, height);
|
||||
|
||||
if (transform != HYPRUTILS_TRANSFORM_NORMAL) {
|
||||
matrixTranslate(mat, 0.5, 0.5);
|
||||
matrixTransform(mat, transform);
|
||||
matrixTranslate(mat, -0.5, -0.5);
|
||||
}
|
||||
|
||||
matrixMultiply(mat, projection, mat);
|
||||
}
|
18
src/backend/drm/Math.hpp
Normal file
18
src/backend/drm/Math.hpp
Normal file
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
// FIXME: migrate this to utils
|
||||
|
||||
// includes box and vector as well
|
||||
#include <hyprutils/math/Region.hpp>
|
||||
|
||||
using namespace Hyprutils::Math;
|
||||
|
||||
void projectBox(float mat[9], CBox& box, eTransform transform, float rotation, const float projection[9]);
|
||||
void matrixProjection(float mat[9], int width, int height, eTransform transform);
|
||||
void matrixTransform(float mat[9], eTransform transform);
|
||||
void matrixRotate(float mat[9], float rad);
|
||||
void matrixScale(float mat[9], float x, float y);
|
||||
void matrixTranslate(float mat[9], float x, float y);
|
||||
void matrixTranspose(float mat[9], const float a[9]);
|
||||
void matrixMultiply(float mat[9], const float a[9], const float b[9]);
|
||||
void matrixIdentity(float mat[9]);
|
465
src/backend/drm/Renderer.cpp
Normal file
465
src/backend/drm/Renderer.cpp
Normal file
|
@ -0,0 +1,465 @@
|
|||
#include "Renderer.hpp"
|
||||
#include <xf86drm.h>
|
||||
#include <xf86drmMode.h>
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include "Math.hpp"
|
||||
#include "Shared.hpp"
|
||||
|
||||
using namespace Aquamarine;
|
||||
using namespace Hyprutils::Memory;
|
||||
using namespace Hyprutils::Math;
|
||||
#define SP CSharedPointer
|
||||
#define WP CWeakPointer
|
||||
|
||||
// static funcs
|
||||
WP<CBackend> gBackend;
|
||||
|
||||
// ------------------- shader utils
|
||||
|
||||
GLuint compileShader(const GLuint& type, std::string src) {
|
||||
auto shader = glCreateShader(type);
|
||||
|
||||
auto shaderSource = src.c_str();
|
||||
|
||||
glShaderSource(shader, 1, (const GLchar**)&shaderSource, nullptr);
|
||||
glCompileShader(shader);
|
||||
|
||||
GLint ok;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &ok);
|
||||
|
||||
if (ok == GL_FALSE)
|
||||
return 0;
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
GLuint createProgram(const std::string& vert, const std::string& frag) {
|
||||
auto vertCompiled = compileShader(GL_VERTEX_SHADER, vert);
|
||||
if (vertCompiled == 0)
|
||||
return 0;
|
||||
|
||||
auto fragCompiled = compileShader(GL_FRAGMENT_SHADER, frag);
|
||||
if (fragCompiled == 0)
|
||||
return 0;
|
||||
|
||||
auto prog = glCreateProgram();
|
||||
glAttachShader(prog, vertCompiled);
|
||||
glAttachShader(prog, fragCompiled);
|
||||
glLinkProgram(prog);
|
||||
|
||||
glDetachShader(prog, vertCompiled);
|
||||
glDetachShader(prog, fragCompiled);
|
||||
glDeleteShader(vertCompiled);
|
||||
glDeleteShader(fragCompiled);
|
||||
|
||||
GLint ok;
|
||||
glGetProgramiv(prog, GL_LINK_STATUS, &ok);
|
||||
if (ok == GL_FALSE)
|
||||
return 0;
|
||||
|
||||
return prog;
|
||||
}
|
||||
|
||||
inline const std::string VERT_SRC = R"#(
|
||||
uniform mat3 proj;
|
||||
attribute vec2 pos;
|
||||
attribute vec2 texcoord;
|
||||
varying vec2 v_texcoord;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(proj * vec3(pos, 1.0), 1.0);
|
||||
v_texcoord = texcoord;
|
||||
})#";
|
||||
|
||||
inline const std::string FRAG_SRC = R"#(
|
||||
precision highp float;
|
||||
varying vec2 v_texcoord; // is in 0-1
|
||||
uniform sampler2D tex;
|
||||
|
||||
void main() {
|
||||
gl_FragColor = texture2D(tex, v_texcoord);
|
||||
})#";
|
||||
|
||||
// ------------------- gbm stuff
|
||||
|
||||
static int openRenderNode(int drmFd) {
|
||||
auto renderName = drmGetRenderDeviceNameFromFd(drmFd);
|
||||
if (!renderName) {
|
||||
// This can happen on split render/display platforms, fallback to
|
||||
// primary node
|
||||
renderName = drmGetPrimaryDeviceNameFromFd(drmFd);
|
||||
if (!renderName) {
|
||||
gBackend->log(AQ_LOG_ERROR, "drmRenderer: drmGetPrimaryDeviceNameFromFd failed");
|
||||
return -1;
|
||||
}
|
||||
gBackend->log(AQ_LOG_WARNING, std::format("DRM dev {} has no render node, falling back to primary", renderName));
|
||||
|
||||
drmVersion* render_version = drmGetVersion(drmFd);
|
||||
if (render_version && render_version->name) {
|
||||
if (strcmp(render_version->name, "evdi") == 0) {
|
||||
free(renderName);
|
||||
renderName = (char*)malloc(sizeof(char) * 15);
|
||||
strcpy(renderName, "/dev/dri/card0");
|
||||
}
|
||||
drmFreeVersion(render_version);
|
||||
}
|
||||
}
|
||||
|
||||
int renderFD = open(renderName, O_RDWR | O_CLOEXEC);
|
||||
if (renderFD < 0)
|
||||
gBackend->log(AQ_LOG_ERROR, std::format("openRenderNode failed to open drm device {}", renderName));
|
||||
|
||||
free(renderName);
|
||||
return renderFD;
|
||||
}
|
||||
|
||||
// ------------------- egl stuff
|
||||
|
||||
inline void loadGLProc(void* pProc, const char* name) {
|
||||
void* proc = (void*)eglGetProcAddress(name);
|
||||
if (proc == NULL) {
|
||||
gBackend->log(AQ_LOG_ERROR, std::format("eglGetProcAddress({}) failed", name));
|
||||
abort();
|
||||
}
|
||||
*(void**)pProc = proc;
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
SP<CDRMRenderer> CDRMRenderer::attempt(int drmfd, SP<CBackend> backend_) {
|
||||
SP<CDRMRenderer> renderer = SP<CDRMRenderer>(new CDRMRenderer());
|
||||
renderer->drmFD = drmfd;
|
||||
renderer->backend = backend_;
|
||||
gBackend = backend_;
|
||||
|
||||
const std::string EGLEXTENSIONS = (const char*)eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
|
||||
|
||||
if (!EGLEXTENSIONS.contains("KHR_platform_gbm")) {
|
||||
backend_->log(AQ_LOG_ERROR, "CDRMRenderer: fail, no gbm support");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// init gbm stuff
|
||||
|
||||
renderer->gbm.fd = openRenderNode(drmfd);
|
||||
if (!renderer->gbm.fd) {
|
||||
backend_->log(AQ_LOG_ERROR, "CDRMRenderer: fail, no gbm fd");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
renderer->gbm.device = gbm_create_device(renderer->gbm.fd);
|
||||
if (!renderer->gbm.device) {
|
||||
backend_->log(AQ_LOG_ERROR, "CDRMRenderer: fail, no gbm device");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// init egl
|
||||
|
||||
if (eglBindAPI(EGL_OPENGL_ES_API) == EGL_FALSE) {
|
||||
backend_->log(AQ_LOG_ERROR, "CDRMRenderer: fail, eglBindAPI failed");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
loadGLProc(&renderer->egl.eglGetPlatformDisplayEXT, "eglGetPlatformDisplayEXT");
|
||||
loadGLProc(&renderer->egl.eglCreateImageKHR, "eglCreateImageKHR");
|
||||
loadGLProc(&renderer->egl.eglDestroyImageKHR, "eglDestroyImageKHR");
|
||||
loadGLProc(&renderer->egl.glEGLImageTargetTexture2DOES, "glEGLImageTargetTexture2DOES");
|
||||
loadGLProc(&renderer->egl.glEGLImageTargetRenderbufferStorageOES, "glEGLImageTargetRenderbufferStorageOES");
|
||||
|
||||
if (!renderer->egl.eglGetPlatformDisplayEXT) {
|
||||
backend_->log(AQ_LOG_ERROR, "CDRMRenderer: fail, no eglGetPlatformDisplayEXT");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!renderer->egl.eglCreateImageKHR) {
|
||||
backend_->log(AQ_LOG_ERROR, "CDRMRenderer: fail, no eglCreateImageKHR");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<EGLint> attrs = {EGL_NONE};
|
||||
renderer->egl.display = renderer->egl.eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR, renderer->gbm.device, attrs.data());
|
||||
if (renderer->egl.display == EGL_NO_DISPLAY) {
|
||||
backend_->log(AQ_LOG_ERROR, "CDRMRenderer: fail, eglGetPlatformDisplayEXT failed");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
EGLint major, minor;
|
||||
if (eglInitialize(renderer->egl.display, &major, &minor) == EGL_FALSE) {
|
||||
backend_->log(AQ_LOG_ERROR, "CDRMRenderer: fail, eglInitialize failed");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
attrs.clear();
|
||||
|
||||
const std::string EGLEXTENSIONS2 = (const char*)eglQueryString(renderer->egl.display, EGL_EXTENSIONS);
|
||||
|
||||
if (EGLEXTENSIONS2.contains("IMG_context_priority")) {
|
||||
attrs.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG);
|
||||
attrs.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
|
||||
}
|
||||
|
||||
attrs.push_back(EGL_CONTEXT_MAJOR_VERSION);
|
||||
attrs.push_back(2);
|
||||
attrs.push_back(EGL_CONTEXT_MINOR_VERSION);
|
||||
attrs.push_back(0);
|
||||
attrs.push_back(EGL_CONTEXT_OPENGL_DEBUG);
|
||||
attrs.push_back(EGL_FALSE);
|
||||
|
||||
attrs.push_back(EGL_NONE);
|
||||
|
||||
renderer->egl.context = eglCreateContext(renderer->egl.display, EGL_NO_CONFIG_KHR, EGL_NO_CONTEXT, attrs.data());
|
||||
if (renderer->egl.context == EGL_NO_CONTEXT) {
|
||||
backend_->log(AQ_LOG_ERROR, "CDRMRenderer: fail, eglCreateContext failed");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (EGLEXTENSIONS2.contains("IMG_context_priority")) {
|
||||
EGLint priority = EGL_CONTEXT_PRIORITY_MEDIUM_IMG;
|
||||
eglQueryContext(renderer->egl.display, renderer->egl.context, EGL_CONTEXT_PRIORITY_LEVEL_IMG, &priority);
|
||||
if (priority != EGL_CONTEXT_PRIORITY_HIGH_IMG)
|
||||
backend_->log(AQ_LOG_DEBUG, "CDRMRenderer: didnt get a high priority context");
|
||||
else
|
||||
backend_->log(AQ_LOG_DEBUG, "CDRMRenderer: got a high priority context");
|
||||
}
|
||||
|
||||
// init shaders
|
||||
|
||||
renderer->setEGL();
|
||||
|
||||
renderer->gl.shader.program = createProgram(VERT_SRC, FRAG_SRC);
|
||||
if (renderer->gl.shader.program == 0) {
|
||||
backend_->log(AQ_LOG_ERROR, "CDRMRenderer: fail, shader failed");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
renderer->gl.shader.proj = glGetUniformLocation(renderer->gl.shader.program, "proj");
|
||||
renderer->gl.shader.posAttrib = glGetAttribLocation(renderer->gl.shader.program, "pos");
|
||||
renderer->gl.shader.texAttrib = glGetAttribLocation(renderer->gl.shader.program, "texcoord");
|
||||
renderer->gl.shader.tex = glGetUniformLocation(renderer->gl.shader.program, "tex");
|
||||
|
||||
renderer->restoreEGL();
|
||||
|
||||
backend_->log(AQ_LOG_DEBUG, "CDRMRenderer: success");
|
||||
|
||||
return renderer;
|
||||
}
|
||||
|
||||
void CDRMRenderer::setEGL() {
|
||||
savedEGLState.display = eglGetCurrentDisplay();
|
||||
savedEGLState.context = eglGetCurrentContext();
|
||||
savedEGLState.draw = eglGetCurrentSurface(EGL_DRAW);
|
||||
savedEGLState.read = eglGetCurrentSurface(EGL_READ);
|
||||
|
||||
if (!eglMakeCurrent(egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE, egl.context))
|
||||
backend->log(AQ_LOG_WARNING, "CDRMRenderer: setEGL eglMakeCurrent failed");
|
||||
}
|
||||
|
||||
void CDRMRenderer::restoreEGL() {
|
||||
EGLDisplay dpy = savedEGLState.display ? savedEGLState.display : egl.display;
|
||||
|
||||
// egl can't handle this
|
||||
if (dpy == EGL_NO_DISPLAY)
|
||||
return;
|
||||
|
||||
if (!eglMakeCurrent(dpy, savedEGLState.draw, savedEGLState.read, savedEGLState.context))
|
||||
backend->log(AQ_LOG_WARNING, "CDRMRenderer: restoreEGL eglMakeCurrent failed");
|
||||
}
|
||||
|
||||
EGLImageKHR CDRMRenderer::createEGLImage(const SDMABUFAttrs& attrs) {
|
||||
std::vector<uint32_t> attribs;
|
||||
|
||||
attribs.push_back(EGL_WIDTH);
|
||||
attribs.push_back(attrs.size.x);
|
||||
attribs.push_back(EGL_HEIGHT);
|
||||
attribs.push_back(attrs.size.y);
|
||||
attribs.push_back(EGL_LINUX_DRM_FOURCC_EXT);
|
||||
attribs.push_back(attrs.format);
|
||||
|
||||
struct {
|
||||
EGLint fd;
|
||||
EGLint offset;
|
||||
EGLint pitch;
|
||||
EGLint modlo;
|
||||
EGLint modhi;
|
||||
} attrNames[4] = {
|
||||
{EGL_DMA_BUF_PLANE0_FD_EXT, EGL_DMA_BUF_PLANE0_OFFSET_EXT, EGL_DMA_BUF_PLANE0_PITCH_EXT, EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT},
|
||||
{EGL_DMA_BUF_PLANE1_FD_EXT, EGL_DMA_BUF_PLANE1_OFFSET_EXT, EGL_DMA_BUF_PLANE1_PITCH_EXT, EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT, EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT},
|
||||
{EGL_DMA_BUF_PLANE2_FD_EXT, EGL_DMA_BUF_PLANE2_OFFSET_EXT, EGL_DMA_BUF_PLANE2_PITCH_EXT, EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT, EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT},
|
||||
{EGL_DMA_BUF_PLANE3_FD_EXT, EGL_DMA_BUF_PLANE3_OFFSET_EXT, EGL_DMA_BUF_PLANE3_PITCH_EXT, EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT, EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT}};
|
||||
|
||||
for (int i = 0; i < attrs.planes; i++) {
|
||||
attribs.push_back(attrNames[i].fd);
|
||||
attribs.push_back(attrs.fds[i]);
|
||||
attribs.push_back(attrNames[i].offset);
|
||||
attribs.push_back(attrs.offsets[i]);
|
||||
attribs.push_back(attrNames[i].pitch);
|
||||
attribs.push_back(attrs.strides[i]);
|
||||
if (attrs.modifier != DRM_FORMAT_MOD_INVALID) { // FIXME: this will implode if we don't support mods. Does anyone not support them??
|
||||
attribs.push_back(attrNames[i].modlo);
|
||||
attribs.push_back(attrs.modifier & 0xFFFFFFFF);
|
||||
attribs.push_back(attrNames[i].modhi);
|
||||
attribs.push_back(attrs.modifier >> 32);
|
||||
}
|
||||
}
|
||||
|
||||
attribs.push_back(EGL_IMAGE_PRESERVED_KHR);
|
||||
attribs.push_back(EGL_TRUE);
|
||||
|
||||
attribs.push_back(EGL_NONE);
|
||||
|
||||
EGLImageKHR image = egl.eglCreateImageKHR(egl.display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, (int*)attribs.data());
|
||||
if (image == EGL_NO_IMAGE_KHR) {
|
||||
backend->log(AQ_LOG_ERROR, std::format("EGL: EGLCreateImageKHR failed: {}", eglGetError()));
|
||||
return EGL_NO_IMAGE_KHR;
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
#define GLCALL(__CALL__) \
|
||||
{ \
|
||||
__CALL__; \
|
||||
auto err = glGetError(); \
|
||||
if (err != GL_NO_ERROR) { \
|
||||
backend->log(AQ_LOG_ERROR, \
|
||||
std::format("[GLES] Error in call at {}@{}: 0x{:x}", __LINE__, \
|
||||
([]() constexpr -> std::string { return std::string(__FILE__).substr(std::string(__FILE__).find_last_of('/') + 1); })(), err)); \
|
||||
} \
|
||||
}
|
||||
|
||||
CDRMRenderer::GLTex CDRMRenderer::glTex(Hyprutils::Memory::CSharedPointer<IBuffer> buffa) {
|
||||
GLTex tex;
|
||||
|
||||
tex.image = createEGLImage(buffa->dmabuf());
|
||||
if (tex.image == EGL_NO_IMAGE_KHR) {
|
||||
backend->log(AQ_LOG_ERROR, std::format("EGL (glTex): createEGLImage failed: {}", eglGetError()));
|
||||
return tex;
|
||||
}
|
||||
|
||||
GLCALL(glGenTextures(1, &tex.texid));
|
||||
|
||||
GLCALL(glBindTexture(GL_TEXTURE_2D, tex.texid));
|
||||
GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
|
||||
GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
|
||||
egl.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, tex.image);
|
||||
GLCALL(glBindTexture(GL_TEXTURE_2D, 0));
|
||||
|
||||
return tex;
|
||||
}
|
||||
|
||||
inline const float fullVerts[] = {
|
||||
1, 0, // top right
|
||||
0, 0, // top left
|
||||
1, 1, // bottom right
|
||||
0, 1, // bottom left
|
||||
};
|
||||
|
||||
bool CDRMRenderer::blit(SP<IBuffer> from, SP<IBuffer> to) {
|
||||
setEGL();
|
||||
|
||||
if (from->dmabuf().size != to->dmabuf().size) {
|
||||
backend->log(AQ_LOG_ERROR, "EGL (blit): buffer sizes mismatched");
|
||||
return false;
|
||||
}
|
||||
|
||||
// firstly, get a texture from the from buffer
|
||||
auto fromTex = glTex(from);
|
||||
|
||||
TRACE(backend->log(AQ_LOG_TRACE, std::format("EGL (blit): fromTex id {}, image 0x{:x}", fromTex.texid, (uintptr_t)fromTex.image)));
|
||||
|
||||
// then, get a rbo from our to buffer
|
||||
auto toDma = to->dmabuf();
|
||||
|
||||
auto rboImage = createEGLImage(toDma);
|
||||
if (rboImage == EGL_NO_IMAGE_KHR) {
|
||||
backend->log(AQ_LOG_ERROR, std::format("EGL (blit): createEGLImage failed: {}", eglGetError()));
|
||||
return false;
|
||||
}
|
||||
|
||||
TRACE(backend->log(AQ_LOG_TRACE, std::format("EGL (blit): rboImage 0x{:x}", (uintptr_t)rboImage)));
|
||||
|
||||
GLuint rboID = 0, fboID = 0;
|
||||
|
||||
// TODO: don't spam this?
|
||||
GLCALL(glGenRenderbuffers(1, &rboID));
|
||||
GLCALL(glBindRenderbuffer(GL_RENDERBUFFER, rboID));
|
||||
egl.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, (GLeglImageOES)rboImage);
|
||||
GLCALL(glBindRenderbuffer(GL_RENDERBUFFER, 0));
|
||||
|
||||
GLCALL(glGenFramebuffers(1, &fboID));
|
||||
GLCALL(glBindFramebuffer(GL_FRAMEBUFFER, fboID));
|
||||
GLCALL(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rboID));
|
||||
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||
backend->log(AQ_LOG_ERROR, std::format("EGL (blit): glCheckFramebufferStatus failed: {}", glGetError()));
|
||||
return false;
|
||||
}
|
||||
|
||||
GLCALL(glBindRenderbuffer(GL_RENDERBUFFER, rboID));
|
||||
GLCALL(glBindFramebuffer(GL_FRAMEBUFFER, fboID));
|
||||
|
||||
TRACE(backend->log(AQ_LOG_TRACE, std::format("EGL (blit): fbo {} rbo {}", fboID, rboID)));
|
||||
|
||||
glClearColor(0.77F, 0.F, 0.74F, 1.F);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
// done, let's render the texture to the rbo
|
||||
CBox renderBox = {{}, toDma.size};
|
||||
|
||||
TRACE(backend->log(AQ_LOG_TRACE, std::format("EGL (blit): box size {}", renderBox.size())));
|
||||
|
||||
float mtx[9];
|
||||
float identity[9];
|
||||
float monitorProj[9];
|
||||
matrixIdentity(identity);
|
||||
projectBox(mtx, renderBox, HYPRUTILS_TRANSFORM_NORMAL, 0, identity);
|
||||
|
||||
matrixProjection(monitorProj, toDma.size.x, toDma.size.y, HYPRUTILS_TRANSFORM_NORMAL);
|
||||
|
||||
float glMtx[9];
|
||||
matrixMultiply(glMtx, monitorProj, mtx);
|
||||
|
||||
GLCALL(glViewport(0, 0, toDma.size.x, toDma.size.y));
|
||||
|
||||
GLCALL(glActiveTexture(GL_TEXTURE0));
|
||||
GLCALL(glBindTexture(GL_TEXTURE_2D, fromTex.texid));
|
||||
GLCALL(glUseProgram(gl.shader.program));
|
||||
GLCALL(glDisable(GL_BLEND));
|
||||
GLCALL(glDisable(GL_SCISSOR_TEST));
|
||||
|
||||
matrixTranspose(glMtx, glMtx);
|
||||
GLCALL(glUniformMatrix3fv(gl.shader.proj, 1, GL_FALSE, glMtx));
|
||||
|
||||
GLCALL(glUniform1i(gl.shader.tex, 0));
|
||||
|
||||
GLCALL(glVertexAttribPointer(gl.shader.posAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts));
|
||||
GLCALL(glVertexAttribPointer(gl.shader.texAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts));
|
||||
|
||||
GLCALL(glEnableVertexAttribArray(gl.shader.posAttrib));
|
||||
GLCALL(glEnableVertexAttribArray(gl.shader.texAttrib));
|
||||
|
||||
GLCALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4));
|
||||
|
||||
GLCALL(glDisableVertexAttribArray(gl.shader.posAttrib));
|
||||
GLCALL(glDisableVertexAttribArray(gl.shader.texAttrib));
|
||||
|
||||
GLCALL(glBindTexture(GL_TEXTURE_2D, 0));
|
||||
|
||||
// rendered, cleanup
|
||||
|
||||
GLCALL(glBindRenderbuffer(GL_RENDERBUFFER, 0));
|
||||
GLCALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
|
||||
|
||||
GLCALL(glDeleteTextures(1, &fromTex.texid));
|
||||
|
||||
GLCALL(glDeleteRenderbuffers(1, &rboID));
|
||||
GLCALL(glDeleteFramebuffers(1, &fboID));
|
||||
|
||||
egl.eglDestroyImageKHR(egl.display, rboImage);
|
||||
egl.eglDestroyImageKHR(egl.display, fromTex.image);
|
||||
|
||||
restoreEGL();
|
||||
|
||||
return true;
|
||||
}
|
65
src/backend/drm/Renderer.hpp
Normal file
65
src/backend/drm/Renderer.hpp
Normal file
|
@ -0,0 +1,65 @@
|
|||
#pragma once
|
||||
|
||||
#include <aquamarine/backend/DRM.hpp>
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
#include <GLES2/gl2.h>
|
||||
#include <GLES2/gl2ext.h>
|
||||
#include <gbm.h>
|
||||
|
||||
namespace Aquamarine {
|
||||
class CDRMRenderer {
|
||||
public:
|
||||
static Hyprutils::Memory::CSharedPointer<CDRMRenderer> attempt(int drmfd, Hyprutils::Memory::CSharedPointer<CBackend> backend_);
|
||||
|
||||
int drmFD = -1;
|
||||
|
||||
bool blit(Hyprutils::Memory::CSharedPointer<IBuffer> from, Hyprutils::Memory::CSharedPointer<IBuffer> to);
|
||||
|
||||
void setEGL();
|
||||
void restoreEGL();
|
||||
|
||||
struct {
|
||||
struct {
|
||||
GLuint program = 0;
|
||||
GLint proj = -1, tex = -1, posAttrib = -1, texAttrib = -1;
|
||||
} shader;
|
||||
} gl;
|
||||
|
||||
struct {
|
||||
int fd = -1;
|
||||
gbm_device* device = nullptr;
|
||||
} gbm;
|
||||
|
||||
struct {
|
||||
EGLDisplay display = nullptr;
|
||||
EGLContext context = nullptr;
|
||||
|
||||
PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = nullptr;
|
||||
PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR = nullptr;
|
||||
PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR = nullptr;
|
||||
PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES = nullptr;
|
||||
PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glEGLImageTargetRenderbufferStorageOES = nullptr;
|
||||
} egl;
|
||||
|
||||
struct {
|
||||
EGLDisplay display = nullptr;
|
||||
EGLContext context = nullptr;
|
||||
EGLSurface draw = nullptr, read = nullptr;
|
||||
} savedEGLState;
|
||||
|
||||
struct GLTex {
|
||||
EGLImage image = nullptr;
|
||||
GLuint texid = 0;
|
||||
};
|
||||
|
||||
GLTex glTex(Hyprutils::Memory::CSharedPointer<IBuffer> buf);
|
||||
|
||||
private:
|
||||
CDRMRenderer() = default;
|
||||
|
||||
EGLImageKHR createEGLImage(const SDMABUFAttrs& attrs);
|
||||
|
||||
Hyprutils::Memory::CWeakPointer<CBackend> backend;
|
||||
};
|
||||
};
|
Loading…
Reference in a new issue