{"id":26885307,"url":"https://github.com/cuarzosoftware/srm","last_synced_at":"2025-05-08T22:31:17.573Z","repository":{"id":170466008,"uuid":"631691704","full_name":"CuarzoSoftware/SRM","owner":"CuarzoSoftware","description":"Simple Rendering Manager","archived":false,"fork":false,"pushed_at":"2025-02-09T22:23:20.000Z","size":1003,"stargazers_count":56,"open_issues_count":0,"forks_count":6,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-03-31T18:52:27.338Z","etag":null,"topics":["c","drm","egl","gles2","kms","linux"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-2.1","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/CuarzoSoftware.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"CuarzoSoftware"}},"created_at":"2023-04-23T20:11:08.000Z","updated_at":"2025-03-07T20:07:04.000Z","dependencies_parsed_at":null,"dependency_job_id":"f9d306a1-f591-4f12-b34f-1b69c811d6fe","html_url":"https://github.com/CuarzoSoftware/SRM","commit_stats":null,"previous_names":["cuarzosoftware/srm"],"tags_count":31,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CuarzoSoftware%2FSRM","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CuarzoSoftware%2FSRM/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CuarzoSoftware%2FSRM/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CuarzoSoftware%2FSRM/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/CuarzoSoftware","download_url":"https://codeload.github.com/CuarzoSoftware/SRM/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253157705,"owners_count":21863153,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c","drm","egl","gles2","kms","linux"],"created_at":"2025-03-31T18:52:33.408Z","updated_at":"2025-05-08T22:31:17.552Z","avatar_url":"https://github.com/CuarzoSoftware.png","language":"C","readme":"# Simple Rendering Manager\n\n\u003cp align=\"left\"\u003e\n  \u003ca href=\"https://github.com/CuarzoSoftware/SRM/blob/main/LICENSE\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/license-LGPLv2.1-blue.svg\" alt=\"SRM is released under the LGPLv2.1 license.\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/CuarzoSoftware/SRM\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/version-0.12.0-brightgreen\" alt=\"Current SRM version.\" /\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\nSRM is a C library that simplifies the development of Linux DRM/KMS applications.\n\nWith SRM, you can focus on the OpenGL ES 2.0 logic of your application. For each available display, you can start a rendering thread that triggers common events like **initializeGL()**, **paintGL()**, **resizeGL()**, **pageFlipped()** and **uninitializeGL()**.\n\nIt also ensures textures can be rendered across all screens, even if they are attached to different GPUs.\n\n### Links\n\n* [📖 C API Documentation](https://cuarzosoftware.github.io/SRM/topics.html)\n* [🎓 Tutorial](https://cuarzosoftware.github.io/SRM/tutorial_page.html)\n* [🕹️ Examples](https://cuarzosoftware.github.io/SRM/examples_page.html)\n* [📦 Downloads](https://cuarzosoftware.github.io/SRM/downloads_page.html)\n* [⚙️ Environment](https://cuarzosoftware.github.io/SRM/envs_page.html)\n* [💬 Contact](https://cuarzosoftware.github.io/SRM/contact_page.html)\n\n### Used By\n\nSRM is the main graphic backend used by the [Louvre C++ Wayland Library](https://github.com/CuarzoSoftware/Louvre), as depicted in the image below.\n\n![Louvre Example](https://lh3.googleusercontent.com/pw/AIL4fc9VCmbRMl7f4ibvQqDrWpmLkXJ9W3MHHWKKE7g5oKcYSIrOut0mQEb1sDoblm9h35zUXk5zhwOwlWnM-soCtjeznhmA7yfRNqo-5a3PdwNYapM1vn4=w2400)\n\n### Features\n\n* Support for multi-GPU setups\n* Automatic configuration of GPUs and connectors\n* Texture allocation from main memory, DMA Buffers, GBM BOs and Wayland DRM Buffers\n* Multi-session capability (e.g. can use [libseat](https://github.com/kennylevinsen/seatd) to open DRM devices)\n* Listener for connectors hot-plugging events\n* Simple cursor planes control\n* V-Sync control (support for the atomic DRM API requires Linux \u003e= 6.8)\n* Framebuffer damage (enhances performance in multi-GPU setups where DMA support is lacking)\n* Access to screen framebuffers as textures\n* Direct Scanout (primary plane)\n* Support for double and triple buffering\n* Gamma correction\n\n### Tested on\n\n* Intel GPUs (i915 driver)\n* NVIDIA GPUs (Nouveau \u0026 proprietary)\n* AMD GPUs (AMDGPU driver)\n* Mali GPUs (Lima driver)\n\n### Rendering Modes\n\nIn multi-GPU setups, connectors such as eDP, HDMI, DisplayPort, etc., can be attached to different GPUs. However, not all GPUs may support texture sharing, which could prevent, for example, a compositor from dragging a window across screens. Therefore, SRM assigns one of the following rendering modes to each GPU to ensure textures can always be rendered on all displays:\n\n**SELF MODE**: If there's a single GPU or one that can handle all buffer formats from the allocator device, it directly renders into its own connectors, which is the best case.\n\n**PRIME MODE**: When there are multiple GPUs and one cannot support all formats from the allocator, another GPU handles the rendering into a DMA-importable buffer, which is then blitted into the screen framebuffer. This approach is fast but not optimal, as only one GPU performs the \"heavy\" rendering.\n\n**DUMB MODE**: In setups with multiple GPUs where one can't import buffers but supports [dumb buffers](https://manpages.debian.org/testing/libdrm-dev/drm-memory.7.en.html), another GPU handles rendering and copies the result to dumb buffers for direct scanout. This is sub-optimal as only one GPU does the rendering and involves a GPU-to-CPU copy.\n\n**CPU MODE**: Similar to the previous case, but when dumb buffers are not supported, the rendered content is copied to main memory. Then, a texture supported by the affected GPU is updated from the pixel data and rendered into the screen framebuffer. This is the worst-case scenario as it involves CPU copying for both reading and uploading to GPUs.\n\nPerformance in the last three modes can be significantly improved by specifying the damage generated during a `paintGL()` event using `srmConnectorSetBufferDamage()`.\n\n### Basic Example\n\n```c\n#include \u003cSRMCore.h\u003e\n#include \u003cSRMDevice.h\u003e\n#include \u003cSRMConnector.h\u003e\n#include \u003cSRMConnectorMode.h\u003e\n#include \u003cSRMListener.h\u003e\n\n#include \u003cSRMList.h\u003e\n#include \u003cSRMLog.h\u003e\n\n#include \u003cGLES2/gl2.h\u003e\n\n#include \u003cmath.h\u003e\n#include \u003cfcntl.h\u003e\n#include \u003cunistd.h\u003e\n\nfloat color = 0.f;\n\n/* Opens a DRM device */\nstatic int openRestricted(const char *path, int flags, void *userData)\n{\n    SRM_UNUSED(userData);\n\n    // Here something like libseat could be used instead\n    return open(path, flags);\n}\n\n/* Closes a DRM device */\nstatic void closeRestricted(int fd, void *userData)\n{\n    SRM_UNUSED(userData);\n    close(fd);\n}\n\nstatic SRMInterface srmInterface =\n{\n    .openRestricted = \u0026openRestricted,\n    .closeRestricted = \u0026closeRestricted\n};\n\nstatic void initializeGL(SRMConnector *connector, void *userData)\n{\n    SRM_UNUSED(userData);\n\n    /* You must not do any drawing here as it won't make it to\n     * the screen. */\n\n    SRMConnectorMode *mode = srmConnectorGetCurrentMode(connector);\n\n    glViewport(0, \n               0, \n               srmConnectorModeGetWidth(mode), \n               srmConnectorModeGetHeight(mode));\n\n    // Schedule a repaint (this eventually calls paintGL() later, not directly)\n    srmConnectorRepaint(connector);\n}\n\nstatic void paintGL(SRMConnector *connector, void *userData)\n{\n    SRM_UNUSED(userData);\n\n    glClearColor((sinf(color) + 1.f) / 2.f,\n                 (sinf(color * 0.5f) + 1.f) / 2.f,\n                 (sinf(color * 0.25f) + 1.f) / 2.f,\n                 1.f);\n\n    color += 0.01f;\n\n    if (color \u003e M_PI*4.f)\n        color = 0.f;\n\n    glClear(GL_COLOR_BUFFER_BIT);\n    srmConnectorRepaint(connector);\n}\n\nstatic void resizeGL(SRMConnector *connector, void *userData)\n{\n    /* You must not do any drawing here as it won't make it to\n     * the screen.\n     * This is called when the connector changes its current mode,\n     * set with srmConnectorSetMode() */\n\n    // Reuse initializeGL() as it only sets the viewport\n    initializeGL(connector, userData);\n}\n\nstatic void pageFlipped(SRMConnector *connector, void *userData)\n{\n    SRM_UNUSED(connector);\n    SRM_UNUSED(userData);\n\n    /* You must not do any drawing here as it won't make it to\n     * the screen.\n     * This is called when the last rendered frame is now being\n     * displayed on screen.\n     * Google v-sync for more info. */\n}\n\nstatic void uninitializeGL(SRMConnector *connector, void *userData)\n{\n    SRM_UNUSED(connector);\n    SRM_UNUSED(userData);\n\n    /* You must not do any drawing here as it won't make it to\n     * the screen.\n     * Here you should free any resource created on initializeGL()\n     * like shaders, programs, textures, etc. */\n}\n\nstatic SRMConnectorInterface connectorInterface =\n{\n    .initializeGL = \u0026initializeGL,\n    .paintGL = \u0026paintGL,\n    .resizeGL = \u0026resizeGL,\n    .pageFlipped = \u0026pageFlipped,\n    .uninitializeGL = \u0026uninitializeGL\n};\n\nstatic void connectorPluggedEventHandler(SRMListener *listener, SRMConnector *connector)\n{\n    SRM_UNUSED(listener);\n\n    /* This is called when a new connector is avaliable (E.g. Plugging an HDMI display). */\n\n    /* Got a new connector, let's render on it */\n    if (!srmConnectorInitialize(connector, \u0026connectorInterface, NULL))\n        SRMError(\"[srm-basic] Failed to initialize connector %s.\",\n                 srmConnectorGetModel(connector));\n}\n\nstatic void connectorUnpluggedEventHandler(SRMListener *listener, SRMConnector *connector)\n{\n    SRM_UNUSED(listener);\n    SRM_UNUSED(connector);\n\n    /* This is called when a connector is no longer avaliable (E.g. Unplugging an HDMI display). */\n\n    /* The connnector is automatically uninitialized after this event (if initialized)\n     * so calling srmConnectorUninitialize() here is not required. */\n}\n\nint main(void)\n{\n    SRMCore *core = srmCoreCreate(\u0026srmInterface, NULL);\n\n    if (!core)\n    {\n        SRMFatal(\"[srm-basic] Failed to initialize SRM core.\");\n        return 1;\n    }\n\n    // Subscribe to Udev events\n    SRMListener *connectorPluggedEventListener = srmCoreAddConnectorPluggedEventListener(core, \u0026connectorPluggedEventHandler, NULL);\n    SRMListener *connectorUnpluggedEventListener = srmCoreAddConnectorUnpluggedEventListener(core, \u0026connectorUnpluggedEventHandler, NULL);\n\n    // Find and initialize avaliable connectors\n\n    // Loop each GPU (device)\n    SRMListForeach (deviceIt, srmCoreGetDevices(core))\n    {\n        SRMDevice *device = srmListItemGetData(deviceIt);\n\n        // Loop each GPU connector (screen)\n        SRMListForeach (connectorIt, srmDeviceGetConnectors(device))\n        {\n            SRMConnector *connector = srmListItemGetData(connectorIt);\n\n            if (srmConnectorIsConnected(connector))\n            {\n                if (!srmConnectorInitialize(connector, \u0026connectorInterface, NULL))\n                    SRMError(\"[srm-basic] Failed to initialize connector %s.\",\n                             srmConnectorGetModel(connector));\n            }\n        }\n    }\n\n    while (1)\n    {\n        /* Udev monitor poll DRM devices/connectors hotplugging events (-1 disables timeout).\n         * To get a pollable FD use srmCoreGetMonitorFD() */\n\n        if (srmCoreProcessMonitor(core, -1) \u003c 0)\n            break;\n    }\n\n    /* Unsubscribe to DRM events\n     *\n     * These listeners are automatically destroyed when calling srmCoreDestroy()\n     * so there is no need to free them manually.\n     * This is here just to show how to unsubscribe to events on the fly. */\n\n    srmListenerDestroy(connectorPluggedEventListener);\n    srmListenerDestroy(connectorUnpluggedEventListener);\n\n    // Finish SRM\n    srmCoreDestroy(core);\n\n    return 0;\n}\n```\n","funding_links":["https://github.com/sponsors/CuarzoSoftware"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcuarzosoftware%2Fsrm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcuarzosoftware%2Fsrm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcuarzosoftware%2Fsrm/lists"}