edubart/sokol_gp

Cannot render to frame buffer

Closed this issue · 1 comments

Hi,

Thanks for this excellent library, I was using sokol_gfx directly in a 2D engine and sokol_gp made my life a lot easier.

However, I can't seem to be able to render to a frame buffer. My rendering starts with a start call, which receives an optional render target:

void start(rm::RenderTarget const* target) override {
    _renderTarget = target;

    if (_renderTarget == nullptr) {
        sgp_begin(_size.width(), _size.height());
        sgp_viewport(0, 0, _size.width(), _size.height());
        sgp_project(0.0f, _size.width(), 0.0f, _size.height());

        sgp_set_color(0.0f, 0.0f, 0.0f, 1.0f);
        sgp_clear();
        sgp_reset_color();
    }
    else {
        rm::Size<long> const& size = _renderTarget->size();
        sgp_begin(size.width(), size.height());
        sgp_viewport(0, 0, size.width(), size.height());
        sgp_project(0, size.width(), 0.0f, size.height());

        sg_pass_action action = {};
        action.colors[0].action = SG_ACTION_CLEAR;
        action.colors[0].value.r = 0.0f;
        action.colors[0].value.g = 0.0f;
        action.colors[0].value.b = 0.0f;
        action.colors[0].value.a = 1.0f;
        sg_begin_pass(static_cast<SokolRenderTarget const*>(_renderTarget)->_pass, &action);
    }
}

After that, I can draw quads, textured or not:

void draw(rm::Quad const& quad, rm::Texture const* const texture, Post post) override {
    rm::Point const& position = quad.position();
    rm::Size<float> const& size = quad.size();
    rm::Point const& center = quad.center();
    rm::Vec2 const& scale = quad.scale();
    float const angle = -quad.angle();
    rm::Color const& color = quad.color();

    rm::Point const screencenter(position.x() + center.x(), position.y() + center.y());

    sgp_push_transform();
    sgp_scale_at(scale.x(), scale.y(), screencenter.x(), screencenter.y());
    sgp_rotate_at(angle, screencenter.x(), screencenter.y());

    sgp_set_color(color.r(), color.g(), color.b(), color.a());
    sgp_set_blend_mode(SGP_BLENDMODE_BLEND);

    if (texture != nullptr) {
        sgp_set_image(0, static_cast<SokolTexture const*>(texture)->_texture);
        sgp_draw_textured_rect(position.x(), position.y(), size.width(), size.height());
        sgp_reset_image(0);
    }
    else {
        sgp_draw_filled_rect(position.x(), position.y(), size.width(), size.height());
    }

    sgp_reset_blend_mode();
    sgp_reset_color();
    sgp_pop_transform();
}

And then I call present to commit the draw calls:

void present() override {
    if (_renderTarget == nullptr) {
        sg_pass_action action = {0};
        sg_begin_default_pass(&action, _size.width(), _size.height());

        sgp_flush();
        sgp_end();

        sg_end_pass();
        sg_commit();

        _platform->swapBuffers();
    }
    else {
        sgp_flush();
        sgp_end();
        sg_end_pass();

        _renderTarget = nullptr;
    }
}

When I render to the screen, I can see everything is there (it's just one textured quad), but if I render to a framebuffer, all I see when I render it to the screen is a white quad. If I set a breakpoint at draw I can see that texture is not null, and all the other values are good. I can also see that the framebuffer path is being taken in start and present.

This is how I'm creating the framebuffer:

SokolRenderTarget(long const width, long const height, Filter const filter) : rm::RenderTarget(width, height) {
    {

        sg_image_desc desc = {};
        desc.render_target = true;
        desc.width = width;
        desc.height = height;
        desc.pixel_format = SG_PIXELFORMAT_RGBA8;
        desc.min_filter = filter == rm::Texture::Filter::Nearest ? SG_FILTER_NEAREST : SG_FILTER_LINEAR;
        desc.mag_filter = desc.min_filter;
        desc.wrap_u = SG_WRAP_CLAMP_TO_EDGE;
        desc.wrap_v = SG_WRAP_CLAMP_TO_EDGE;
        _target = sg_make_image(&desc);
    }

    {
        sg_desc sgdesc = sg_query_desc();

        sg_image_desc desc = {};
        desc.render_target = true;
        desc.width = width;
        desc.height = height;
        desc.pixel_format = SG_PIXELFORMAT_DEPTH_STENCIL;
        desc.sample_count = sgdesc.context.sample_count;
        _depth = sg_make_image(&desc);
    }

    {
        sg_pass_desc desc = {};
        desc.color_attachments[0].image = _target;
        desc.depth_stencil_attachment.image = _depth;
        _pass = sg_make_pass(&desc);
    }
}

I followed the framebuffer example to write the code, but the example is a bit contrived in that it follows a fixed path whereas I need the flexibility to draw anything, anytime to a framebuffer, but still it looks correct to me.

Any help is greatly appreciated.

Hm I've tried so many things that I can't tell why it's working now. It was my fault of course, as the framebuffer sample worked just fine.

Well, sorry for the noise, and thanks again for this great library.