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

Window hint for allowing software renderer #589

Open
verybigbadboy opened this issue Aug 27, 2015 · 30 comments · May be fixed by #2491
Open

Window hint for allowing software renderer #589

verybigbadboy opened this issue Aug 27, 2015 · 30 comments · May be fixed by #2491
Labels
enhancement Feature suggestions and PRs macOS Windows Win32 specific (not Cygwin or WSL) X11

Comments

@verybigbadboy
Copy link

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 (PFD_GENERIC_ACCELERATED)

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

thank you :)

@elmindreda
Copy link
Member

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 added the enhancement Feature suggestions and PRs label Sep 7, 2015
@elmindreda elmindreda changed the title windows, support for non-accelerated pixel formats Window hint for allowing software renderer Sep 7, 2015
@elmindreda elmindreda added this to the 3.3 milestone Apr 4, 2016
@elmindreda elmindreda added macOS Windows Win32 specific (not Cygwin or WSL) X11 labels Sep 6, 2016
@linkmauve
Copy link
Member

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

@felselva
Copy link
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()?

@felselva
Copy link
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
Copy link
Contributor

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
Copy link
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
Copy link

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
Copy link
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
Copy link

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
Copy link
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
Copy link

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
Copy link

treeform commented Aug 7, 2017

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

@raysan5
Copy link

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
This window hint allows choosing between hardware and software renderers
(where available) for the created OpenGL or OpenGL ES context.

Fixes #589.
@elmindreda
Copy link
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.

@raysan5
Copy link

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.

@chipweinberger
Copy link

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

@ben1
Copy link
Contributor

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 ;)

@ghost
Copy link

ghost 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
Copy link
Contributor

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.

@Dschaehn
Copy link

We stumbled upon the same issue when using windows remote screensharing. For us commenting the lines mentioned by ben1 also fixed the issue.

@elmindreda is it planned to integrate the hint on the main branch as well?

@codedcosmos
Copy link

I'm currently using SDL2 with a putpixels method I found here. I would much prefer to switch over to GLFW, since I prefer it over SDL2. But I am guessing this feature isn't getting integrated with the main branch? I hope I am wrong and it's coming in the next version.

@stgatilov
Copy link

It would be great to have a way to disable the check without rebuilding the library. I hoped I would use all libraries via conan, but now I'm afraid I will have to fork-and-hack the recipe.

@MrMusiX
Copy link

MrMusiX commented Mar 16, 2020

Any chance for this? I don't see the point in forbidding the creation of a "non-accelerated" OpenGL context if not explicitly requested.

@stgatilov
Copy link

In case anyone uses glfw via conan, here is the place where I hacked glfw recipe to make RDP work:

    def source(self):
        sha256 = "0c623f65a129c424d0fa45591694fde3719ad4a0955d4835182fda71b255446f"
        ver = self.version.split('-')[0]
        tools.get("{}/archive/{}.zip".format(self.homepage, ver), sha256=sha256)
        extracted_folder = self.name + '-' + ver
        os.rename(extracted_folder, self._source_subfolder)
        # stgatilov: comment out check for hardware acceleration to allow RDP
        # see https://github.com/glfw/glfw/issues/589
        tools.replace_in_file(self._source_subfolder + "/src/wgl_context.c",
            "if (!(pfd.dwFlags & PFD_GENERIC_ACCELERATED) &&",
            "if (!(pfd.dwFlags & PFD_GENERIC_ACCELERATED) && 0 &&"
        )

@Wilm0rien
Copy link

hi @elmindreda, I just want to add that it would be really great to have a software renderer in GLFW!

@codedcosmos
Copy link

Though I still think it would be cool if GLFW had this.

If you are wanting to learn software rendering and you are familar with Rust. I would recommend the pixels library.

Good for raytracing too.

@ws909
Copy link

ws909 commented Mar 13, 2023

Is it not sufficient to obtain/enable a software/CPU implementation of OpenGL or Vulkan for this purpose?

@codedcosmos
Copy link

Not exactly. Really what people want is direct access to the framebuffer to render from their CPU.

Not to go through OpenGL/Vulkan.

@flysand7
Copy link

What's the status on this? I've been meaning to write GUI for my application on top of GLFW, but I wanted to have a fallback for rendering, just in case the drivers are messed up / whatever it is on the user's side.

@ColleagueRiley
Copy link

RGFW supports software rendering via RGFW_BUFFER. If anyone wants to reference that.

Also, I believe if you get the HWND, x window, or NSWindow, you can use that for software rendering without changing any glfw code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Feature suggestions and PRs macOS Windows Win32 specific (not Cygwin or WSL) X11
Projects
None yet
Development

Successfully merging a pull request may close this issue.