Managing to get GBM working
This commit is contained in:
434
platform/gbm/gbm.c
Normal file
434
platform/gbm/gbm.c
Normal file
@ -0,0 +1,434 @@
|
||||
/**
|
||||
* Copyright (c) 2021 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
#include "gbm.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <xf86drm.h>
|
||||
#include <xf86drmMode.h>
|
||||
#include <gbm.h>
|
||||
|
||||
#define GL_GLEXT_PROTOTYPES 1
|
||||
#include <GLES2/gl2.h>
|
||||
#include <GLES2/gl2ext.h>
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
|
||||
|
||||
|
||||
static struct {
|
||||
EGLDisplay display;
|
||||
EGLConfig config;
|
||||
EGLContext context;
|
||||
EGLSurface surface;
|
||||
GLuint program;
|
||||
GLint modelviewmatrix, modelviewprojectionmatrix, normalmatrix;
|
||||
GLuint vbo;
|
||||
GLuint positionsoffset, colorsoffset, normalsoffset;
|
||||
} gl;
|
||||
|
||||
static struct {
|
||||
struct gbm_device *dev;
|
||||
struct gbm_surface *surface;
|
||||
} gbm;
|
||||
|
||||
static struct {
|
||||
int fd;
|
||||
drmModeModeInfo *mode;
|
||||
uint32_t crtc_id;
|
||||
uint32_t connector_id;
|
||||
} drm;
|
||||
|
||||
struct drm_fb {
|
||||
struct gbm_bo *bo;
|
||||
uint32_t fb_id;
|
||||
};
|
||||
|
||||
static uint32_t find_crtc_for_encoder(
|
||||
const drmModeRes *resources, const drmModeEncoder *encoder
|
||||
) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < resources->count_crtcs; i++) {
|
||||
/* possible_crtcs is a bitmask as described here:
|
||||
* https://dvdhrm.wordpress.com/2012/09/13/linux-drm-mode-setting-api
|
||||
*/
|
||||
const uint32_t crtc_mask = 1 << i;
|
||||
const uint32_t crtc_id = resources->crtcs[i];
|
||||
if(encoder->possible_crtcs & crtc_mask) {
|
||||
return crtc_id;
|
||||
}
|
||||
}
|
||||
|
||||
/* no match found */
|
||||
return -1;
|
||||
}
|
||||
|
||||
static uint32_t find_crtc_for_connector(
|
||||
const drmModeRes *resources, const drmModeConnector *connector
|
||||
) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < connector->count_encoders; i++) {
|
||||
const uint32_t encoder_id = connector->encoders[i];
|
||||
drmModeEncoder *encoder = drmModeGetEncoder(drm.fd, encoder_id);
|
||||
|
||||
if(encoder) {
|
||||
const uint32_t crtc_id = find_crtc_for_encoder(resources, encoder);
|
||||
|
||||
drmModeFreeEncoder(encoder);
|
||||
if(crtc_id != 0) {
|
||||
return crtc_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* no match found */
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static int32_t drmInit(void) {
|
||||
drmModeRes *resources;
|
||||
drmModeConnector *connector = NULL;
|
||||
drmModeEncoder *encoder = NULL;
|
||||
int i, area;
|
||||
|
||||
drm.fd = open("/dev/dri/card0", O_RDWR);
|
||||
|
||||
if(drm.fd < 0) {
|
||||
printf("could not open drm device\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
resources = drmModeGetResources(drm.fd);
|
||||
if(!resources) {
|
||||
printf("drmModeGetResources failed: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Find an existing connected connector.
|
||||
for (i = 0; i < resources->count_connectors; i++) {
|
||||
connector = drmModeGetConnector(drm.fd, resources->connectors[i]);
|
||||
if(connector->connection == DRM_MODE_CONNECTED) {
|
||||
break;
|
||||
}
|
||||
drmModeFreeConnector(connector);
|
||||
connector = NULL;
|
||||
}
|
||||
|
||||
// Did we find a connector?
|
||||
if(!connector) {
|
||||
printf("No connected connector!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Now get the mode to use, based on resolution.
|
||||
for (i = 0, area = 0; i < connector->count_modes; i++) {
|
||||
drmModeModeInfo *current_mode = &connector->modes[i];
|
||||
|
||||
if(current_mode->type & DRM_MODE_TYPE_PREFERRED) drm.mode = current_mode;
|
||||
|
||||
int current_area = current_mode->hdisplay * current_mode->vdisplay;
|
||||
if(current_area >= area) {
|
||||
drm.mode = current_mode;
|
||||
area = current_area;
|
||||
}
|
||||
}
|
||||
|
||||
// Did we find the best mode?
|
||||
if(!drm.mode) {
|
||||
printf("could not find mode!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Now find a display encoder
|
||||
for (i = 0; i < resources->count_encoders; i++) {
|
||||
encoder = drmModeGetEncoder(drm.fd, resources->encoders[i]);
|
||||
if(encoder->encoder_id == connector->encoder_id) break;
|
||||
drmModeFreeEncoder(encoder);
|
||||
encoder = NULL;
|
||||
}
|
||||
|
||||
// Is there an encoder?
|
||||
if(encoder) {
|
||||
drm.crtc_id = encoder->crtc_id;
|
||||
} else {
|
||||
// No, use a CRTC
|
||||
uint32_t crtc_id = find_crtc_for_connector(resources, connector);
|
||||
if(crtc_id == 0) {
|
||||
printf("no crtc found!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
drm.crtc_id = crtc_id;
|
||||
}
|
||||
|
||||
// Update the DRM with the new connector ID.
|
||||
drm.connector_id = connector->connector_id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t gbmInit(void) {
|
||||
gbm.dev = gbm_create_device(drm.fd);
|
||||
|
||||
gbm.surface = gbm_surface_create(
|
||||
gbm.dev,
|
||||
drm.mode->vdisplay, drm.mode->vdisplay,
|
||||
GBM_FORMAT_XRGB8888,
|
||||
GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING
|
||||
);
|
||||
if(!gbm.surface) {
|
||||
printf("failed to create gbm surface\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t glInit(void) {
|
||||
EGLint major, minor, n;
|
||||
GLuint vertex_shader, fragment_shader;
|
||||
GLint ret;
|
||||
|
||||
// Set up the attributes for the EGL
|
||||
static const EGLint context_attribs[] = {
|
||||
EGL_CONTEXT_CLIENT_VERSION, 2,
|
||||
EGL_NONE
|
||||
};
|
||||
|
||||
static const EGLint config_attribs[] = {
|
||||
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
||||
EGL_RED_SIZE, 1,
|
||||
EGL_GREEN_SIZE, 1,
|
||||
EGL_BLUE_SIZE, 1,
|
||||
EGL_ALPHA_SIZE, 0,
|
||||
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
||||
EGL_NONE
|
||||
};
|
||||
|
||||
// Get the "display getter".
|
||||
PFNEGLGETPLATFORMDISPLAYEXTPROC getDisplay = NULL;
|
||||
getDisplay = (void *)eglGetProcAddress("eglGetPlatformDisplayEXT");
|
||||
if(getDisplay == NULL) {
|
||||
printf("Failed to get GBM-EGL Display.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Now get the display with that getter.
|
||||
gl.display = getDisplay(EGL_PLATFORM_GBM_KHR, gbm.dev, NULL);
|
||||
|
||||
// Initialize EGL
|
||||
if(!eglInitialize(gl.display, &major, &minor)) {
|
||||
printf("failed to initialize\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Bind OpenGL API
|
||||
if(!eglBindAPI(EGL_OPENGL_ES_API)) {
|
||||
printf("Failed to bind api\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!eglChooseConfig(gl.display, config_attribs, &gl.config, 1, &n) || n != 1) {
|
||||
printf("failed to choose config: %d\n", n);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Create a new EGL Context.
|
||||
gl.context = eglCreateContext(
|
||||
gl.display, gl.config, EGL_NO_CONTEXT, context_attribs
|
||||
);
|
||||
if(gl.context == NULL) {
|
||||
printf("failed to create context\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Create the EGL Surface to render to.
|
||||
gl.surface = eglCreateWindowSurface(gl.display, gl.config, gbm.surface, NULL);
|
||||
if(gl.surface == EGL_NO_SURFACE) {
|
||||
printf("failed to create egl surface\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Update the current EGL Context
|
||||
eglMakeCurrent(gl.display, gl.surface, gl.surface, gl.context);
|
||||
printf("GL Extensions: \"%s\"\n", glGetString(GL_EXTENSIONS));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void drm_fb_destroy_callback(struct gbm_bo *bo, void *data) {
|
||||
struct drm_fb *fb = data;
|
||||
struct gbm_device *gbm = gbm_bo_get_device(bo);
|
||||
|
||||
if(fb->fb_id) drmModeRmFB(drm.fd, fb->fb_id);
|
||||
free(fb);
|
||||
}
|
||||
|
||||
static struct drm_fb * drm_fb_get_from_bo(struct gbm_bo *bo) {
|
||||
struct drm_fb *fb = gbm_bo_get_user_data(bo);
|
||||
uint32_t width, height, stride, handle;
|
||||
int ret;
|
||||
|
||||
if(fb)
|
||||
return fb;
|
||||
|
||||
fb = calloc(1, sizeof *fb);
|
||||
fb->bo = bo;
|
||||
|
||||
width = gbm_bo_get_width(bo);
|
||||
height = gbm_bo_get_height(bo);
|
||||
stride = gbm_bo_get_stride(bo);
|
||||
handle = gbm_bo_get_handle(bo).u32;
|
||||
|
||||
ret = drmModeAddFB(drm.fd, width, height, 24, 32, stride, handle, &fb->fb_id);
|
||||
if(ret) {
|
||||
printf("failed to create fb: %s\n", strerror(errno));
|
||||
free(fb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback);
|
||||
return fb;
|
||||
}
|
||||
|
||||
static void page_flip_handler(
|
||||
int fd, unsigned int frame, unsigned int sec, unsigned int usec, void *data
|
||||
) {
|
||||
int *waiting_for_flip = data;
|
||||
*waiting_for_flip = 0;
|
||||
}
|
||||
|
||||
int32_t main(int argc, char *argv[]) {
|
||||
fd_set fds;
|
||||
drmEventContext evctx = {
|
||||
.version = DRM_EVENT_CONTEXT_VERSION,
|
||||
.page_flip_handler = page_flip_handler,
|
||||
};
|
||||
struct gbm_bo *bo;
|
||||
struct drm_fb *fb;
|
||||
int32_t ret;
|
||||
int32_t width, height;
|
||||
|
||||
// Init the DRM
|
||||
ret = drmInit();
|
||||
if(ret) {
|
||||
printf("failed to initialize DRM\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
// ???
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(0, &fds);
|
||||
FD_SET(drm.fd, &fds);
|
||||
|
||||
// Init the GBM
|
||||
ret = gbmInit();
|
||||
if(ret) {
|
||||
printf("failed to initialize GBM\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Now Initialize OpenGL/OpenGL ES
|
||||
ret = glInit();
|
||||
if(ret) {
|
||||
printf("failed to initialize EGL\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Do a once clear of the color buffer
|
||||
eglSwapBuffers(gl.display, gl.surface);
|
||||
|
||||
// Now uh?
|
||||
bo = gbm_surface_lock_front_buffer(gbm.surface);
|
||||
fb = drm_fb_get_from_bo(bo);
|
||||
width = gbm_bo_get_width(bo);
|
||||
height = gbm_bo_get_height(bo);
|
||||
stride = gbm_bo_get_stride(bo);
|
||||
|
||||
// ?
|
||||
ret = drmModeSetCrtc(
|
||||
drm.fd, drm.crtc_id, fb->fb_id, 0, 0, &drm.connector_id, 1, drm.mode
|
||||
);
|
||||
if(ret) {
|
||||
printf("failed to set mode: %s\n", strerror(errno));
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Init Game
|
||||
game_t *game = malloc(sizeof(game_t));
|
||||
printf("Game is %zu bytes.\n", sizeof(game_t));
|
||||
printf("Resolution is %i x %i x %i\n", width, height, stride);
|
||||
renderSetResolution(&game->engine.render, (int32_t)width, (int32_t)height);
|
||||
if(!gameInit(game)) {
|
||||
printf("Game Init Error\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
struct gbm_bo *next_bo;
|
||||
int waiting_for_flip = 1;
|
||||
|
||||
// Draw
|
||||
float fDelta = 0.016f;
|
||||
if(!gameUpdate(game, fDelta)) break;
|
||||
// Drawn
|
||||
|
||||
// Swap the EGL Buffers
|
||||
eglSwapBuffers(gl.display, gl.surface);
|
||||
|
||||
// Now do a back buffer flip
|
||||
next_bo = gbm_surface_lock_front_buffer(gbm.surface);
|
||||
fb = drm_fb_get_from_bo(next_bo);
|
||||
|
||||
// ?
|
||||
ret = drmModePageFlip(drm.fd, drm.crtc_id, fb->fb_id,
|
||||
DRM_MODE_PAGE_FLIP_EVENT, &waiting_for_flip
|
||||
);
|
||||
if(ret) {
|
||||
printf("Failed to queue page flip: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
// ?
|
||||
while(waiting_for_flip) {
|
||||
ret = select(drm.fd + 1, &fds, NULL, NULL, NULL);
|
||||
if(ret < 0) {
|
||||
printf("select err: %s\n", strerror(errno));
|
||||
return ret;
|
||||
} else if(ret == 0) {
|
||||
printf("select timeout!\n");
|
||||
return -1;
|
||||
} else if(FD_ISSET(0, &fds)) {
|
||||
printf("user interrupted!\n");
|
||||
break;
|
||||
}
|
||||
drmHandleEvent(drm.fd, &evctx);
|
||||
}
|
||||
|
||||
// ?
|
||||
gbm_surface_release_buffer(gbm.surface, bo);
|
||||
bo = next_bo;
|
||||
}
|
||||
|
||||
// Game has finished running, cleanup.
|
||||
gameDispose(game);
|
||||
free(game);
|
||||
|
||||
return ret;
|
||||
}
|
34
platform/gbm/gbm.h
Normal file
34
platform/gbm/gbm.h
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2021 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <xf86drm.h>
|
||||
#include <xf86drmMode.h>
|
||||
#include <gbm.h>
|
||||
|
||||
#define GL_GLEXT_PROTOTYPES 1
|
||||
#include <GLES2/gl2.h>
|
||||
#include <GLES2/gl2ext.h>
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
|
||||
#include <dawn/dawn.h>
|
||||
#include "../../src/display/render.h"
|
||||
#include "../../src/game/game.h"
|
||||
#include "../../src/input/input.h"
|
||||
int32_t main(int32_t argc, char *argv[]);
|
Reference in New Issue
Block a user