That sounds pretty interesting Groogy, but I've decided to keep it simple, at least for now, and just get rid of threads on the C++ side for my game engine.
Also the whole thing with scripts having direct access to rendering context is not an appropriate way to go.
They don't have direct access to a rendering context. In fact, threads that are ran from the scripting language will not be able to draw anything to the screen in my current concept of how things will work. I just want textures to be able to be loaded from threads. So, to achieve that, I'll make a call to SetActive(true) to see if a thread has an OpenGL context and create a valid OpenGL context for threads that don't have one when attempting to load textures.
Erhm, when I get to school I think I can have a solution for you that we went trough if you still want it to be multi threaded and also be compatible with older platforms. It's quite advanced and will require you to rewrite your whole structure but it is exactly what you are looking for. It will also handle the problem with SetActive.
What you do is divide everything up to different jobs with different dependencies. So running your "scripts" will be one job for instance.
... What you want to do is the [script] thread to generate output that is sent to whatever thread is handling the rendering...
For closure, since this forum thread is named "Handling SetActive efficiently in multithreaded environments" and I'm still interested (but don't plan on implementing it), I would like to understand what you're saying. This implies that I would want threads to be able to draw to the screen. Is there a way to,
without hindering single core processors, forward OpenGL commands to the main thread?
The following code is how I'd do it
with hindering single core processors through lock/unlock (SFML-2.0). It's quite a lot, but I wanted to sacrifice shortness for realism. It basically just sets up a class that can queue OpenGL commands and then runs them all in the main thread.
glwindow.cpp
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include "glwindow.hpp"
#include <SFML/OpenGL.hpp>
////////////////////////////////////////////////////////////
// Color defines a color for the SetColor function
////////////////////////////////////////////////////////////
struct ByteColor
{
GLubyte r;
GLubyte g;
GLubyte b;
};
////////////////////////////////////////////////////////////
// IntRect defines a rectangle for the DrawRectangle function
////////////////////////////////////////////////////////////
struct IntRect
{
GLint x1;
GLint y1;
GLint x2;
GLint y2;
};
GLWindow::GLWindow() :
myShouldClose(false)
{
Initialize();
}
GLWindow::GLWindow(sf::VideoMode mode, const std::string& title, unsigned long style, const sf::ContextSettings& settings) :
sf::Window(mode, title, style, settings),
myShouldClose(false)
{
Initialize();
}
void GLWindow::Clear()
{
// Add a call to ClearColorBuffer to the renderer calls buffer with no argument
myRendererCalls.push_back(RendererCall(ClearColorBuffer, 0));
}
void GLWindow::Close()
{
// We don't directly close the window here because that will make the OpenGL context invalid for any subsequent OpenGL calls
myShouldClose = true;
}
void GLWindow::Display()
{
// Lock other threads' writing access to the renderer calls buffer
myRendererCallsMutex.Lock();
// Flush buffered renderer calls
for (RendererCalls::iterator it = myRendererCalls.begin(); it != myRendererCalls.end(); it = myRendererCalls.erase(it))
switch (it->first)
{
case ClearColorBuffer:
{
// Clear the OpenGL color buffer
glClear(GL_COLOR_BUFFER_BIT);
break;
}
case Color:
{
// Get ByteColor argument
ByteColor* color = static_cast<ByteColor*>(it->second);
// Set the current color with the given arguments
glColor3ub(color->r, color->g, color->b);
// Deallocate the ByteColor argument
delete color;
break;
}
case Rect:
{
// Get IntRect argument
IntRect* rect = static_cast<IntRect*>(it->second);
// Draw a rectangle with the given arguments
glRecti(rect->x1, rect->y1, rect->x2, rect->y2);
// Deallocate the IntRect argument
delete rect;
break;
}
}
// Unlock other threads' writing access to the renderer calls buffer when we're done
myRendererCallsMutex.Unlock();
// Call the underlying Display function
sf::Window::Display();
// If the window should close, call the underlying Close function
if (myShouldClose)
sf::Window::Close();
}
void GLWindow::DrawRectangle(int x1, int y1, int x2, int y2)
{
// Allocate an IntRect with the given coordinates
IntRect* rect = new IntRect;
rect->x1 = x1;
rect->y1 = y1;
rect->x2 = x2;
rect->y2 = y2;
// Add a call to Rect to the renderer calls buffer with an IntRect argument
myRendererCalls.push_back(RendererCall(Rect, rect));
}
void GLWindow::Initialize()
{
// Initialize any OpenGL render states
glOrtho(0, GetWidth(), GetHeight(), 0, -1, 1);
}
void GLWindow::Lock()
{
// Lock other threads' writing access to the renderer calls buffer
myRendererCallsMutex.Lock();
}
void GLWindow::SetColor(unsigned char r, unsigned char g, unsigned char b)
{
// Allocate a ByteColor with the given RGB
ByteColor* color = new ByteColor;
color->r = r;
color->g = g;
color->b = b;
// Add a call to Color to the renderer calls buffer with a ByteColor argument
myRendererCalls.push_back(RendererCall(Color, color));
}
void GLWindow::Unlock()
{
// Unlocks other threads' writing access to the renderer calls buffer
myRendererCallsMutex.Unlock();
}
glwindow.hpp
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Window.hpp>
////////////////////////////////////////////////////////////
// GLWindow member functions are used instead of directly
// using OpenGL functions to give commands to the hardware
////////////////////////////////////////////////////////////
class GLWindow : public sf::Window
{
public :
////////////////////////////////////////////////////////////
// Default constructor
////////////////////////////////////////////////////////////
GLWindow();
////////////////////////////////////////////////////////////
// Constructs a new GL window
////////////////////////////////////////////////////////////
GLWindow(sf::VideoMode mode, const std::string& title, unsigned long style = sf::Style::Default, const sf::ContextSettings& settings = sf::ContextSettings());
////////////////////////////////////////////////////////////
// Clears all pixels to black
////////////////////////////////////////////////////////////
void Clear();
////////////////////////////////////////////////////////////
// Tells the window that it should close
////////////////////////////////////////////////////////////
void Close();
////////////////////////////////////////////////////////////
// Do the renderer calls that have been buffered so far
////////////////////////////////////////////////////////////
void Display();
////////////////////////////////////////////////////////////
// Draws a rectangle
////////////////////////////////////////////////////////////
void DrawRectangle(int x1, int y1, int x2, int y2);
////////////////////////////////////////////////////////////
// Perform renderer initializations
////////////////////////////////////////////////////////////
void Initialize();
////////////////////////////////////////////////////////////
// Locks the renderer buffer
////////////////////////////////////////////////////////////
void Lock();
////////////////////////////////////////////////////////////
// Sets the color of the renderer
////////////////////////////////////////////////////////////
void SetColor(unsigned char r, unsigned char g, unsigned char b);
////////////////////////////////////////////////////////////
// Unlocks the renderer buffer
////////////////////////////////////////////////////////////
void Unlock();
private :
////////////////////////////////////////////////////////////
// Enumeration of the different rendering functions the
// GLWindow is capable of
////////////////////////////////////////////////////////////
enum RendererFunction
{
ClearColorBuffer,
Color,
Rect
};
typedef std::pair<RendererFunction, void*> RendererCall;
typedef std::vector<RendererCall> RendererCalls;
RendererCalls myRendererCalls; // Container of buffered renderer calls to be processed by the GLWindow
sf::Mutex myRendererCallsMutex; // Mutex for synchronizing threads' writing to the renderer calls buffer
bool myShouldClose; // If window should close, this variable is true, else false
};
main.cpp
#include "glwindow.hpp"
bool threadShouldRun = true;
void Thread(GLWindow* window)
{
while (threadShouldRun)
{
// Draw an expanding red rectangle in the top left in this thread
static int Width = 0;
static int Height = 0;
++Width;
++Height;
window->Lock();
window->SetColor(255, 0, 0);
window->DrawRectangle(0, 0, Width, Height);
window->Unlock();
sf::Sleep(0.01);
}
}
int main()
{
GLWindow window(sf::VideoMode(640, 480), "Test", sf::Style::Close);
window.SetFramerateLimit(100);
sf::Thread thread(&Thread, &window);
thread.Launch();
while(window.IsOpened())
{
sf::Event event;
while (window.PollEvent(event))
if (event.Type == sf::Event::Closed)
window.Close();
// Draw an expanding blue rectangle in the bottom right in this thread
static int Width = 640;
static int Height = 480;
--Width;
--Height;
window.Lock();
window.SetColor(0, 0, 255);
window.DrawRectangle(640, 480, Width, Height);
window.Unlock();
window.Display();
}
threadShouldRun = false;
return 0;
}
It's pretty long. In hindsight, it might have been better to upload the files to a separate location and provide a download link. :lol: