diff --git a/test/meson.build b/test/meson.build index 54723d22..052aa524 100644 --- a/test/meson.build +++ b/test/meson.build @@ -8,3 +8,5 @@ if opt_tests.disabled() endif test_incdir = include_directories('.') + +subdir('renderer') diff --git a/test/renderer/meson.build b/test/renderer/meson.build new file mode 100644 index 00000000..42c8cb99 --- /dev/null +++ b/test/renderer/meson.build @@ -0,0 +1,13 @@ + +tests = [ + 'triangle', +] + +foreach test : tests + executable( + test, '@0@.c'.format(test), + dependencies : libtaisei_dep, + include_directories : test_incdir, + install : false, + ) +endforeach diff --git a/test/renderer/test_renderer.h b/test/renderer/test_renderer.h new file mode 100644 index 00000000..8aa82e3a --- /dev/null +++ b/test/renderer/test_renderer.h @@ -0,0 +1,96 @@ + +#include "taisei.h" + +#include "renderer/api.h" +#include "events.h" +#include "log.h" +#include "util/compat.h" +#include "video.h" +#include "config.h" + +#include + +static void test_init_log(void) { + log_init(LOG_ALL); + log_add_output(LOG_ALL, SDL_RWFromFP(stderr, false), log_formatter_console); +} + +static void test_init_sdl(void) { + mem_install_sdl_callbacks(); + + if(SDL_Init(SDL_INIT_EVENTS) < 0) { + log_fatal("SDL_Init() failed: %s", SDL_GetError()); + } + + events_init(); +} + +static void test_init_basic(void) { + setlocale(LC_ALL, "C"); + test_init_log(); + test_init_sdl(); +} + +static ShaderObject *test_renderer_load_glsl(ShaderStage stage, const char *src) { + // TODO: This is mostly copypasted from resource/shader_object; add a generic API for this + + ShaderSource s = { + .content = (char*)src, + .content_size = strlen(src), + .stage = stage, + .lang = { + .lang = SHLANG_GLSL, + .glsl.version = { 330, GLSL_PROFILE_CORE } + }, + }; + + ShaderLangInfo altlang = { SHLANG_INVALID }; + + if(!r_shader_language_supported(&s.lang, &altlang)) { + if(altlang.lang == SHLANG_INVALID) { + log_fatal("Shading language not supported by backend"); + } + + log_warn("Shading language not supported by backend, attempting to translate"); + assert(r_shader_language_supported(&altlang, NULL)); + + spirv_init_compiler(); + + ShaderSource newsrc; + bool result = spirv_transpile(&s, &newsrc, &(SPIRVTranspileOptions) { + .lang = &altlang, + .optimization_level = SPIRV_OPTIMIZE_NONE, + .filename = "", + }); + + if(!result) { + log_fatal("Shader translation failed"); + } + + s = newsrc; + } + + ShaderObject *obj = r_shader_object_compile(&s); + + if(!obj) { + log_fatal("Failed to compile shader"); + } + + if(s.content != src) { + shader_free_source(&s); + } + + return obj; +} + +static void test_init_renderer(void) { + test_init_basic(); + + config_set_int(CONFIG_VSYNC, 1); + config_set_int(CONFIG_VID_RESIZABLE, 1); + + video_init(&(VideoInitParams) { + .width = 800, + .height = 600, + }); +} diff --git a/test/renderer/triangle.c b/test/renderer/triangle.c new file mode 100644 index 00000000..a31e7a60 --- /dev/null +++ b/test/renderer/triangle.c @@ -0,0 +1,85 @@ + +#include "taisei.h" + +#include "test_renderer.h" +#include "resource/model.h" +#include "global.h" + +typedef struct Vertex2D { + vec2 pos; + Color color; +} Vertex2D; + +int main(int argc, char **argv) { + test_init_renderer(); + + const char *vert_shader_src = + "#version 330\n" + "layout(location = 0) in vec2 a_position;\n" + "layout(location = 1) in vec4 a_color;\n" + "out vec4 v_color;\n" + "void main(void) {\n" + " gl_Position = vec4(a_position, 0, 1);\n" + " v_color = a_color;\n" + "}\n"; + + const char *frag_shader_src = + "#version 330\n" + "in vec4 v_color;\n" + "layout(location = 0) out vec4 o_color;\n" + "void main(void) {\n" + " o_color = 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 }, + { 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.0f, -1.0f }, { 1.0f, 0.0f, 0.0f, 1.0f } }, + { { 0.0f, 1.0f }, { 0.0f, 1.0f, 0.0f, 1.0f } }, + { { 1.0f, -1.0f }, { 0.0f, 0.0f, 1.0f, 1.0f } }, + }; + + 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_TRIANGLES, + .vertex_array = vert_array, + .num_vertices = ARRAY_SIZE(vertices), + .offset = 0, + }; + + r_shader_ptr(prog); + + while(!taisei_quit_requested()) { + r_clear(BUFFER_COLOR, RGB(0, 0, 0), 1); + r_draw_model_ptr(&triangle, 1, 0); + events_poll(NULL, 0); + video_swap_buffers(); + } + + r_vertex_buffer_destroy(vert_buf); + r_vertex_array_destroy(vert_array); + + // FIXME doesn't work when "default" shader is not loaded + // r_shader_program_destroy(prog); + + return 0; +}