Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Failed to activate OpenGL: the handle is invalid  (Read 3686 times)

0 Members and 1 Guest are viewing this topic.

Pucci

  • Newbie
  • *
  • Posts: 4
    • View Profile
    • Email
Failed to activate OpenGL: the handle is invalid
« on: March 09, 2022, 07:07:34 pm »
Hi everyone,
I'm writing a code that should show more than a window at a time and, since each update of the windows is inside a while(window.isOpen()) loop, the only way I know to do it is with threads (I never used threads before and I'm using std::thread as suggested in tutorials).
The program run as it should and everything works fine, but I noticed that I need to close the opened windows in reverse order (from the last opened to the first) otherwise this message appears twice in the cmd:
Failed to activate OpenGL context: Handle non valido.
(I'm on italian windows 11)

The creation and update of each window is made in a single thread.

I think the problem could be on the variable bool _interact or std::vector sciame (which are the only shared resources between the two windows) or in the way I draw cicles in function plot (a single object moved and drawn rather than one object for each circle), but I don't know how to fix it.
Thanks a lot in advance.

Here is a minimal version of my code (The full version of the code can be found in https://github.com/sistemicomplessi/Kuramoto):

main.cpp
int main() {
  Firefly::setWindowDim(1000,700);
  Firefly::setK(10);

  std::vector<Firefly> sciame;
  for (int i = 0; i < 1000; i++) {
    sciame.push_back(Firefly(Distribution::Lorentz,1,0.5));
  }
 
  //Draw window, plot window and evolve done in separeted threads, passing sciame as reference
  std::thread dThread(&Firefly::draw, std::ref(sciame));  
  std::thread pThread(&Firefly::plot, std::ref(sciame));
  std::thread eThread(&Firefly::evolve, std::ref(sciame), true);

  //wait for threads to terminate before proceding
  dThread.join();
  pThread.join();
  Firefly::stopEvolve();
  eThread.join();
  return 0;
}
 


firefly.h (public inheritance from class oscillator, but I don't think it's important here)
#ifndef FIREFLY_H
#define FIREFLY_H

#include "oscillator.h"
#include <SFML/Graphics.hpp>

class Firefly : public Oscillator {
  static double _K;               //coupling strength between firefly
  static sf::Vector2f _windowDim; //dimension (pixels, (x,y)) of the grid where fireflies will be placed
  static bool _interaction;       //toggle interaction between fireflies
  static bool _evolve;            //to decide when to stop evolving
  sf::Vector2f _position;         //position of firefly on the window

  //makes the firefly (its oscillator) interact with the others
  void interact(std::vector<Firefly>& system, double dt);

 public:
  Firefly(Distribution dist = Distribution::Lorentz, double mean = 1, double param = 998, sf::Vector2f position = sf::Vector2f(-1,-1));
 
  //get and set functions:
  static void setK(double K) { _K = K; }
  static void setWindowDim(int x, int y) { _windowDim = sf::Vector2f(x,y)); }
  sf::Vector2f position() { return _position; }

  //calls update and interact for each element of the vector
  static void evolve(std::vector<Firefly>& syst, bool saveData = true);

  //break out from evolve loop
  static void stopEvolve() { _evolve = false; }

  //draw the system
  static void draw(std::vector<Firefly>& syst);

  //plot the oscillators in a complex plane
  static void plot(std::vector<Firefly>& syst);
};

#endif  //FIREFLY_H


firefly.cpp
#include "firefly.h"
#include <iostream>
#include <random>
#include <algorithm>
#include <fstream>

sf::Vector2f Firefly::_windowDim = sf::Vector2f(1000, 700);
double Firefly::_K = 1;
bool Firefly::_interaction = false;
bool Firefly::_evolve = true;

void Firefly::interact(std::vector<Firefly>& system, double dt) {
  // do something (i think it's not important here)
}

Firefly::Firefly(Distribution dist, double mean, double param, sf::Vector2f position) : Oscillator(dist, mean, param), _position{position} {
  if (position >= _windowDim) {
    std::cerr << "WARN (13): position value is too high for the set window dimensions: using a random position instead.\n";
    std::cerr << "Use Firefly::setWindowDim static function if you want to change the window dimensions\n";
    position = sf::Vector2f(-1,-1);
  }
  if (position == sf::Vector2f(-1,-1)) {   //default value -> random setting
    std::random_device seed;
    std::uniform_int_distribution<int> distX(0, _windowDim.x);
    std::uniform_int_distribution<int> distY(0, _windowDim.y);
    _position.x = distX(seed);
    _position.y = distY(seed);
  }
}

void Firefly::evolve(std::vector<Firefly>& syst, bool saveData) {
  int size = syst.size();
  sf::Clock clock;

  double time = 0;
  std::fstream fout("r-t data.txt", std::ios::out);

  double dt = 0;
  while(_evolve) {
    dt = clock.restart().asSeconds();
    for (int i = 0; i < size; i++) {
      //update
      syst[i].Oscillator::update(dt);
    }

    if (_interaction) {
      for (int i = 0; i < size; i++) {
        syst[i].interact(syst, dt);
      }

      if (saveData) {
        std::cout << "Saving data\n";
        fout << time << '\t' << moduleOrderParameter(syst) << '\n';
        time += dt;
      }
    }
  } //end while
 
  fout.close();
}

void Firefly::draw(std::vector<Firefly>& syst) {
  int drawSize = 5;

  //load font
  sf::Font arial;
  if (!arial.loadFromFile("arial.ttf"))
    std::cerr << "ERR(21): Couldn't load font. Check if font is present in working folder.\n";
 
  //variables for events managing
  bool showOff = false;     //show/hide not-flashing (off) fireflies
  double addFrequency = 1;  //frequency of the new added firefly

  sf::RenderWindow window(sf::VideoMode(_windowDim.x, _windowDim.y), "Fireflies");
  window.setFramerateLimit(30);   //less lag

  //main loop (or game loop)
  while (window.isOpen())
  {
    //reacts to Events
    sf::Event event;
    while (window.pollEvent(event))
    {
      if (event.type == sf::Event::Closed)
        window.close();

      if (event.type == sf::Event::Resized) {
        //view changes with window. Doing so, instead of squeezing/stretchin things, more things will be shown when resizing.
        sf::FloatRect visibleArea(0, 0, event.size.width, event.size.height);
        window.setView(sf::View(visibleArea));
       
        //Let the place where fireflies spawn be the same of the window
        Firefly::setWindowDim(event.size.width, event.size.height);
      }

      if (event.type == sf::Event::KeyPressed) {
        //S : show/hide not-flashing fireflies
        if (event.key.code == sf::Keyboard::S)  
          showOff = !showOff;

        //I : toggle interraction
        if (event.key.code == sf::Keyboard::I)
        {
          _interaction = !_interaction;
        }
      }
    } //end react to events

    window.clear(); //clear with default color (black)

    //draw fireflies
    int N = syst.size();
    for (int i = 0; i < N; i++) {
      sf::CircleShape circle(drawSize);
      circle.setPosition(syst[i].position());

      if (std::cos(syst[i].phase()) > 0.9) {
        circle.setFillColor(sf::Color::Yellow);
        window.draw(circle);
      }
      else if (showOff) {
        circle.setFillColor(sf::Color(120,120,120));  //gray
        window.draw(circle);
      }
    }

    //draw dinamic (changing) text
    sf::String dString = "\n\n\n\nPress 'I' to toggle interaction between fireflies (" + std::to_string(_interaction) + ')';
    sf::Text dText(dString, arial,12);
    window.draw(dText);

    //refresh display
    window.display();
  }
}

void Firefly::plot(std::vector<Firefly>& syst) {
  int windowSize = 500;
  int radius = 1;
  int dim = windowSize + radius*2;

  //create window
  sf::RenderWindow window(sf::VideoMode(dim, dim), "Plot");
  window.setFramerateLimit(30); //less lag

  //load font
  sf::Font arial;
  if (!arial.loadFromFile("arial.ttf"))
    std::cerr << "ERR(32): Couldn't load font. Check if font is present in working folder.\n";

  //setting background
  sf::RectangleShape xAxis(sf::Vector2f(dim,1));
  xAxis.setPosition(0, dim/2);
  xAxis.setFillColor(sf::Color(130,130,130));
  sf::RectangleShape yAxis(sf::Vector2f(1,dim));
  yAxis.setPosition(dim/2, 0);
  yAxis.setFillColor(sf::Color(130,130,130));
  sf::Text xText("cos θ", arial, 12);
  xText.setPosition(sf::Vector2f(dim-30, dim/2 + 10));
  xText.setFillColor(sf::Color::Black);
  sf::Text yText("sin θ", arial, 12);
  yText.setPosition(sf::Vector2f(dim/2 -10, 30));
  yText.setFillColor(sf::Color::Black);
  yText.rotate(270);

  //circle for fireflies
  sf::CircleShape circle(radius);
  circle.setFillColor(sf::Color::Black);  

  //main loop (or game loop)
  while (window.isOpen())
  {
    //reacts to Events
    sf::Event event;
    while (window.pollEvent(event))
    {
      if (event.type == sf::Event::Closed)
        window.close();
      if (event.type == sf::Event::KeyPressed) {
        if (event.key.code == sf::Keyboard::I)
        {
          _interaction = !_interaction;
        }
      }
    }

    window.clear(sf::Color::White);
    window.draw(xAxis);
    window.draw(yAxis);
    window.draw(xText);
    window.draw(yText);

    //draw oscillators
    int size = syst.size();
    for (int i = 0; i < size; i++) {
      double phase = syst[i].phase();
      circle.setPosition( (std::cos(phase)+1)*windowSize/2 , (std::sin(phase)+1)*windowSize/2 );
      window.draw(circle);
    }

    //draw text
    sf::Text text("Order parameter: |r| = " + std::to_string(Firefly::moduleOrderParameter(syst)),arial,12);
    text.setFillColor(sf::Color::Black);
    window.draw(text);

    //draw arrow (r)
    sf::RectangleShape line(sf::Vector2f(moduleOrderParameter(syst)*dim/2, 2));
    line.setPosition(dim/2, dim/2 +1);
    line.setFillColor(sf::Color::Black);
    line.rotate(angleOrderParameter(syst));
    window.draw(line);

    //refresh display
    window.display();
  }

}
 
« Last Edit: March 09, 2022, 07:14:53 pm by Pucci »

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11030
    • View Profile
    • development blog
    • Email
Re: Failed to activate OpenGL: the handle is invalid
« Reply #1 on: March 10, 2022, 11:08:03 am »
Keep in mind that any shared resources (i.e. any variable, class instance, etc that you read and write to from multiple threads) need to be protected by a mutex or similar. You also need to have an understanding how threads a scheduled and how race conditions can arise. Doing multi-threading is an advanced topic and needs some experience, so make sure it's really what you need.

Didn't exactly go through the whole code, but the error usually occurs when you try to call SFML drawing functions, which use OpenGL underneath, but you haven't activated/deactivated the context on the new thread/old thread.
There can always just be one active OpenGL context per window and thread. So you need to setActive(false) on the thread you're no longer drawing in and setActive(true) on the thread you're trying to draw next.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Pucci

  • Newbie
  • *
  • Posts: 4
    • View Profile
    • Email
Re: Failed to activate OpenGL: the handle is invalid
« Reply #2 on: March 12, 2022, 09:05:29 am »
Thanks a lot for the advices on thread, and I kind of experienced how high level stuff they are. But how can I draw more than a window without using them, considering that each window must have its main/game loop? Are there other options?

fallahn

  • Hero Member
  • *****
  • Posts: 507
  • Buns.
    • View Profile
    • Trederia
Re: Failed to activate OpenGL: the handle is invalid
« Reply #3 on: March 12, 2022, 11:18:03 am »
If you're going to require a thread for each window then the next best thing is to try and isolate the threads as much as possible, so that you don't need to share (memory space) data between them. X Windows (https://en.wikipedia.org/wiki/X_Window_System) uses a client/server architecture so you might gain something by that approach.

Run the logic of your application in its own thread, and use it to create a 'server'. You can then create individual threads for each window you need, completely self containing the drawing and event handling, and then communicate with the server thread using a library such as Enet (http://enet.bespin.org/). Forward events from your window to the server, process them, then have the server broadcast the results to all connected windows (clients) for them to draw.

In this situation you also get the added bonus that your main logic processing is effectively run in a separate thread to graphics and rendering.

Will this introduce lag? Yes, some, but if it's all running on a local machine it will likely be negligible. Depending on your application, it will be fine, for example if you're running some kind of graphing simulation. Turn based games will probably work very well too (I know this from experience 8) https://fallahn.itch.io/vga-golf ). For fast action games there are tried and tested methods which can be implemented to negate perceivable lag, should it be deemed necessary:
https://www.gabrielgambetta.com/client-server-game-architecture.html
https://gafferongames.com/categories/networked-physics/

HTH