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

Window hint for allowing software renderer #589

Open
verybigbadboy opened this Issue Aug 27, 2015 · 19 comments

Comments

Projects
None yet
10 participants
@verybigbadboy

verybigbadboy commented Aug 27, 2015

I'm using glfw for simple render with OpenGL 1.1 support only
I am targeting for windows vm with no video-card installed (Microsoft basic display adapter)

I able to use my app if I create context manually, however it is not possible to use glfw because of check for acceleration:
https://github.com/glfw/glfw/blob/master/src/wgl_context.c#L210

I know it is very specific issue, could we ifdef + add cmake property for non-accelerated pf support?

thank you :)

@elmindreda

This comment has been minimized.

Show comment
Hide comment
@elmindreda

elmindreda Aug 27, 2015

Member

I may add a window hint for it. In the meantime, just remove that test.

Member

elmindreda commented Aug 27, 2015

I may add a window hint for it. In the meantime, just remove that test.

@elmindreda elmindreda self-assigned this Aug 27, 2015

@elmindreda elmindreda changed the title from windows, support for non-accelerated pixel formats to Window hint for allowing software renderer Sep 7, 2015

@elmindreda elmindreda added this to the 3.3 milestone Apr 4, 2016

@linkmauve

This comment has been minimized.

Show comment
Hide comment
@linkmauve

linkmauve Sep 28, 2016

Member

On Mesa (at least on Wayland and X11, possibly other platforms as well) this can be implemented with setenv("LIBGL_ALWAYS_SOFTWARE", "", 1).

Member

linkmauve commented Sep 28, 2016

On Mesa (at least on Wayland and X11, possibly other platforms as well) this can be implemented with setenv("LIBGL_ALWAYS_SOFTWARE", "", 1).

@felselva

This comment has been minimized.

Show comment
Hide comment
@felselva

felselva Mar 21, 2017

Contributor

This is a very interesting and useful feature. I would love to work on it, on X11 and Wayland.

I imagine some design choices should be made beforehand? Because each platform gives access to the window pixels in different way, maybe a proxy (buffer) should be made and then copied to the native window using glfwSwapBuffer()?

Contributor

felselva commented Mar 21, 2017

This is a very interesting and useful feature. I would love to work on it, on X11 and Wayland.

I imagine some design choices should be made beforehand? Because each platform gives access to the window pixels in different way, maybe a proxy (buffer) should be made and then copied to the native window using glfwSwapBuffer()?

@felselva

This comment has been minimized.

Show comment
Hide comment
@felselva

felselva Apr 7, 2017

Contributor

Because this is fun, I will get my hands dirty on it. Let me know what you think:

I've been thinking, the most simple way to do this would be providing a function like this:

glfwPushPixels(glfwWindow *window, unsigned char *pixels);

Where pixels is an array with the contiguous RGB data, and it would be the responsibility of the user that the total of elements of pixels is correct, that would be the width of the window × height of the window. That's it.

I will start the X11 implementation first. The first issue that I know in advance is that X11 doesn't have any function to plot pixels in a whole batch, instead it uses XDrawPoint to plot single pixels, so it is a matter of traverse the pixels and plotting them using XDrawPoint. There is an extension to plot whole batches of pixels, but it's really old (MIT-SHM).

I did this kind of thing for Windows a decade+ ago, using GDI. I don't remember anything, but nothing that some 90s tutorials can't solve 😂 .

Extra: here's a nice software renderer https://github.com/ssloy/tinyrenderer.

Update:
Initial implementation on X11:

static int RGBtoInt(unsigned char red, unsigned char green, unsigned char blue)
{
    return ((((int)(red) % 256) << 16) +
            (((int)(green) % 256) << 8) +
            (((int)(blue) % 256)));
}

void _glfwPlaftormPushPixels(_GLFWwindow *window, unsigned char *pixels)
{
    int col, row;
    int width, height;
    unsigned char *pixel;
    GC gc = XCreateGC(_glfw.x11.display, window->x11.handle, 0, NULL);
    _glfwPlatformGetWindowSize(window, &width, &height);

    for (row = 0; row < height; row++)
    {
        for (col = 0; col < width; col++)
        {
            pixel = &pixels[(row * width) + (col * 3)];
            XSetForeground(_glfw.x11.display, gc, RGBtoInt(pixel[0], pixel[1], pixel[2]));
            XDrawPoint(_glfw.x11.display, window->x11.handle, gc, col, row);
        }
    }

    XFreeGC(_glfw.x11.display, gc);
}
Contributor

felselva commented Apr 7, 2017

Because this is fun, I will get my hands dirty on it. Let me know what you think:

I've been thinking, the most simple way to do this would be providing a function like this:

glfwPushPixels(glfwWindow *window, unsigned char *pixels);

Where pixels is an array with the contiguous RGB data, and it would be the responsibility of the user that the total of elements of pixels is correct, that would be the width of the window × height of the window. That's it.

I will start the X11 implementation first. The first issue that I know in advance is that X11 doesn't have any function to plot pixels in a whole batch, instead it uses XDrawPoint to plot single pixels, so it is a matter of traverse the pixels and plotting them using XDrawPoint. There is an extension to plot whole batches of pixels, but it's really old (MIT-SHM).

I did this kind of thing for Windows a decade+ ago, using GDI. I don't remember anything, but nothing that some 90s tutorials can't solve 😂 .

Extra: here's a nice software renderer https://github.com/ssloy/tinyrenderer.

Update:
Initial implementation on X11:

static int RGBtoInt(unsigned char red, unsigned char green, unsigned char blue)
{
    return ((((int)(red) % 256) << 16) +
            (((int)(green) % 256) << 8) +
            (((int)(blue) % 256)));
}

void _glfwPlaftormPushPixels(_GLFWwindow *window, unsigned char *pixels)
{
    int col, row;
    int width, height;
    unsigned char *pixel;
    GC gc = XCreateGC(_glfw.x11.display, window->x11.handle, 0, NULL);
    _glfwPlatformGetWindowSize(window, &width, &height);

    for (row = 0; row < height; row++)
    {
        for (col = 0; col < width; col++)
        {
            pixel = &pixels[(row * width) + (col * 3)];
            XSetForeground(_glfw.x11.display, gc, RGBtoInt(pixel[0], pixel[1], pixel[2]));
            XDrawPoint(_glfw.x11.display, window->x11.handle, gc, col, row);
        }
    }

    XFreeGC(_glfw.x11.display, gc);
}
@ben1

This comment has been minimized.

Show comment
Hide comment
@ben1

ben1 Apr 7, 2017

Personally, I just want a way to tell glfw to not require acceleration. Commenting out the check inside glfw works, but ideally there'd be a hint to tell glfw to not care.

ben1 commented Apr 7, 2017

Personally, I just want a way to tell glfw to not require acceleration. Commenting out the check inside glfw works, but ideally there'd be a hint to tell glfw to not care.

@felselva

This comment has been minimized.

Show comment
Hide comment
@felselva

felselva Apr 7, 2017

Contributor

I will try to implement the hint for disabling hardware acceleration.

I believe we have here two features, actually, since software rendering and hardware rendering aren't mutually exclusive.

Contributor

felselva commented Apr 7, 2017

I will try to implement the hint for disabling hardware acceleration.

I believe we have here two features, actually, since software rendering and hardware rendering aren't mutually exclusive.

@nkindt

This comment has been minimized.

Show comment
Hide comment
@nkindt

nkindt Apr 25, 2017

Definitely an important feature imho. I use many apps which should not require HW rendering and should not depend on the OS and driver thus using a consistent SW rasterizer result on all platforms/architectures/etc. Thus a custom SW renderer that rasterizes into an allocated memory and then lets GLFW blit the content into the window (glfwSwapBuffer i.e.). Thanks for the consideration and work on this feature!

nkindt commented Apr 25, 2017

Definitely an important feature imho. I use many apps which should not require HW rendering and should not depend on the OS and driver thus using a consistent SW rasterizer result on all platforms/architectures/etc. Thus a custom SW renderer that rasterizes into an allocated memory and then lets GLFW blit the content into the window (glfwSwapBuffer i.e.). Thanks for the consideration and work on this feature!

@felselva

This comment has been minimized.

Show comment
Hide comment
@felselva

felselva Apr 25, 2017

Contributor

On X11, I was able to make the function to push pixels using XImage, which allows to push a batch of pixels to the window. Using glfwPushPixels() and glfwSwapBuffer() together made my computer go nuts, however. I still will investigate why.

This is the impementation:

void _glfwPlaftormPushPixels(_GLFWwindow *window, unsigned char *pixels)
{
    int width, height;
    _glfwPlatformGetWindowSize(window, &width, &height);
    GC gc = DefaultGC(_glfw.x11.display, _glfw.x11.screen);
    XImage *image = XCreateImage(_glfw.x11.display,
            DefaultVisual(_glfw.x11.display, _glfw.x11.screen),
            24, ZPixmap, 32, pixels, width, height, 32, width * 4/* or 0? */);
    XPutImage(_glfw.x11.display,
            window->x11.handle,
            gc,
            image,
            0, 0, 0, 0, width, height);
    XFree(image);
}

Here I'm using XImage to push a batch of pixels to the window, instead of using XDrawPoint to draw each pixel on the window. I'm still going to make the Windows implementation.

As for Wayland backend and macOS. If someone is using one of those two, it probably means that the computer has good hardware acceleration support. But, for the sake of consistency, and portability of any game/tool made using GLFW and using this glfwPushPixel, I think it's good to implement for those two, also.

Contributor

felselva commented Apr 25, 2017

On X11, I was able to make the function to push pixels using XImage, which allows to push a batch of pixels to the window. Using glfwPushPixels() and glfwSwapBuffer() together made my computer go nuts, however. I still will investigate why.

This is the impementation:

void _glfwPlaftormPushPixels(_GLFWwindow *window, unsigned char *pixels)
{
    int width, height;
    _glfwPlatformGetWindowSize(window, &width, &height);
    GC gc = DefaultGC(_glfw.x11.display, _glfw.x11.screen);
    XImage *image = XCreateImage(_glfw.x11.display,
            DefaultVisual(_glfw.x11.display, _glfw.x11.screen),
            24, ZPixmap, 32, pixels, width, height, 32, width * 4/* or 0? */);
    XPutImage(_glfw.x11.display,
            window->x11.handle,
            gc,
            image,
            0, 0, 0, 0, width, height);
    XFree(image);
}

Here I'm using XImage to push a batch of pixels to the window, instead of using XDrawPoint to draw each pixel on the window. I'm still going to make the Windows implementation.

As for Wayland backend and macOS. If someone is using one of those two, it probably means that the computer has good hardware acceleration support. But, for the sake of consistency, and portability of any game/tool made using GLFW and using this glfwPushPixel, I think it's good to implement for those two, also.

@nkindt

This comment has been minimized.

Show comment
Hide comment
@nkindt

nkindt Apr 26, 2017

Thank you for the code snippet! I am actually more of a Windows low-level-API-blitting kinda guy and used X on rare occasions :) That's why i love GLFW and SDL so much for the abstraction layer. SDL 1.x actually has blitting into a window implemented and it's my choice for now if i need SW rasterizer support on any platform (tested on Windows and Linux). Any chance to learn something from there ?
The key factor here is of course speed. Would a mem-alloc on each push (XCreateImage) not be slow due to heap fragmentation? (especially big chunks on Full-HD-like windows). It's usually discouraged to do mem-allocs on a per-frame basis unless this is the only way X can handle it of course.

nkindt commented Apr 26, 2017

Thank you for the code snippet! I am actually more of a Windows low-level-API-blitting kinda guy and used X on rare occasions :) That's why i love GLFW and SDL so much for the abstraction layer. SDL 1.x actually has blitting into a window implemented and it's my choice for now if i need SW rasterizer support on any platform (tested on Windows and Linux). Any chance to learn something from there ?
The key factor here is of course speed. Would a mem-alloc on each push (XCreateImage) not be slow due to heap fragmentation? (especially big chunks on Full-HD-like windows). It's usually discouraged to do mem-allocs on a per-frame basis unless this is the only way X can handle it of course.

@felselva

This comment has been minimized.

Show comment
Hide comment
@felselva

felselva Apr 26, 2017

Contributor

I will definitelly make an update to allocate that XImage only once (stored in the internal structure of the window), but I need to make at least other implementation (like for Windows) to check which will be the better way to organize things. Speed was quite good for software-rendering, though, I got around 1500 fps with that code :)

SDL 1.x actually has blitting into a window implemented and it's my choice for now if i need SW rasterizer support on any platform (tested on Windows and Linux). Any chance to learn something from there ?

Thanks, I had completely forgotten about SDL, I will check the implementation, definitelly there's something useful.

Contributor

felselva commented Apr 26, 2017

I will definitelly make an update to allocate that XImage only once (stored in the internal structure of the window), but I need to make at least other implementation (like for Windows) to check which will be the better way to organize things. Speed was quite good for software-rendering, though, I got around 1500 fps with that code :)

SDL 1.x actually has blitting into a window implemented and it's my choice for now if i need SW rasterizer support on any platform (tested on Windows and Linux). Any chance to learn something from there ?

Thanks, I had completely forgotten about SDL, I will check the implementation, definitelly there's something useful.

@nkindt

This comment has been minimized.

Show comment
Hide comment
@nkindt

nkindt Apr 26, 2017

That sounds fantastic and i definitely appreciate the effort! It's basically the only things keeping me away from using GLFW on all SW-related projects and GLFW is way to awesome to pass up because of this.

nkindt commented Apr 26, 2017

That sounds fantastic and i definitely appreciate the effort! It's basically the only things keeping me away from using GLFW on all SW-related projects and GLFW is way to awesome to pass up because of this.

@elmindreda elmindreda removed their assignment May 2, 2017

@treeform

This comment has been minimized.

Show comment
Hide comment
@treeform

treeform Aug 7, 2017

@ferreiradaselva Did you get any further with your glfwPushPixels idea? Sounds interesting?

treeform commented Aug 7, 2017

@ferreiradaselva Did you get any further with your glfwPushPixels idea? Sounds interesting?

@raysan5

This comment has been minimized.

Show comment
Hide comment
@raysan5

raysan5 Nov 24, 2017

@ferreiradaselva, @elmindreda, what is the state of this enhacement?

glfwPushPixels() idea sounds very interesting to me. It could just update an internal buffer initialized at glfwCreateWindow() and then glfwSwapBuffer() show it in the window.

raysan5 commented Nov 24, 2017

@ferreiradaselva, @elmindreda, what is the state of this enhacement?

glfwPushPixels() idea sounds very interesting to me. It could just update an internal buffer initialized at glfwCreateWindow() and then glfwSwapBuffer() show it in the window.

elmindreda added a commit that referenced this issue Dec 11, 2017

Add GLFW_CONTEXT_RENDERER
This window hint allows choosing between hardware and software renderers
(where available) for the created OpenGL or OpenGL ES context.

Fixes #589.
@elmindreda

This comment has been minimized.

Show comment
Hide comment
@elmindreda

elmindreda Dec 14, 2017

Member

@raysan5 See the context-renderer branch for a first sketch of software renderer selection. No progress on pushing pixels, as far as I know.

Member

elmindreda commented Dec 14, 2017

@raysan5 See the context-renderer branch for a first sketch of software renderer selection. No progress on pushing pixels, as far as I know.

@raysan5

This comment has been minimized.

Show comment
Hide comment
@raysan5

raysan5 Mar 12, 2018

Hi @elmindreda, just jumped into this again today! I didn't see your answer, thanks for the update!

I've been investigating how could I create a glfwPushPixels() on windows using GDI software rendererer, it seems it could be accomplished with:

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 
{
    static unsigned char *pixelsData;   // TODO: Pixels data array, should be initialized somewhere
    static HBITMAP hBitmap;
    
    HDC hdc;
    PAINTSTRUCT ps;
    BITMAP bitmap;
    HDC hdcMem;             // Temp HDC to copy picture
    HGDIOBJ oldBitmap;

    switch(msg) {
        case WM_CREATE:
        {
            // Create bitmap for blitting
            hBitmap = CreateBitmap(screenWidth, screenHeight, 1, 8*4, (void *)pixelsData);

        } break;      
        case WM_PAINT:
        {
            hdc = BeginPaint(hwnd, &ps);                // Get window HDC and prepare for painting
            hdcMem = CreateCompatibleDC(hdc);           // Create temp HDC
            oldBitmap = SelectObject(hdcMem, hBitmap);  // Insert picture into our temp HDC
            GetObject(hBitmap, sizeof(bitmap), &bitmap);
            
            // Copy image from temp HDC to window
            BitBlt(hdc,         // Destination
                   10,          // Upper-left corner X to start copying to
                   10,          // Upper-left corner Y to start copying to
                   bitmap.bmWidth,   // Width
                   bitmap.bmHeight,  // Height
                   hdcMem,      // Data source
                   0,           // Upper-left corner X to copy from source
                   0,           // Upper-left corner Y to copy from source
                   SRCCOPY);    // Defined DWORD to just copy pixels

            SelectObject(hdcMem, oldBitmap);
            DeleteDC(hdcMem);   // Deleting temp HDC
            
            EndPaint(hwnd, &ps);

        } break;
        case WM_DESTROY:
        {
            free(pixelsData);
            DeleteObject(hBitmap);
            PostQuitMessage(0);
            
            return 0;
        }
    }

    return DefWindowProcW(hwnd, msg, wParam, lParam);
}

It's just for reference, haven't tested it yet.

raysan5 commented Mar 12, 2018

Hi @elmindreda, just jumped into this again today! I didn't see your answer, thanks for the update!

I've been investigating how could I create a glfwPushPixels() on windows using GDI software rendererer, it seems it could be accomplished with:

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 
{
    static unsigned char *pixelsData;   // TODO: Pixels data array, should be initialized somewhere
    static HBITMAP hBitmap;
    
    HDC hdc;
    PAINTSTRUCT ps;
    BITMAP bitmap;
    HDC hdcMem;             // Temp HDC to copy picture
    HGDIOBJ oldBitmap;

    switch(msg) {
        case WM_CREATE:
        {
            // Create bitmap for blitting
            hBitmap = CreateBitmap(screenWidth, screenHeight, 1, 8*4, (void *)pixelsData);

        } break;      
        case WM_PAINT:
        {
            hdc = BeginPaint(hwnd, &ps);                // Get window HDC and prepare for painting
            hdcMem = CreateCompatibleDC(hdc);           // Create temp HDC
            oldBitmap = SelectObject(hdcMem, hBitmap);  // Insert picture into our temp HDC
            GetObject(hBitmap, sizeof(bitmap), &bitmap);
            
            // Copy image from temp HDC to window
            BitBlt(hdc,         // Destination
                   10,          // Upper-left corner X to start copying to
                   10,          // Upper-left corner Y to start copying to
                   bitmap.bmWidth,   // Width
                   bitmap.bmHeight,  // Height
                   hdcMem,      // Data source
                   0,           // Upper-left corner X to copy from source
                   0,           // Upper-left corner Y to copy from source
                   SRCCOPY);    // Defined DWORD to just copy pixels

            SelectObject(hdcMem, oldBitmap);
            DeleteDC(hdcMem);   // Deleting temp HDC
            
            EndPaint(hwnd, &ps);

        } break;
        case WM_DESTROY:
        {
            free(pixelsData);
            DeleteObject(hBitmap);
            PostQuitMessage(0);
            
            return 0;
        }
    }

    return DefWindowProcW(hwnd, msg, wParam, lParam);
}

It's just for reference, haven't tested it yet.

@qutoh qutoh referenced this issue May 16, 2018

Open

OpenGL #937

@chipweinberger

This comment has been minimized.

Show comment
Hide comment
@chipweinberger

chipweinberger May 20, 2018

Could you share your code on how to make GLFW work with windows remote desktop?

chipweinberger commented May 20, 2018

Could you share your code on how to make GLFW work with windows remote desktop?

@ben1

This comment has been minimized.

Show comment
Hide comment
@ben1

ben1 May 20, 2018

I don't have the code right now, but all I did was hack GLFW to skip a check during initialization that was making it fail. It would be great to have a flag in the API to do this, but hacking the source was very simple. Just debug into where it's failing and skip that. It worked for me ;)

ben1 commented May 20, 2018

I don't have the code right now, but all I did was hack GLFW to skip a check during initialization that was making it fail. It would be great to have a flag in the API to do this, but hacking the source was very simple. Just debug into where it's failing and skip that. It worked for me ;)

@SairesArt

This comment has been minimized.

Show comment
Hide comment
@SairesArt

SairesArt Aug 3, 2018

@ben1
Could you outline on how to get this working? You words seem pure magic in writing. I'm desperate in getting GLFW to work under WindowsRemote.
Recently I tried out getting it working with the recent OSMesa implementation, but even with glfwWindowHint( GLFW_CONTEXT_CREATION_API, GLFW_OSMESA_CONTEXT_API); it creates an invisible window and I have a hard time finding any information on how to get it visible or anything useful.

It's so incredibly painful to see, that every single OpenGL Program actually works if you LogOut, start the programm and log back in without flaw and 100% flawless Hardware acceleration, but starting a opengl programm from within requires dirty hacks.

SairesArt commented Aug 3, 2018

@ben1
Could you outline on how to get this working? You words seem pure magic in writing. I'm desperate in getting GLFW to work under WindowsRemote.
Recently I tried out getting it working with the recent OSMesa implementation, but even with glfwWindowHint( GLFW_CONTEXT_CREATION_API, GLFW_OSMESA_CONTEXT_API); it creates an invisible window and I have a hard time finding any information on how to get it visible or anything useful.

It's so incredibly painful to see, that every single OpenGL Program actually works if you LogOut, start the programm and log back in without flaw and 100% flawless Hardware acceleration, but starting a opengl programm from within requires dirty hacks.

@ben1

This comment has been minimized.

Show comment
Hide comment
@ben1

ben1 Aug 4, 2018

I don't have access to the code anymore, but I think it was something as simple as commenting out line 176 of wgl_context.c E.g.

            if (!(pfd.dwFlags & PFD_GENERIC_ACCELERATED) &&
                (pfd.dwFlags & PFD_GENERIC_FORMAT))
            {
                // continue;
            }

If that doesn't work for you, you'll have to step into the initialization function in a debugger to see where it fails, because your situation might be different.

ben1 commented Aug 4, 2018

I don't have access to the code anymore, but I think it was something as simple as commenting out line 176 of wgl_context.c E.g.

            if (!(pfd.dwFlags & PFD_GENERIC_ACCELERATED) &&
                (pfd.dwFlags & PFD_GENERIC_FORMAT))
            {
                // continue;
            }

If that doesn't work for you, you'll have to step into the initialization function in a debugger to see where it fails, because your situation might be different.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment