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

Author Topic: Can't Render Across DLL Boundary  (Read 3619 times)

0 Members and 1 Guest are viewing this topic.

mayday556

  • Newbie
  • *
  • Posts: 8
    • View Profile
Can't Render Across DLL Boundary
« on: June 09, 2021, 01:24:05 am »
I've set up my project to be split up into two parts - a DLL project which acts as the game engine, and the normal project which compiles to an .exe and relies on the DLL to run.

The issue I've come across, is that if I pass a sprite from the .exe frontend to a function in the DLL that renders sprites, the sprite doesn't render. I've already ensured I setup SFML properly, as when I create a window and render to that window all from the frontend, it works fine, and the same goes for if I put the sprite, texture, and window all in DLL.

I made a very small representation of this and put it up on Github, since I know it's annoying to setup a solution to have multiple projects and link them all together, etc. in Visual Studio: https://github.com/Bounty556/BrokenDLL Note I am using the latest version of Visual Studio 2019.

The code is as follows:
Main.cpp - in the frontend .exe
#include <Test.h>

#include <SFML/Graphics.hpp>

int main()
{
        Test test;

        sf::Context context;

        sf::Texture texture;
        texture.loadFromFile("res/test.png");
        sf::Sprite sprite(texture);

        test.startWindowAndDraw(sprite);

        return 0;
}

Test.h - in the DLL
#pragma once

#include <SFML/Graphics.hpp>

class __declspec(dllexport) Test
{
public:
        Test();
        void startWindowAndDraw(sf::Sprite& sprite);

private:
        sf::RenderWindow m_Window;
};
 

Test.cpp
#include "Test.h"

Test::Test() :
        m_Window(sf::VideoMode(640, 480), "Test", sf::Style::Close)
{
}

void Test::startWindowAndDraw(sf::Sprite& sprite)
{
    while (m_Window.isOpen())
    {
        sf::Event event;
        while (m_Window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
                m_Window.close();
        }

        m_Window.clear();
        m_Window.draw(sprite);
        m_Window.display();
    }
}

One thing I noticed is that even though I am creating a window before loading the texture, I still have to create a context, otherwise I get lots of rendering errors. Another thing is, if I don't instantiate Test until after I've loaded the texture, the sprite renders as a blank white rectangle, rather than the image it should be.

It doesn't make sense to me that passing anything from the frontend to the DLL would cause anything weird to happen, since DLLs are essentially part of the program at runtime. If anyone has any insight into this, I would be extremely grateful, as I've been racking my brain on this for quite some time now. Thanks!

Edit:
I forgot some contextual details to go along with this:
OS: Windows 10
Graphics Card: AMD Radeon RX 5700XT
SFML Version: Originally I was using 2.5.1, but upgraded to the latest snapshot release today.

The errors I get in console if I don't create a context before loading the texture are as follows:
An internal OpenGL call failed in RenderWindow.cpp(96).
Expression:
   GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, m_defaultFrameBuffer)
Error description:
   GL_INVALID_OPERATION
   The specified operation is not allowed in the current state.

An internal OpenGL call failed in Texture.cpp(778).
Expression:
   glBindTexture(GL_TEXTURE_2D, 0)
Error description:
   GL_INVALID_OPERATION
   The specified operation is not allowed in the current state.

An internal OpenGL call failed in Texture.cpp(781).
Expression:
   glMatrixMode(GL_TEXTURE)
Error description:
   GL_INVALID_OPERATION
   The specified operation is not allowed in the current state.

An internal OpenGL call failed in Texture.cpp(782).
Expression:
   glLoadIdentity()
Error description:
   GL_INVALID_OPERATION
   The specified operation is not allowed in the current state.

An internal OpenGL call failed in Texture.cpp(785).
Expression:
   glMatrixMode(GL_MODELVIEW)
Error description:
   GL_INVALID_OPERATION
   The specified operation is not allowed in the current state.

An internal OpenGL call failed in RenderTarget.cpp(175).
Expression:
   glClearColor(color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f)
Error description:
   GL_INVALID_OPERATION
   The specified operation is not allowed in the current state.

An internal OpenGL call failed in RenderTarget.cpp(176).
Expression:
   glClear(GL_COLOR_BUFFER_BIT)
Error description:
   GL_INVALID_OPERATION
   The specified operation is not allowed in the current state.
« Last Edit: June 09, 2021, 01:57:38 am by mayday556 »

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11030
    • View Profile
    • development blog
    • Email
Re: Can't Render Across DLL Boundary
« Reply #1 on: June 09, 2021, 08:27:45 am »
You haven't mentioned the most important part, how are you linking SFML in your DLL and in your executable?
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

mayday556

  • Newbie
  • *
  • Posts: 8
    • View Profile
Re: Can't Render Across DLL Boundary
« Reply #2 on: June 09, 2021, 03:01:07 pm »
You haven't mentioned the most important part, how are you linking SFML in your DLL and in your executable?

My apologies, in both instances, SFML is linked statically.

mayday556

  • Newbie
  • *
  • Posts: 8
    • View Profile
Re: Can't Render Across DLL Boundary
« Reply #3 on: June 09, 2021, 03:37:10 pm »
You got me curious with that last reply, so I linked both the DLL and the exe dynamically and it works perfectly now... Do you know the reason why this doesn't work when linked statically? If there is a way to have SFML linked statically and have it still work that'd be preferable, but this works just as well, thanks for the hint!

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11030
    • View Profile
    • development blog
    • Email
Re: Can't Render Across DLL Boundary
« Reply #4 on: June 09, 2021, 04:27:14 pm »
A static library is nothing more than an archive of object files. So when you statically link something, you directly build/link these object files into your executable.
This effectively means, that when you link SFML statically into your DLL and your application, you end up with two separate copies of SFML.
Since SFML does some state tracking using globals/statics you end up with copies of those in two different locations and you can't really share things across the DLL boundary.

When you link SFML dynamically, you just have that one version of SFML loaded for both your DLL and your application and you can properly share things across boundaries.

tl;dr you can't link SFML statically when using it in a DLL and said DLL is used in your application.

An alternative to linking everything dynamically, is to also link your library statically, that way all the object files will be linked once into you application.
« Last Edit: June 09, 2021, 05:45:11 pm by eXpl0it3r »
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

mayday556

  • Newbie
  • *
  • Posts: 8
    • View Profile
Re: Can't Render Across DLL Boundary
« Reply #5 on: June 09, 2021, 05:21:07 pm »
I see, I had no idea linking statically was so different from linking dynamically. Thanks a ton for the help here, cheers!  :)