Homebrew [Release] {beta} sf2dlib - Simple and Fast 2D library (using the GPU)

  • Thread starter Thread starter xerpi
  • Start date Start date
  • Views Views 97,763
  • Replies Replies 501
  • Likes Likes 32

xerpi

Well-Known Member
Member
Joined
Dec 25, 2011
Messages
215
Reaction score
759
Trophies
1
Age
30
Location
Tokyo
XP
1,574
Country
Spain
Hi there, lately I've been coding a GPU accelerated 2D library for the Nintendo 3DS, on top of ctrulib. I've got to the point where it starts to be usable, so I think it's time to release it.
This will benefit you if you want to code a 2D game/app/emulator, as you obviously will need to draw textures/images. Maybe software rendering is enough, but if you have a GPU out there, why don't you use it ? ;)

DOCUMENTATION: http://xerpi.github.io/sf2dlib/html/sf2d_8h.html#func-members

DOWNLOAD: https://github.com/xerpi/sf2dlib

Precompiled portlibs: https://github.com/xerpi/3ds_portlibs/releases/tag/1

If you need to load images, take a look at this lib: https://github.com/xerpi/sfillib

Here there are the functions SF2D provides:
Code:
int sf2d_init();
int sf2d_init_advanced(int gpucmd_size, int temppool_size);
int sf2d_fini();

void sf2d_set_3D(int enable);

void sf2d_start_frame(gfxScreen_t screen, gfx3dSide_t side);
void sf2d_end_frame();

void sf2d_swapbuffers();
void sf2d_set_vblank_wait(int enable);

float sf2d_get_fps();

void *sf2d_pool_malloc(u32 size);
void *sf2d_pool_memalign(u32 size, u32 alignment);
unsigned int sf2d_pool_space_free();
void sf2d_pool_reset();

void sf2d_set_clear_color(u32 color);

void sf2d_draw_line(int x0, int y0, int x1, int y1, u32 color);
void sf2d_draw_rectangle(int x, int y, int w, int h, u32 color);
void sf2d_draw_rectangle_rotate(int x, int y, int w, int h, u32 color, float rad);

sf2d_texture *sf2d_create_texture(int width, int height, GPU_TEXCOLOR pixel_format, sf2d_place place);
void sf2d_free_texture(sf2d_texture *texture);

void sf2d_fill_texture_from_RGBA8(sf2d_texture *dst, const void *rgba8, int source_w, int source_h);
sf2d_texture *sf2d_create_texture_mem_RGBA8(const void *src_buffer, int src_w, int src_h, GPU_TEXCOLOR pixel_format, sf2d_place place);

void sf2d_bind_texture(const sf2d_texture *texture, GPU_TEXUNIT unit);
void sf2d_bind_texture_color(const sf2d_texture *texture, GPU_TEXUNIT unit, u32 color);

void sf2d_draw_texture(const sf2d_texture *texture, int x, int y);
void sf2d_draw_texture_rotate(const sf2d_texture *texture, int x, int y, float rad);
void sf2d_draw_texture_part(const sf2d_texture *texture, int x, int y, int tex_x, int tex_y, int tex_w, int tex_h);
void sf2d_draw_texture_scale(const sf2d_texture *texture, int x, int y, float x_scale, float y_scale);
void sf2d_draw_texture_rotate_cut_scale(const sf2d_texture *texture, int x, int y, float rad, int tex_x, int tex_y, int tex_w, int tex_h, float x_scale, float y_scale);
void sf2d_draw_texture_blend(const sf2d_texture *texture, int x, int y, u32 color);
void sf2d_draw_texture_part_blend(const sf2d_texture *texture, int x, int y, int tex_x, int tex_y, int tex_w, int tex_h, u32 color);
void sf2d_draw_texture_depth(const sf2d_texture *texture, int x, int y, signed short z);
void sf2d_texture_tile32(sf2d_texture *texture);

void sf2d_set_scissor_test(GPU_SCISSORMODE mode, u32 x, u32 y, u32 w, u32 h);

gfxScreen_t sf2d_get_current_screen();
gfx3dSide_t sf2d_get_current_side();

The 2D coordinate system goes like this:
Code:
Top screen:
      X
  ---------->
  | (0,0)
  |  ______________________
Y | |                      |
  | |                      |
  v |                      |
    |                      |
    |                      |
    |______________________|
                      (400, 240)
Bottom screen:
        X
    ---------->
    | (0,0)
    |  ________________
  Y | |                |
    | |                |
    v |                |
      |                |
      |                |
      |________________|
                  (320, 240)
And the skeleton of a program using this lib:
Code:
int main()
{
    sf2d_init();
    sf2d_set_clear_color(RGBA8(0x00, 0x00, 0x00, 0x00));

    while (aptMainLoop()) {

        hidScanInput();
        if (hidKeysDown() & KEY_START) break;

        sf2d_start_frame(GFX_TOP, GFX_LEFT);
            //Draws a 100x100 yellow rectangle (255, 255, 00, 255) at (150, 70)
            sf2d_draw_rectangle(150, 70, 100, 100, RGBA8(0xFF, 0xFF, 0x00, 0xFF));
        sf2d_end_frame();

        sf2d_start_frame(GFX_BOTTOM, GFX_LEFT);
            //Draws a 70x100 blue rectangle (0, 0, 00, 255) at (120, 30)
            sf2d_draw_rectangle(120, 30, 70, 100, RGBA8(0x00, 0x00, 0xFF, 0xFF));
        sf2d_end_frame();

        sf2d_swapbuffers();
    }

    sf2d_fini();
    return 0;
}

A more complex sample: https://github.com/xerpi/sf2dlib/blob/master/sample/source/main.c

Screenshot of Citra emulator running a sample using my lib:
v93z1eU.png
 
Last edited by xerpi,
I was just starting converting lpp-3ds luaGraphics to ctrGL for GPU usage but this sounds much more better, very thanks for sharing this :D

No problem :D Btw this will be a 2D-only lib, so if you want to do some 3D on lpp-3ds maybe it's not the best option.
I'll add texture rotation/zoom support very soon!
 
No problem :D Btw this will be a 2D-only lib, so if you want to do some 3D on lpp-3ds maybe it's not the best option.
I'll add texture rotation/zoom support very soon!

luaGraphics module is only for 2D Graphics (probably for 3D i'll use another module name like lua3D).
 
  • Like
Reactions: Margen67
luaGraphics module is only for 2D Graphics (probably for 3D i'll use another module name like lua3D).

Cool! Btw switching from 2D to 3DS isn't hard, you mainly have to change the projection matrix (an uniform passed to the GPU) and maybe the shader.
 
Got an error during compilation, i'm using GPU folder from latest libctru revision:
Code:
c:/Users/Alessio/Desktop/lpp-3ds/source/include/sf2d/sf2d.c:48:58: error: request for member 'vertexShader' in something not a structure or union
  modelview_desc = shaderInstanceGetUniformLocation(shader.vertexShader, "modelview");
 
Got an error during compilation, i'm using GPU folder from latest libctru revision:
Code:
c:/Users/Alessio/Desktop/lpp-3ds/source/include/sf2d/sf2d.c:48:58: error: request for member 'vertexShader' in something not a structure or union
  modelview_desc = shaderInstanceGetUniformLocation(shader.vertexShader, "modelview");

Weird, are you sure you are using the latest ctrulib version?
I've shader defined as:
Code:
static shaderProgram_s shader;
And if you look at the source of ctrulib: https://github.com/smealum/ctrulib/blob/master/libctru/include/3ds/gpu/shaderProgram.h#L28
It has the vertexShader member.
 
  • Like
Reactions: xerpi
Ah yeah, forgot to say this, (0, 0) is top left.
2D coordinates go like this:
Code:
      X
---------->
| (0,0)
|  ______________________
Y|  |                      |
|  |                      |
v  |                      |
    |                      |
    |                      |
    |______________________|
                      (400, 240)

Perfect, i will not have to change the cord system i currently use :)
 
Yes, this also happens with the ctrulib's GPU sample.
Are you using my sf2d sample?

I just tried to blend two simple rectangles:

Code:
Graphics.init()
while true do
    Graphics.initBlend(TOP_SCREEN)
    Graphics.fillRect(50,100,50,100,Color.new(255,255,0,255))
    Graphics.termBlend()
    Graphics.initBlend(BOTTOM_SCREEN)
    Graphics.fillRect(70,120,100,120,Color.new(255,255,0,255))
    Graphics.termBlend()
end

where:
Code:
static int lua_init(lua_State *L) {
    int argc = lua_gettop(L);
    if (argc != 0) return luaL_error(L, "wrong number of arguments"); 
    sf2d_init();
    sf2d_set_clear_color(RGBA8(0x00, 0x00, 0x00, 0xFF));
    return 0;
}
 
static int lua_initblend(lua_State *L) {
    int argc = lua_gettop(L);
    if ((argc != 1) && (argc != 2))  return luaL_error(L, "wrong number of arguments");
    int screen = luaL_checkinteger(L,1);
    int side=0;
    if (argc == 2) side = luaL_checkinteger(L,2);
    gfxScreen_t my_screen;
    gfx3dSide_t eye;
    if (screen == 0) my_screen = GFX_TOP;
    else my_screen = GFX_BOTTOM;
    if (side == 0) eye = GFX_LEFT;
    else eye = GFX_RIGHT;
    sf2d_start_frame(my_screen,eye);
    return 0;
}
 
static int lua_rect(lua_State *L) {
    int argc = lua_gettop(L);
    if (argc != 5) return luaL_error(L, "wrong number of arguments");
    int x1 = luaL_checkinteger(L,1);
    int x2 = luaL_checkinteger(L,2);
    int y1 = luaL_checkinteger(L,3);
    int y2 = luaL_checkinteger(L,4);
    if (x2 < x1){
        int tmp = x2;
        x2 = x1;
        x1 = tmp;
    }
    if (y2 < y1){
        int tmp = y2;
        y2 = y1;
        y1 = tmp;
    }
    u32 color = luaL_checkinteger(L,5);
    sf2d_draw_rectangle(x1, y1, x2-x1, y2-y1, RGBA8((color >> 16) & 0xFF, (color >> 8) & 0xFF, (color) & 0xFF, (color >> 24) & 0xFF));
    return 0;
}
 
static int lua_termblend(lua_State *L) {
    int argc = lua_gettop(L);
    if (argc != 0) return luaL_error(L, "wrong number of arguments");
    sf2d_end_frame();
    return 0;
}
 
I just tried to blend two simple rectangles:


I see what's wrong, you have to call sf2d_swapbuffers(); once after rendering, something like this:
Code:
sf2d_start_frame(GFX_TOP, GFX_LEFT);
    //Draw top
sf2d_end_frame();
sf2d_start_frame(GFX_BOTTOM, GFX_LEFT);
    //Draw bottom
sf2d_end_frame();
sf2d_swapbuffers();
 

Site & Scene News

Popular threads in this forum