Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RPi 4 multiple display support #2

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions demos/tests/1080p60-HDMI1+2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
'use strict';
// attempts to create two instances of amino, one on each screen
// requires a Pi 4 with two HDMI displays connected

const amino = require('../../main.js');
function make1() { // 1
//create instance
const gfx = new amino.AminoGfx({
resolution: '1080p@60',

//multi-display support
display: 'HDMI-A-1' //Pi 4: HDMI 2
});

gfx.start(function (err) {
if (err) {
console.log('Amino error: ' + err.message);
return;
}

this.fill('#FF0000');

//some info
console.log('#1 screen: ' + JSON.stringify(gfx.screen));
console.log('#1 window size: ' + this.w() + 'x' + this.h());
console.log('#1 runtime: ' + JSON.stringify(gfx.runtime));

console.log("screen #1 intitialized, starting 2...");
make2();
});
}
function make2() { // 2
//create instance
const gfx = new amino.AminoGfx({
resolution: '1080p@60',

//multi-display support
display: 'HDMI-A-2' //Pi 4: HDMI 2
});

gfx.start(function (err) {
if (err) {
console.log('Amino error: ' + err.message);
return;
}

this.fill('#00FF00');

//some info
console.log('#2 screen: ' + JSON.stringify(gfx.screen));
console.log('#2 window size: ' + this.w() + 'x' + this.h());
console.log('#2 runtime: ' + JSON.stringify(gfx.runtime));
});
}

make1();
134 changes: 81 additions & 53 deletions src/rpi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,22 +85,26 @@ void AminoGfxRPi::setup() {
bcm_host_init();

#ifdef EGL_GBM
if (DEBUG_GLES) {
printf("-> initializing OpenGL driver\n");
}
if (!driDevice) {
if (DEBUG_GLES) {
printf("-> initializing OpenGL driver\n");
}

//TODO how to use the dual display output?
//TODO how to use the dual display output?

//access OpenGL driver (available if OpenGL driver is loaded)
//Note: Pi0-3 use /dev/dri/card0
std::string devicePath = "/dev/dri/card1";
//access OpenGL driver (available if OpenGL driver is loaded)
//Note: Pi0-3 use /dev/dri/card0
std::string devicePath = "/dev/dri/card1";

driDevice = open(devicePath.c_str(), O_RDWR | O_CLOEXEC);
driDevice = open(devicePath.c_str(), O_RDWR | O_CLOEXEC);

assert(driDevice > 0);
assert(driDevice > 0);

if (DEBUG_GLES) {
printf("-> DRI device ready: %s\n", devicePath.c_str());
if (DEBUG_GLES) {
printf("-> DRI device ready: %s\n", devicePath.c_str());
}
} else {
printf("using existing driDevice\n");
}
#endif
//Note: tvservice and others are already initialized by bcm_host_init() call!
Expand All @@ -122,36 +126,41 @@ void AminoGfxRPi::setup() {
//show info screen (Note: seems not to work!)
//vc_tv_show_info(1);

//handle preferred resolution
if (!createParams.IsEmpty()) {
v8::Local<v8::Object> obj = Nan::New(createParams);
glESInitialized = true;
} else {
printf("glES already initialized, skipping\n");
}

//handle preferred resolution
if (!createParams.IsEmpty()) {
v8::Local<v8::Object> obj = Nan::New(createParams);

//resolution
Nan::MaybeLocal<v8::Value> resolutionMaybe = Nan::Get(obj, Nan::New<v8::String>("resolution").ToLocalChecked());
//resolution
Nan::MaybeLocal<v8::Value> resolutionMaybe = Nan::Get(obj, Nan::New<v8::String>("resolution").ToLocalChecked());

if (!resolutionMaybe.IsEmpty()) {
v8::Local<v8::Value> resolutionValue = resolutionMaybe.ToLocalChecked();
if (!resolutionMaybe.IsEmpty()) {
v8::Local<v8::Value> resolutionValue = resolutionMaybe.ToLocalChecked();

if (resolutionValue->IsString()) {
prefRes = AminoJSObject::toString(resolutionValue);
if (resolutionValue->IsString()) {
prefRes = AminoJSObject::toString(resolutionValue);

#ifdef EGL_DISPMANX
//change resolution
if (prefRes == "720p@24") {
forceHdmiMode(HDMI_CEA_720p24);
} else if (prefRes == "720p@25") {
forceHdmiMode(HDMI_CEA_720p25);
} else if (prefRes == "720p@30") {
forceHdmiMode(HDMI_CEA_720p30);
} else if (prefRes == "720p@50") {
forceHdmiMode(HDMI_CEA_720p50);
} else if (prefRes == "720p@60") {
forceHdmiMode(HDMI_CEA_720p60);
} else if (prefRes == "1080p@24") {
forceHdmiMode(HDMI_CEA_1080p24);
} else if (prefRes == "1080p@25") {
forceHdmiMode(HDMI_CEA_1080p25);
} else if (prefRes == "1080p@30") {
//change resolution
if (prefRes == "720p@24") {
forceHdmiMode(HDMI_CEA_720p24);
} else if (prefRes == "720p@25") {
forceHdmiMode(HDMI_CEA_720p25);
} else if (prefRes == "720p@30") {
forceHdmiMode(HDMI_CEA_720p30);
} else if (prefRes == "720p@50") {
forceHdmiMode(HDMI_CEA_720p50);
} else if (prefRes == "720p@60") {
forceHdmiMode(HDMI_CEA_720p60);
} else if (prefRes == "1080p@24") {
forceHdmiMode(HDMI_CEA_1080p24);
} else if (prefRes == "1080p@25") {
forceHdmiMode(HDMI_CEA_1080p25);
} else if (prefRes == "1080p@30") {
forceHdmiMode(HDMI_CEA_1080p30);
} else if (prefRes == "1080p@50") {
forceHdmiMode(HDMI_CEA_1080p50);
Expand All @@ -174,11 +183,10 @@ void AminoGfxRPi::setup() {
prefDisp = AminoJSObject::toString(displayValue);
}
}
} else {
printf("no createParams\n");
}

glESInitialized = true;
}

//instance
addInstance();

Expand All @@ -199,7 +207,11 @@ void AminoGfxRPi::initEGL() {

#ifdef EGL_GBM
//create GBM device (Note: fails if not enough rights e.g. no root access)
displayType = gbm_create_device(driDevice);
if (!displayType) {
displayType = gbm_create_device(driDevice);
} else if (DEBUG_GLES) {
printf("using existing displayType\n");
}

if (!displayType) {
printf("Could not create the GBM device! Please check your permissions (e.g. run with root privileges).\n");
Expand All @@ -212,24 +224,31 @@ void AminoGfxRPi::initEGL() {
}
#endif

EGLBoolean res;
//get an EGL display connection
display = eglGetDisplay(displayType);
if (display == EGL_NO_DISPLAY) {
display = eglGetDisplay(displayType);

assert(display != EGL_NO_DISPLAY);
assert(display != EGL_NO_DISPLAY);

if (DEBUG_GLES) {
printf("-> got EGL display\n");
}
if (DEBUG_GLES) {
printf("-> got EGL display\n");
}

//initialize the EGL display connection
EGLBoolean res = eglInitialize(display, NULL, NULL);
//initialize the EGL display connection
res = eglInitialize(display, NULL, NULL);

assert(res != EGL_FALSE);
assert(res != EGL_FALSE);

if (DEBUG_GLES) {
printf("-> EGL initialized\n");
if (DEBUG_GLES) {
printf("-> EGL initialized\n");
}
} else if (DEBUG_GLES) {
printf("using existing EGL display\n");
}



//get an appropriate EGL frame buffer configuration
static const EGLint attribute_list[] = {
//RGBA
Expand Down Expand Up @@ -398,6 +417,7 @@ void AminoGfxRPi::initEGL() {
drmModeConnector *connector2 = drmModeGetConnector(driDevice, resources->connectors[i]);

if (!connector2) {
printf("warning: failed to get connector #%d, skipping\n", i);
continue;
}

Expand All @@ -413,19 +433,23 @@ void AminoGfxRPi::initEGL() {

if (DEBUG_GLES) {
//debug
//printf("-> checking display %s %s\n", name.c_str(), prefDisp.c_str());
printf("-> checking display %s %s\n", name.c_str(), prefDisp.c_str());

if (match) {
printf("-> using display %s\n", prefDisp.c_str());
printf("-> using matched display %s\n", prefDisp.c_str());
}
}
} else if (DEBUG_GLES) {
printf("no prefDisp, using %s\n", name.c_str());
}

if (match) {
//Note: have to free instance later
connector = connector2;
break;
}
} else if (DEBUG_GLES) {
printf("warning: connector #%d invalid, skipping\n", i);
}

drmModeFreeConnector(connector2);
Expand Down Expand Up @@ -1914,7 +1938,11 @@ void AminoGfxRPi::destroyEGLImageHandler(AsyncValueUpdate *update, int state) {

//static initializers
bool AminoGfxRPi::glESInitialized = false;

NativeDisplayType AminoGfxRPi::displayType = 0;
EGLDisplay AminoGfxRPi::display = EGL_NO_DISPLAY;
#ifdef EGL_GBM
int AminoGfxRPi::driDevice = 0;
#endif
#ifdef EGL_DISPMANX
sem_t AminoGfxRPi::resSem;
bool AminoGfxRPi::resSemValid = false;
Expand Down
6 changes: 3 additions & 3 deletions src/rpi.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ class AminoGfxRPi : public AminoGfx {
static bool glESInitialized;

//OpenGL ES
NativeDisplayType displayType = EGL_DEFAULT_DISPLAY;
EGLDisplay display = EGL_NO_DISPLAY;
static NativeDisplayType displayType;
static EGLDisplay display;
EGLContext context = EGL_NO_CONTEXT;
EGLSurface surface = EGL_NO_SURFACE;
EGLConfig config;
Expand All @@ -60,7 +60,7 @@ class AminoGfxRPi : public AminoGfx {

#ifdef EGL_GBM
//DRM/GBM
int driDevice = 0;
static int driDevice;
uint32_t connector_id = 0;
drmModeModeInfo mode_info;
gbm_surface *gbmSurface = NULL;
Expand Down