Skip to content

Commit

Permalink
gbm example
Browse files Browse the repository at this point in the history
-exercises libgbm and libdrm wrappers

Signed-off-by: Joel Winarske <joel.winarske@gmail.com>
  • Loading branch information
jwinarske committed Nov 25, 2024
1 parent 6a8be69 commit b13c7eb
Show file tree
Hide file tree
Showing 11 changed files with 320 additions and 2 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/clang-tidy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ jobs:
echo ${{ github.server_url }}
sudo apt-get -o DPkg::Lock::Timeout=1200 -y update
sudo apt-get -o DPkg::Lock::Timeout=1200 -y install ninja-build \
libdrm-dev libinput-dev libsystemd-dev libxkbcommon-dev
libdrm-dev libinput-dev libsystemd-dev libxkbcommon-dev \
mesa-common-dev libegl1-mesa-dev libgles2-mesa-dev mesa-utils
wget http://security.ubuntu.com/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb
sudo apt install ./libtinfo5_6.3-2ubuntu0.1_amd64.deb
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ jobs:
echo ${{ github.server_url }}
sudo apt-get -o DPkg::Lock::Timeout=1200 -y update
sudo apt-get -o DPkg::Lock::Timeout=1200 -y install ninja-build \
libdrm-dev libinput-dev libsystemd-dev libxkbcommon-dev hwdata
libdrm-dev libinput-dev libsystemd-dev libxkbcommon-dev hwdata \
mesa-common-dev libegl1-mesa-dev libgles2-mesa-dev mesa-utils
pip install --user meson
meson --version
gcc --version
Expand Down
257 changes: 257 additions & 0 deletions examples/drm_gbm.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
/*
* Copyright (c) 2024 The drmpp Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <csignal>
#include <filesystem>
#include <iostream>

#include "shared_libs/libdrm.h"
#include "shared_libs/libgbm.h"

#include <EGL/egl.h>
#include <GL/gl.h>

#include <cxxopts.hpp>

#include "drmpp.h"

struct Configuration {};

static volatile bool gRunning = true;

/**
* @brief Signal handler function to handle signals.
*
* This function is a signal handler for handling signals. It sets the value of
* keep_running to false, which will stop the program from running. The function
* does not take any input parameters.
*
* @param signal The signal number. This parameter is not used by the function.
*
* @return void
*/
void handle_signal(const int signal) {
if (signal == SIGINT) {
gRunning = false;
}
}

class App final {
public:
explicit App(const Configuration& /* config */)
: logging_(std::make_unique<Logging>()) {}

~App() = default;

bool run() {
device_ = open("/dev/dri/card0", O_RDWR);
assert(device_ != 0);

resources_ = LibDrm->mode_get_resources(device_);
assert(resources_ != nullptr);

connector_ = find_connector(resources_);
assert(connector_ != nullptr);

printf("Mode list:\n------------------\n");
for (int i = 0; i < connector_->count_modes; i++) {
printf("Mode %d: %s\n", i, connector_->modes[i].name);
}

connector_id_ = connector_->connector_id;
mode_info_ = connector_->modes[0];

encoder_ = find_encoder(connector_);
assert(encoder_ != nullptr);

crtc_ = LibDrm->mode_get_crtc(device_, encoder_->crtc_id);
assert(crtc_ != nullptr);

LibDrm->mode_free_encoder(encoder_);
LibDrm->mode_free_connector(connector_);
LibDrm->mode_free_resources(resources_);

gbm_device_ = LibGbm->create_device(device_);
assert(gbm_device_ != nullptr);

gbm_surface_ = LibGbm->surface_create(
gbm_device_, mode_info_.hdisplay, mode_info_.vdisplay,
GBM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
assert(gbm_surface_ != nullptr);

static EGLint attributes[] = {EGL_SURFACE_TYPE,
EGL_WINDOW_BIT,
EGL_RED_SIZE,
8,
EGL_GREEN_SIZE,
8,
EGL_BLUE_SIZE,
8,
EGL_ALPHA_SIZE,
0,
EGL_RENDERABLE_TYPE,
EGL_OPENGL_ES2_BIT,
EGL_NONE};

static constexpr EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE};

display_ = eglGetDisplay(gbm_device_);
eglInitialize(display_, nullptr, nullptr);
eglBindAPI(EGL_OPENGL_API);
eglGetConfigs(display_, nullptr, 0, &count_);
configs_ = static_cast<EGLConfig*>(malloc(count_ * sizeof(*configs_)));
eglChooseConfig(display_, attributes, configs_, count_, &num_config_);
config_index_ = match_config_to_visual(display_, GBM_FORMAT_XRGB8888,
configs_, num_config_);
context_ = eglCreateContext(display_, configs_[config_index_],
EGL_NO_CONTEXT, context_attribs);
egl_surface_ = eglCreateWindowSurface(display_, configs_[config_index_],
gbm_surface_, nullptr);
free(configs_);
eglMakeCurrent(display_, egl_surface_, egl_surface_, context_);

for (auto i = 0; i < 600; i++)
draw(static_cast<float>(i) / 600.0f);

LibDrm->mode_set_crtc(device_, crtc_->crtc_id, crtc_->buffer_id, crtc_->x,
crtc_->y, &connector_id_, 1, &crtc_->mode);
LibDrm->mode_free_crtc(crtc_);

if (previous_bo_) {
drmModeRmFB(device_, previous_fb_);
LibGbm->surface_release_buffer(gbm_surface_, previous_bo_);
}

eglDestroySurface(display_, egl_surface_);
LibGbm->surface_destroy(gbm_surface_);
eglDestroyContext(display_, context_);
eglTerminate(display_);
LibGbm->device_destroy(gbm_device_);

close(device_);

return false;
}

private:
std::unique_ptr<Logging> logging_;

int device_{};
drmModeRes* resources_{};
drmModeConnector* connector_{};
uint32_t connector_id_{};
drmModeEncoder* encoder_{};
drmModeModeInfo mode_info_{};
drmModeCrtc* crtc_{};
gbm_device* gbm_device_{};
EGLDisplay display_{};
EGLContext context_{};
gbm_surface* gbm_surface_{};
EGLSurface egl_surface_{};
EGLConfig config_{};
EGLint num_config_{};
EGLint count_ = 0;
EGLConfig* configs_{};
int config_index_{};
int i_{};

gbm_bo* previous_bo_{};
uint32_t previous_fb_{};

gbm_bo* bo_{};
uint32_t handle_{};
uint32_t pitch_{};
uint32_t fb_{};
uint64_t modifier_{};

drmModeConnector* find_connector(const drmModeRes* resources) const {
for (auto i = 0; i < resources->count_connectors; i++) {
drmModeConnector* connector =
drmModeGetConnector(device_, resources->connectors[i]);
if (connector->connection == DRM_MODE_CONNECTED) {
return connector;
}
drmModeFreeConnector(connector);
}
return nullptr; // if no connector found
}

drmModeEncoder* find_encoder(const drmModeConnector* connector) const {
return LibDrm->mode_get_encoder(device_, connector->encoder_id);
}

void swap_buffers() {
eglSwapBuffers(display_, egl_surface_);
bo_ = LibGbm->surface_lock_front_buffer(gbm_surface_);
handle_ = LibGbm->bo_get_handle(bo_).u32;
pitch_ = LibGbm->bo_get_stride(bo_);
LibDrm->mode_add_fb(device_, mode_info_.hdisplay, mode_info_.vdisplay, 24,
32, pitch_, handle_, &fb_);
LibDrm->mode_set_crtc(device_, crtc_->crtc_id, fb_, 0, 0, &connector_id_, 1,
&mode_info_);
if (previous_bo_) {
LibDrm->mode_rm_fb(device_, previous_fb_);
LibGbm->surface_release_buffer(gbm_surface_, previous_bo_);
}
previous_bo_ = bo_;
previous_fb_ = fb_;
}

void draw(const float progress) {
glClearColor(1.0f - progress, progress, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
swap_buffers();
}

static int match_config_to_visual(EGLDisplay egl_display,
const EGLint& visual_id,
const EGLConfig* configs,
const int count) {
EGLint id;
for (auto i = 0; i < count; ++i) {
assert(configs);
assert(configs[i]);
if (!eglGetConfigAttrib(egl_display, configs[i], EGL_NATIVE_VISUAL_ID,
&id))
continue;
if (id == visual_id)
return i;
}
return -1;
}
};

int main(const int argc, char** argv) {
std::signal(SIGINT, handle_signal);

cxxopts::Options options("drm-simple", "Simple DRM example");
options.set_width(80)
.set_tab_expansion()
.allow_unrecognised_options()
.add_options()("help", "Print help");

if (options.parse(argc, argv).count("help")) {
spdlog::info("{}", options.help({"", "Group"}));
exit(EXIT_SUCCESS);
}

App app({});

(void)app.run();

return EXIT_SUCCESS;
}
Empty file added examples/drm_snake.cc
Empty file.
15 changes: 15 additions & 0 deletions examples/meson.build
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# SPDX-FileCopyrightText: (c) 2024 The drmpp Contributors
# SPDX-License-Identifier: Apache-2.0

egl_dep = dependency('egl', include_type : 'system', required : true)
opengl_dep = dependency('opengl', include_type : 'system', required : true)

cmake = import('cmake')

opt_var = cmake.subproject_options()
Expand Down Expand Up @@ -90,4 +93,16 @@ executable('drm-lz', ['drm_lz.cc'],
drmp_dep,
],
install : false,
)

executable('drm-gbm', ['drm_gbm.cc'],
include_directories : incdirs,
dependencies : [
cxxopts_dep,
egl_dep,
opengl_dep,
drmp_dep,
gbm_dep,
],
install : false,
)
Empty file added examples/snake_game.cc
Empty file.
Empty file added examples/snake_game.h
Empty file.
9 changes: 9 additions & 0 deletions include/drmpp/shared_libs/libdrm.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,14 @@ struct LibDrmExports {
typedef drmModePlanePtr (*DrmModeGetPlane)(int fd, uint32_t plane_id);
typedef void (*DrmModeFreePlane)(drmModePlanePtr ptr);
typedef int (*DrmModeRmFB)(int fd, uint32_t bufferId);
typedef int (*DrmModeAddFB)(int fd,
uint32_t width,
uint32_t height,
uint8_t depth,
uint8_t bpp,
uint32_t pitch,
uint32_t bo_handle,
uint32_t* buf_id);

DrmIoctl drm_ioctl = nullptr;
DrmModeGetConnector mode_get_connector = nullptr;
Expand All @@ -104,6 +112,7 @@ struct LibDrmExports {
DrmModeSetCrtc mode_set_crtc = nullptr;
DrmModeFreeCrtc mode_free_crtc = nullptr;
DrmModeGetFB2 mode_get_fb2 = nullptr;
DrmModeAddFB mode_add_fb = nullptr;
DrmModeAddFB2 mode_add_fb2 = nullptr;
DrmModeAddFB2WithModifiers mode_add_fb2_with_modifiers = nullptr;
DrmModeFreeFB2 mode_free_fb2 = nullptr;
Expand Down
23 changes: 23 additions & 0 deletions include/drmpp/shared_libs/libgbm.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,20 @@ struct LibGbmExports {
typedef uint32_t (*BoGetHeightFnPtr)(gbm_bo* bo);
typedef uint32_t (*BoGetFormatFnPtr)(gbm_bo* bo);
typedef uint64_t (*BoGetModifierFnPtr)(gbm_bo* bo);
typedef uint32_t (*BoGetStride)(gbm_bo* bo);

typedef gbm_bo_handle (*BoGetHandle)(gbm_bo* bo);
typedef gbm_bo* (*SurfaceLockFrontBuffer)(gbm_surface* surface);
typedef void (*SurfaceReleaseBuffer)(gbm_surface* surface, gbm_bo* bo);

typedef gbm_device* (*CreateDevice)(int fd);
typedef void (*DeviceDestroy)(gbm_device* gbm);
typedef gbm_surface* (*SurfaceCreate)(gbm_device* gbm,
uint32_t width,
uint32_t height,
uint32_t format,
uint32_t flags);
typedef void (*SurfaceDestroy)(gbm_surface* surface);

BoCreateFnPtr bo_create = nullptr;
BoDestroyFnPtr bo_destroy = nullptr;
Expand All @@ -67,6 +81,15 @@ struct LibGbmExports {
BoGetHeightFnPtr bo_get_height = nullptr;
BoGetFormatFnPtr bo_get_format = nullptr;
BoGetModifierFnPtr bo_get_modifier = nullptr;
BoGetStride bo_get_stride = nullptr;
BoGetHandle bo_get_handle = nullptr;

CreateDevice create_device = nullptr;
DeviceDestroy device_destroy = nullptr;
SurfaceCreate surface_create = nullptr;
SurfaceDestroy surface_destroy = nullptr;
SurfaceLockFrontBuffer surface_lock_front_buffer = nullptr;
SurfaceReleaseBuffer surface_release_buffer = nullptr;
};

class LibGbm {
Expand Down
1 change: 1 addition & 0 deletions src/drm/libdrm.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ LibDrmExports::LibDrmExports(void* lib) {
GetFuncAddress(lib, "drmModeSetCrtc", &mode_set_crtc);
GetFuncAddress(lib, "drmModeFreeCrtc", &mode_free_crtc);
GetFuncAddress(lib, "drmModeGetFB2", &mode_get_fb2);
GetFuncAddress(lib, "drmModeAddFB", &mode_add_fb);
GetFuncAddress(lib, "drmModeAddFB2", &mode_add_fb2);
GetFuncAddress(lib, "drmModeAddFB2WithModifiers",
&mode_add_fb2_with_modifiers);
Expand Down
11 changes: 11 additions & 0 deletions src/gbm/libgbm.cc
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@ LibGbmExports::LibGbmExports(void* lib) {
GetFuncAddress(lib, "gbm_bo_get_height", &bo_get_height);
GetFuncAddress(lib, "gbm_bo_get_format", &bo_get_format);
GetFuncAddress(lib, "gbm_bo_get_modifier", &bo_get_modifier);
GetFuncAddress(lib, "gbm_bo_get_stride", &bo_get_stride);
GetFuncAddress(lib, "gbm_bo_get_handle", &bo_get_handle);

GetFuncAddress(lib, "gbm_create_device", &create_device);
GetFuncAddress(lib, "gbm_device_destroy", &device_destroy);

GetFuncAddress(lib, "gbm_surface_create", &surface_create);
GetFuncAddress(lib, "gbm_surface_destroy", &surface_destroy);
GetFuncAddress(lib, "gbm_surface_lock_front_buffer",
&surface_lock_front_buffer);
GetFuncAddress(lib, "gbm_surface_release_buffer", &surface_release_buffer);
}
}

Expand Down

0 comments on commit b13c7eb

Please sign in to comment.