Zombieschannel / SFML

Simple and Fast Multimedia Library
https://www.sfml-dev.org/
zlib License
1 stars 1 forks source link

No error but invisible #7

Closed Nathan-M-code closed 2 months ago

Nathan-M-code commented 3 months ago

Prerequisite Checklist

Describe your issue here

Sorry it is not really an error but I am struggling to find my mistake. I did a test application to test shaders and it works well. But when I ported that on my actual game, all sprites with shader are just invisible. These are my shaders vertex :

#version 100

attribute vec2 position;
attribute vec4 color;
attribute vec2 texCoord;

varying vec4 sf_color;
varying vec2 sf_texCoord;

uniform mat4 sf_modelview;
uniform mat4 sf_projection;

void main()
{
    vec2 pos = position;
    sf_color = color;
    sf_texCoord = texCoord;
    gl_Position = sf_projection * sf_modelview * vec4(pos.xy, 0.0, 1.0);
}

fragment :

#version 100
precision mediump float;

varying vec4 sf_color;
varying vec2 sf_texCoord;

uniform sampler2D sf_sampler;
uniform mat4 sf_texture;

void main()
{
    vec4 coord = sf_texture * vec4(sf_texCoord, 0.0, 1.0);
    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0) * texture2D(sf_sampler, coord.xy);
}

Again, that is working with a little test application :

#include <SFML/Graphics.hpp>
#include <SFML/System.hpp>

#include <android/log.h>
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
#include <android/native_activity.h>
#include <android/configuration.h>
#include <SFML/System/NativeActivity.hpp>

#include <filesystem>

#define LOG(...) ((void)__android_log_print(ANDROID_LOG_INFO, "FC", __VA_ARGS__))

std::vector<std::uint8_t> readBinary(std::filesystem::path path, bool internalStorage=false)
{
    std::vector<std::uint8_t> ret;

    ANativeActivity* nativeActivity = sf::getNativeActivity();
    AAssetManager* assetManager = nativeActivity->assetManager;
    AAsset* mAsset = AAssetManager_open(assetManager, path.c_str(), AASSET_MODE_BUFFER);

    if(!mAsset){
        LOG("Can't open file");
        return ret;
    }

    int len = AAsset_getLength(mAsset);
    ret.resize(len);  // Allocate memory for the vector
    int lenRead = AAsset_read(mAsset,ret.data(),len);
    AAsset_close(mAsset);

    LOG("Read %s bytes", std::to_string(lenRead).c_str());

    return ret;
}

int main()
{
    sf::RenderWindow window(sf::VideoMode::getDesktopMode(), "Test", sf::Style::Fullscreen);

    sf::Texture t;
    t.loadFromFile("image.png");

    auto path = dyeShaderPath.at(0);
    auto s = std::make_unique<sf::Shader>();

    auto bytesVertexShader = readBinary("minimal.vert.android");
    std::string vertexShader(bytesVertexShader.begin(), bytesVertexShader.end());

    auto bytes = readBinary("red.frag.android");
    std::string fragShader(bytes.begin(), bytes.end());

    if(!s->loadFromMemory(vertexShader, fragShader)){
        throw std::runtime_error("Couldn't load "+path.string());
    }

    s->setUniform("sf_sampler", sf::Shader::CurrentTexture);

    sf::Clock clock;

    sf::Sprite sprite(t);

    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed) window.close();
        }
        window.clear();

        s->setUniform("time", clock.getElapsedTime().asSeconds());

        window.draw(sprite, s.get());
        window.display();
    }

    return EXIT_SUCCESS;
}

I was wondering what could be the problem that leads to invisible sprite ? The compilation is fine, no sfml's error in logs. I checked with wrong shader and I've got error message. Any clue ? Thank you

Your Environment

Steps to reproduce

I am looking for step to reproduce my problem

Expected behavior

working shaders

Actual behavior

invisible sprite

Zombieschannel commented 3 months ago

I slightly changed your code to read shaders from a string(since I don't know what does the readBinary do and how to test with it):

#include <SFML/Graphics.hpp>
#include <SFML/System.hpp>

std::string vert = R"(#version 100

attribute vec2 position;
attribute vec4 color;
attribute vec2 texCoord;

varying vec4 sf_color;
varying vec2 sf_texCoord;

uniform mat4 sf_modelview;
uniform mat4 sf_projection;

void main()
{
    vec2 pos = position;
    sf_color = color;
    sf_texCoord = texCoord;
    gl_Position = sf_projection * sf_modelview * vec4(pos.xy, 0.0, 1.0);
})";

std::string frag = R"(#version 100
precision mediump float;

varying vec4 sf_color;
varying vec2 sf_texCoord;

uniform sampler2D sf_sampler;
uniform mat4 sf_texture;

void main()
{
    vec4 coord = sf_texture * vec4(sf_texCoord, 0.0, 1.0);
    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0) * texture2D(sf_sampler, coord.xy);
})";

int main()
{
    sf::RenderWindow window(sf::VideoMode::getDesktopMode(), "Test", sf::Style::Fullscreen);

    sf::Texture t;
    t.loadFromFile("Res/settings.png");

    sf::Shader s;

    if(!s.loadFromMemory(vert, frag)){
        sf::err() << "a";
    }

    //not necessary
    s.setUniform("sf_sampler", sf::Shader::CurrentTexture);

    sf::Clock clock;

    sf::Sprite sprite(t);

    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed) window.close();
        }
        window.clear();

        //also not necessary
        s.setUniform("time", clock.getElapsedTime().asSeconds());

        window.draw(sprite, &s);
        window.display();
    }

    return EXIT_SUCCESS;
}

This works completely fine, so your error might be somewhere in getting the shader source code?

Screenshot_20240617-145915

Nathan-M-code commented 3 months ago

Yes the code is working, it is my test program. In my real game, the code is more or less the same with some layer of complexity added. I am looking for the difference that could lead to invisible sprites. I was wondering if you had any idea where the problem could come from, whether in loading, or in shader uniform setting for example. What is weird is that everything seem fine in logs. No err message, and it is just the sprite with shaders.

Zombieschannel commented 3 months ago

Without more information, I honestly have no clue, sorry.

Nathan-M-code commented 3 months ago

Yeah sure I should have given a reproducible example at first here we go

#include <SFML/Graphics.hpp>
#include <SFML/System.hpp>

#include <android/log.h>
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
#include <android/native_activity.h>
#include <android/configuration.h>
#include <SFML/System/NativeActivity.hpp>

#include <filesystem>

#define LOG(...) ((void)__android_log_print(ANDROID_LOG_INFO, "FC", __VA_ARGS__))

std::vector<std::uint8_t> readBinary(std::filesystem::path path, bool internalStorage=false)
{
    std::vector<std::uint8_t> ret;

    ANativeActivity* nativeActivity = sf::getNativeActivity();
    AAssetManager* assetManager = nativeActivity->assetManager;
    AAsset* mAsset = AAssetManager_open(assetManager, path.c_str(), AASSET_MODE_BUFFER);

    if(!mAsset){
        LOG("Can't open file");
        return ret;
    }

    int len = AAsset_getLength(mAsset);
    ret.resize(len);  // Allocate memory for the vector
    int lenRead = AAsset_read(mAsset,ret.data(),len);
    AAsset_close(mAsset);

    LOG("Read %s bytes", std::to_string(lenRead).c_str());

    return ret;
}

sf::Shader* requestShader()
{
    auto s = new sf::Shader;

    auto bytesVertexShader = readBinary("minimal.vert.android");
    std::string vertexShader(bytesVertexShader.begin(), bytesVertexShader.end());
    LOG("vertexShader: %s", vertexShader.c_str());

    auto bytes = readBinary("red.frag.android");
    std::string fragShader(bytes.begin(), bytes.end());
    LOG("fragShader: %s", fragShader.c_str());

    if(!s->loadFromMemory(vertexShader, fragShader)){
        throw std::runtime_error("Couldn't compile");
    }

    s->setUniform("sf_sampler", sf::Shader::CurrentTexture);

    LOG("loaded !");

    return s;
}

int main()
{
    sf::RenderWindow window(sf::VideoMode::getDesktopMode(), "Test", sf::Style::Fullscreen);

    sf::Texture t;
    t.loadFromFile("image.png");
    sf::Sprite sprite(t);

    sf::Clock clock;

    sf::Shader* s = nullptr;

    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed) window.close();
            if (event.type == sf::Event::TouchBegan){
                s = requestShader();
            }
        }

        window.clear();
        window.draw(sprite, s);
        window.display();
    }

    return EXIT_SUCCESS;
}

The problem seems to come when we load shader at runtime. Logs show that reading files is ok, I can see them through LOG("fragShader: %s", fragShader.c_str()); and LOG("vertexShader: %s", vertexShader.c_str());, and the compilation is fine.

Once the shader is applied, the sprite is invisible.

Nathan-M-code commented 3 months ago

By the way, I use readBinary to load files from android assets folder, as loadFromFile loads only the internal storage with std::ifstream.

Zombieschannel commented 3 months ago

Uhhh, I'm pretty sure it is the opposite way around, loadFromFile reads the assets folder in the APK, while with std::ifstream you can read from anywhere on the device (as long as you have permission). At least I've been using loadFromFile for loading read-only resources/assets and std::ifstream and std::ofstream for things like save files.

Nathan-M-code commented 3 months ago

At least I've been using loadFromFile for loading read-only resources/assets and std::ifstream and std::ofstream for things like save files.

Yes, the loadFromFile from class sf::Shader uses std::ifstream while other loadFromFile from other classes (sf::Texture for example) are linked with Android specific code that reads assets. I think they did not do the same thing with sf::Shader because opening a shader in android was irrelevant.

Nathan-M-code commented 3 months ago

Did you succeed to run the minimal example ? Should this issue be reopen ? If you have an idea of what the problem could be, I can have a look at it and maybe pr something.

Zombieschannel commented 3 months ago

I've been busy last week, so sorry for this late reply. I've tested this and again, I am not familiar with what should I do outside the program which is likely the reason I'm getting the error "Can't open file". Anyways I see now that sf::Shader uses std::ifstream so I think I'll change it to use sf::FileInputStream to be the same as sf::Texture sf::Font and so on which all load from the assets folder.

Nathan-M-code commented 3 months ago

It is weird that you get "Can't open file" with the program I gave if the shaders are in the asset folder. Anyway I think the problem comes from loading shader at runtime, because I have the same issue with this program :

#include <SFML/Graphics.hpp>
#include <SFML/System.hpp>

#include <android/log.h>
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
#include <android/native_activity.h>
#include <android/configuration.h>
#include <SFML/System/NativeActivity.hpp>

#include <filesystem>

#define LOG(...) ((void)__android_log_print(ANDROID_LOG_INFO, "FC", __VA_ARGS__))

std::string vert = R"(#version 100

attribute vec2 position;
attribute vec4 color;
attribute vec2 texCoord;

varying vec4 sf_color;
varying vec2 sf_texCoord;

uniform mat4 sf_modelview;
uniform mat4 sf_projection;

void main()
{
    vec2 pos = position;
    sf_color = color;
    sf_texCoord = texCoord;
    gl_Position = sf_projection * sf_modelview * vec4(pos.xy, 0.0, 1.0);
})";

std::string frag = R"(#version 100
precision mediump float;

varying vec4 sf_color;
varying vec2 sf_texCoord;

uniform sampler2D sf_sampler;
uniform mat4 sf_texture;

void main()
{
    vec4 coord = sf_texture * vec4(sf_texCoord, 0.0, 1.0);
    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0) * texture2D(sf_sampler, coord.xy);
})";

sf::Shader* requestShader()
{
    auto s = new sf::Shader;

    if(!s->loadFromMemory(vert, frag)){
        throw std::runtime_error("Couldn't compile");
    }

    s->setUniform("sf_sampler", sf::Shader::CurrentTexture);

    LOG("loaded !");

    return s;
}

int main()
{
    sf::RenderWindow window(sf::VideoMode::getDesktopMode(), "Test", sf::Style::Fullscreen);

    sf::Texture t;
    t.loadFromFile("image.png");
    sf::Sprite sprite(t);

    sf::Clock clock;

    sf::Shader* s = nullptr;

    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed) window.close();
            if (event.type == sf::Event::TouchBegan){
                s = requestShader();
            }
        }

        window.clear();
        window.draw(sprite, s);
        window.display();
    }

    return EXIT_SUCCESS;
}

And I get "loaded !" in logs

Nathan-M-code commented 2 months ago

The merge #12 fixed the issue ! Thank you all