#include "../libretro.h" #include #include #include #include #include #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) static struct retro_hw_render_callback hw_render; #include "../gfx/glsym/glsym.h" #define BASE_WIDTH 320 #define BASE_HEIGHT 240 #ifdef GLES #define MAX_WIDTH 1024 #define MAX_HEIGHT 1024 #else #define MAX_WIDTH 1920 #define MAX_HEIGHT 1600 #endif static unsigned width = BASE_WIDTH; static unsigned height = BASE_HEIGHT; static GLuint prog; static GLuint vbo; #ifdef CORE static bool context_alive; static bool multisample_fbo; static unsigned multisample; static GLuint vao; static GLuint fbo; static GLuint rbo_color, rbo_depth_stencil; #endif static const GLfloat vertex_data[] = { -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, }; #ifdef CORE static const char *vertex_shader[] = { "#version 140\n" "uniform mat4 uMVP;", "in vec2 aVertex;", "in vec4 aColor;", "out vec4 color;", "void main() {", " gl_Position = uMVP * vec4(aVertex, 0.0, 1.0);", " color = aColor;", "}", }; static const char *fragment_shader[] = { "#version 140\n" "in vec4 color;", "out vec4 FragColor;\n" "void main() {", " FragColor = color;", "}", }; #else static const char *vertex_shader[] = { "uniform mat4 uMVP;", "attribute vec2 aVertex;", "attribute vec4 aColor;", "varying vec4 color;", "void main() {", " gl_Position = uMVP * vec4(aVertex, 0.0, 1.0);", " color = aColor;", "}", }; static const char *fragment_shader[] = { "#ifdef GL_ES\n", "precision mediump float;\n", "#endif\n", "varying vec4 color;", "void main() {", " gl_FragColor = color;", "}", }; #endif static void compile_program(void) { prog = glCreateProgram(); GLuint vert = glCreateShader(GL_VERTEX_SHADER); GLuint frag = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(vert, ARRAY_SIZE(vertex_shader), vertex_shader, 0); glShaderSource(frag, ARRAY_SIZE(fragment_shader), fragment_shader, 0); glCompileShader(vert); glCompileShader(frag); glAttachShader(prog, vert); glAttachShader(prog, frag); glLinkProgram(prog); glDeleteShader(vert); glDeleteShader(frag); } #ifdef CORE static void init_multisample(unsigned samples) { multisample = samples; if (!context_alive) return; if (rbo_color) glDeleteRenderbuffers(1, &rbo_color); if (rbo_depth_stencil) glDeleteRenderbuffers(1, &rbo_depth_stencil); if (fbo) glDeleteFramebuffers(1, &fbo); rbo_color = rbo_depth_stencil = fbo = 0; multisample_fbo = false; if (samples <= 1) return; if (glRenderbufferStorageMultisample) { glGenRenderbuffers(1, &rbo_color); glGenRenderbuffers(1, &rbo_depth_stencil); glGenFramebuffers(1, &fbo); glBindRenderbuffer(GL_RENDERBUFFER, rbo_color); glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_RGBA, MAX_WIDTH, MAX_HEIGHT); glBindRenderbuffer(GL_RENDERBUFFER, rbo_depth_stencil); glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_DEPTH24_STENCIL8, MAX_WIDTH, MAX_HEIGHT); glBindRenderbuffer(GL_RENDERBUFFER, 0); glGenFramebuffers(1, &fbo); glBindFramebuffer(GL_FRAMEBUFFER, fbo); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo_color); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo_depth_stencil); GLenum ret = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (ret == GL_FRAMEBUFFER_COMPLETE) { fprintf(stderr, "Using multisampled FBO.\n"); multisample_fbo = true; } else fprintf(stderr, "Multisampled FBO failed.\n"); glBindFramebuffer(GL_FRAMEBUFFER, 0); } else fprintf(stderr, "Multisampled FBOs not supported.\n"); } #endif static void setup_vao(void) { #ifdef CORE glGenVertexArrays(1, &vao); #endif glUseProgram(prog); glGenBuffers(1, &vbo); glBindBuffer(GL_ARRAY_BUFFER, vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); glUseProgram(0); } void retro_init(void) {} void retro_deinit(void) {} unsigned retro_api_version(void) { return RETRO_API_VERSION; } void retro_set_controller_port_device(unsigned port, unsigned device) { (void)port; (void)device; } void retro_get_system_info(struct retro_system_info *info) { memset(info, 0, sizeof(*info)); info->library_name = "TestCore GL"; info->library_version = "v1"; info->need_fullpath = false; info->valid_extensions = NULL; // Anything is fine, we don't care. } void retro_get_system_av_info(struct retro_system_av_info *info) { info->timing = (struct retro_system_timing) { .fps = 60.0, .sample_rate = 30000.0, }; info->geometry = (struct retro_game_geometry) { .base_width = BASE_WIDTH, .base_height = BASE_HEIGHT, .max_width = MAX_WIDTH, .max_height = MAX_HEIGHT, .aspect_ratio = 4.0 / 3.0, }; } static retro_video_refresh_t video_cb; static retro_audio_sample_t audio_cb; static retro_audio_sample_batch_t audio_batch_cb; static retro_environment_t environ_cb; static retro_input_poll_t input_poll_cb; static retro_input_state_t input_state_cb; void retro_set_environment(retro_environment_t cb) { environ_cb = cb; struct retro_variable variables[] = { { "testgl_resolution", #ifdef GLES "Internal resolution; 320x240|360x480|480x272|512x384|512x512|640x240|640x448|640x480|720x576|800x600|960x720|1024x768", #else "Internal resolution; 320x240|360x480|480x272|512x384|512x512|640x240|640x448|640x480|720x576|800x600|960x720|1024x768|1024x1024|1280x720|1280x960|1600x1200|1920x1080|1920x1440|1920x1600", #endif }, #ifdef CORE { "testgl_multisample", "Multisampling; 1x|2x|4x" }, #endif { NULL, NULL }, }; bool no_rom = true; cb(RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME, &no_rom); cb(RETRO_ENVIRONMENT_SET_VARIABLES, variables); } void retro_set_audio_sample(retro_audio_sample_t cb) { audio_cb = cb; } void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) { audio_batch_cb = cb; } void retro_set_input_poll(retro_input_poll_t cb) { input_poll_cb = cb; } void retro_set_input_state(retro_input_state_t cb) { input_state_cb = cb; } void retro_set_video_refresh(retro_video_refresh_t cb) { video_cb = cb; } static void update_variables(void) { struct retro_variable var = { .key = "testgl_resolution", }; if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { char *pch; char str[100]; snprintf(str, sizeof(str), "%s", var.value); pch = strtok(str, "x"); if (pch) width = strtoul(pch, NULL, 0); pch = strtok(NULL, "x"); if (pch) height = strtoul(pch, NULL, 0); fprintf(stderr, "[libretro-test]: Got size: %u x %u.\n", width, height); } #ifdef CORE var.key = "testgl_multisample"; var.value = NULL; if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { switch (*var.value) { case '1': init_multisample(1); break; case '2': init_multisample(2); break; case '4': init_multisample(4); break; } } #endif } void retro_run(void) { bool updated = false; if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated) update_variables(); input_poll_cb(); #ifdef CORE glBindVertexArray(vao); if (multisample_fbo) glBindFramebuffer(GL_FRAMEBUFFER, fbo); else #endif glBindFramebuffer(GL_FRAMEBUFFER, hw_render.get_current_framebuffer()); glClearColor(0.3, 0.4, 0.5, 1.0); glViewport(0, 0, width, height); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glUseProgram(prog); glEnable(GL_DEPTH_TEST); glBindBuffer(GL_ARRAY_BUFFER, vbo); int vloc = glGetAttribLocation(prog, "aVertex"); glVertexAttribPointer(vloc, 2, GL_FLOAT, GL_FALSE, 0, (void*)0); glEnableVertexAttribArray(vloc); int cloc = glGetAttribLocation(prog, "aColor"); glVertexAttribPointer(cloc, 4, GL_FLOAT, GL_FALSE, 0, (void*)(8 * sizeof(GLfloat))); glEnableVertexAttribArray(cloc); glBindBuffer(GL_ARRAY_BUFFER, 0); int loc = glGetUniformLocation(prog, "uMVP"); static unsigned frame_count; frame_count++; float angle = frame_count / 100.0; float cos_angle = cos(angle); float sin_angle = sin(angle); const GLfloat mvp[] = { cos_angle, -sin_angle, 0, 0, sin_angle, cos_angle, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, }; glUniformMatrix4fv(loc, 1, GL_FALSE, mvp); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); cos_angle *= 0.5; sin_angle *= 0.5; const GLfloat mvp2[] = { cos_angle, -sin_angle, 0, 0.0, sin_angle, cos_angle, 0, 0.0, 0, 0, 1, 0, 0.4, 0.4, 0.2, 1, }; glUniformMatrix4fv(loc, 1, GL_FALSE, mvp2); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDisableVertexAttribArray(vloc); glDisableVertexAttribArray(cloc); glUseProgram(0); #ifdef CORE glBindVertexArray(0); if (multisample_fbo) // Resolve the multisample. { glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, hw_render.get_current_framebuffer()); glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST); glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); } #endif video_cb(RETRO_HW_FRAME_BUFFER_VALID, width, height, 0); } static void context_reset(void) { fprintf(stderr, "Context reset!\n"); rglgen_resolve_symbols(hw_render.get_proc_address); compile_program(); setup_vao(); #ifdef CORE context_alive = true; init_multisample(multisample); #endif } static void context_destroy(void) { fprintf(stderr, "Context destroy!\n"); #ifdef CORE glDeleteVertexArrays(1, &vao); vao = 0; init_multisample(0); context_alive = false; #endif glDeleteBuffers(1, &vbo); vbo = 0; glDeleteProgram(prog); prog = 0; } bool retro_load_game(const struct retro_game_info *info) { update_variables(); enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_XRGB8888; if (!environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt)) { fprintf(stderr, "XRGB8888 is not supported.\n"); return false; } #ifdef GLES #if defined(GLES31) hw_render.context_type = RETRO_HW_CONTEXT_OPENGLES_VERSION; hw_render.version_major = 3; hw_render.version_minor = 1; #elif defined(GLES3) hw_render.context_type = RETRO_HW_CONTEXT_OPENGLES3; #else hw_render.context_type = RETRO_HW_CONTEXT_OPENGLES2; #endif #else #ifdef CORE hw_render.context_type = RETRO_HW_CONTEXT_OPENGL_CORE; hw_render.version_major = 3; hw_render.version_minor = 1; #else hw_render.context_type = RETRO_HW_CONTEXT_OPENGL; #endif #endif hw_render.context_reset = context_reset; hw_render.context_destroy = context_destroy; hw_render.depth = true; hw_render.stencil = true; hw_render.bottom_left_origin = true; if (!environ_cb(RETRO_ENVIRONMENT_SET_HW_RENDER, &hw_render)) return false; fprintf(stderr, "Loaded game!\n"); (void)info; return true; } void retro_unload_game(void) {} unsigned retro_get_region(void) { return RETRO_REGION_NTSC; } bool retro_load_game_special(unsigned type, const struct retro_game_info *info, size_t num) { (void)type; (void)info; (void)num; return false; } size_t retro_serialize_size(void) { return 0; } bool retro_serialize(void *data, size_t size) { (void)data; (void)size; return false; } bool retro_unserialize(const void *data, size_t size) { (void)data; (void)size; return false; } void *retro_get_memory_data(unsigned id) { (void)id; return NULL; } size_t retro_get_memory_size(unsigned id) { (void)id; return 0; } void retro_reset(void) {} void retro_cheat_reset(void) {} void retro_cheat_set(unsigned index, bool enabled, const char *code) { (void)index; (void)enabled; (void)code; }