diff --git a/test/renderer/meson.build b/test/renderer/meson.build index 42c8cb99..0c12f527 100644 --- a/test/renderer/meson.build +++ b/test/renderer/meson.build @@ -1,6 +1,7 @@ tests = [ 'triangle', + 'texture', ] foreach test : tests diff --git a/test/renderer/texture.c b/test/renderer/texture.c new file mode 100644 index 00000000..37e1ca08 --- /dev/null +++ b/test/renderer/texture.c @@ -0,0 +1,161 @@ + +#include "taisei.h" + +#include "test_renderer.h" +#include "global.h" + +typedef struct Vertex2D { + vec2 pos; + vec2 uv; + vec4 color; +} Vertex2D; + +static Texture *load_texture(const char *path) { + auto istream = SDL_RWFromFile(path, "rb"); + + if(!istream) { + log_sdl_error(LOG_FATAL, "SDL_IOFromFile"); + } + + Pixmap px = {}; + PixmapFormat fmt = PIXMAP_FORMAT_RGBA8; + PixmapOrigin origin = PIXMAP_ORIGIN_TOPLEFT; + + if(r_features() & RFEAT_TEXTURE_BOTTOMLEFT_ORIGIN) { + origin = PIXMAP_ORIGIN_BOTTOMLEFT; + } + + if(!pixmap_load_stream(istream, PIXMAP_FILEFORMAT_AUTO, &px, fmt)) { + log_fatal("pixmap_load_stream() failed"); + } + + SDL_RWclose(istream); + + pixmap_convert_inplace_realloc(&px, fmt); + pixmap_flip_to_origin_inplace(&px, origin); + + Texture *tex = r_texture_create(&(TextureParams) { + .class = TEXTURE_CLASS_2D, + .type = r_texture_type_from_pixmap_format(fmt), + .width = px.width, + .height = px.height, + .layers = 1, + .filter.min = TEX_FILTER_LINEAR, + .filter.mag = TEX_FILTER_LINEAR, + .wrap.s = TEX_WRAP_REPEAT, + .wrap.t = TEX_WRAP_REPEAT, + }); + + if(!tex) { + log_fatal("r_texture_create() failed"); + } + + r_texture_fill(tex, 0, 0, &px); + mem_free(px.data.untyped); + + return tex; +} + +int main(int argc, char **argv) { + test_init_renderer(); + time_init(); + + FPSCounter fps = {}; + + const char *vert_shader_src = + "#version 420\n" + "layout(location = 0) in vec2 a_position;\n" + "layout(location = 1) in vec2 a_uv;\n" + "layout(location = 2) in vec4 a_color;\n" + "layout(location = 0) out vec2 v_uv;\n" + "layout(location = 1) out vec4 v_color;\n" + "void main(void) {\n" + " gl_Position = vec4(a_position, 0, 1);\n" + " v_uv = a_uv;\n" + " v_color = a_color;\n" + "}\n"; + + const char *frag_shader_src = + "#version 420\n" + "layout(location = 0) in vec2 v_uv;\n" + "layout(location = 1) in vec4 v_color;\n" + "layout(location = 0) out vec4 o_color;\n" + "uniform sampler2D u_sampler;\n" + "void main(void) {\n" + " o_color = texture(u_sampler, v_uv) * v_color;\n" + "}\n"; + + ShaderObject *vert_obj = test_renderer_load_glsl(SHADER_STAGE_VERTEX, vert_shader_src); + ShaderObject *frag_obj = test_renderer_load_glsl(SHADER_STAGE_FRAGMENT, frag_shader_src); + ShaderProgram *prog = r_shader_program_link(2, (ShaderObject*[]) { vert_obj, frag_obj }); + r_shader_object_destroy(vert_obj); + r_shader_object_destroy(frag_obj); + + VertexAttribSpec va_spec[] = { + { 2, VA_FLOAT, VA_CONVERT_FLOAT }, + { 2, VA_FLOAT, VA_CONVERT_FLOAT }, + { 4, VA_FLOAT, VA_CONVERT_FLOAT }, + }; + + VertexAttribFormat va_format[ARRAY_SIZE(va_spec)]; + r_vertex_attrib_format_interleaved(ARRAY_SIZE(va_spec), va_spec, va_format, 0); + + Vertex2D vertices[] = { + // { { 1, -1, }, { 1, 1 }, { 1, 0, 0, 1 }, }, + // { { 1, 1, }, { 1, 0 }, { 0, 1, 0, 1 }, }, + // { { -1, -1, }, { 0, 1 }, { 0, 0, 1, 1 }, }, + // { { -1, 1, }, { 0, 0 }, { 1, 1, 0, 1 }, }, + + { { 1, -1, }, { 1, 0 }, { 1, 0, 0, 1 }, }, + { { 1, 1, }, { 1, 1 }, { 0, 1, 0, 1 }, }, + { { -1, -1, }, { 0, 0 }, { 0, 0, 1, 1 }, }, + { { -1, 1, }, { 0, 1 }, { 1, 1, 0, 1 }, }, + }; + + VertexBuffer *vert_buf = r_vertex_buffer_create(sizeof(vertices), vertices); + r_vertex_buffer_set_debug_label(vert_buf, "Triangle vertex buffer"); + + VertexArray *vert_array = r_vertex_array_create(); + r_vertex_array_set_debug_label(vert_array, "Triangle vertex array"); + r_vertex_array_layout(vert_array, ARRAY_SIZE(va_format), va_format); + r_vertex_array_attach_vertex_buffer(vert_array, vert_buf, 0); + + Model triangle = { + .primitive = PRIM_TRIANGLE_STRIP, + .vertex_array = vert_array, + .num_vertices = ARRAY_SIZE(vertices), + .offset = 0, + }; + + Texture *tex = load_texture("texture0.webp"); + + r_shader_ptr(prog); + auto u_sampler = r_shader_uniform(prog, "u_sampler"); + + hrtime_t last_print_time = 0; + + while(!taisei_quit_requested()) { + r_clear(BUFFER_COLOR, RGB(0, 0, 0), 1); + r_uniform_sampler(u_sampler, tex); + r_draw_model_ptr(&triangle, 1, 0); + events_poll(NULL, 0); + video_swap_buffers(); + + fpscounter_update(&fps); + + hrtime_t t = time_get(); + if(t - last_print_time > HRTIME_RESOLUTION) { + last_print_time = t; + log_debug("%.02f FPS", fps.fps); + } + } + + r_vertex_buffer_destroy(vert_buf); + r_vertex_array_destroy(vert_array); + + r_shader_program_destroy(prog); + + time_shutdown(); + test_shutdown_renderer(); + return 0; +} diff --git a/test/renderer/texture0.webp b/test/renderer/texture0.webp new file mode 100644 index 00000000..12190aae Binary files /dev/null and b/test/renderer/texture0.webp differ