Add initial GL direct rendering test.

This commit is contained in:
Themaister 2013-03-27 16:15:15 +01:00
parent b8178a60eb
commit af40f3e9b8
11 changed files with 398 additions and 20 deletions

View File

@ -270,6 +270,16 @@ void driver_set_monitor_refresh_rate(float hz)
}
uintptr_t driver_get_current_framebuffer(void)
{
#ifdef HAVE_FBO
if (driver.video_poke && driver.video_poke->get_current_framebuffer)
return driver.video_poke->get_current_framebuffer(driver.video_data);
else
#endif
return 0;
}
// Only called once on init and deinit.
// Video and input drivers need to be active (owned)
// before retroarch core starts.

View File

@ -323,6 +323,7 @@ typedef struct video_poke_interface
#ifdef HAVE_FBO
void (*set_fbo_state)(void *data, unsigned state);
unsigned (*get_fbo_state)(void *data);
uintptr_t (*get_current_framebuffer)(void *data);
#endif
void (*set_aspect_ratio)(void *data, unsigned aspectratio_index);
void (*apply_state_changes)(void *data);
@ -446,6 +447,9 @@ void uninit_audio(void);
void driver_set_monitor_refresh_rate(float hz);
// Used by RETRO_ENVIRONMENT_SET_HW_RENDER.
uintptr_t driver_get_current_framebuffer(void);
extern driver_t driver;
//////////////////////////////////////////////// Backends

View File

@ -556,6 +556,15 @@ static bool environment_cb(unsigned cmd, void *data)
g_extern.system.disk_control = *(const struct retro_disk_control_callback*)data;
break;
case RETRO_ENVIRONMENT_SET_HW_RENDER:
{
RARCH_LOG("Environ SET_HW_RENDER.\n");
struct retro_hw_render_callback *cb = (struct retro_hw_render_callback*)data;
cb->get_current_framebuffer = driver_get_current_framebuffer;
memcpy(&g_extern.system.hw_render_callback, cb, sizeof(*cb));
break;
}
default:
RARCH_LOG("Environ UNSUPPORTED (#%u).\n", cmd);
return false;

View File

@ -392,6 +392,7 @@ struct global
retro_keyboard_event_t key_event;
struct retro_disk_control_callback disk_control;
struct retro_hw_render_callback hw_render_callback;
} system;
struct

103
gfx/gl.c
View File

@ -699,6 +699,27 @@ void gl_init_fbo(void *data, unsigned width, unsigned height)
gl->fbo_inited = true;
}
void gl_init_hw_render(gl_t *gl, unsigned width, unsigned height)
{
RARCH_LOG("[GL]: Initializing HW render (%u x %u).\n", width, height);
glBindTexture(GL_TEXTURE_2D, 0);
pglGenFramebuffers(TEXTURES, gl->hw_render_fbo);
for (unsigned i = 0; i < TEXTURES; i++)
{
pglBindFramebuffer(GL_FRAMEBUFFER, gl->hw_render_fbo[i]);
pglFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gl->texture[i], 0);
GLenum status = pglCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
RARCH_ERR("[GL]: Failed to create HW render FBO.\n");
}
pglBindFramebuffer(GL_FRAMEBUFFER, 0);
gl->hw_render_fbo_init = true;
}
#endif
void gl_set_projection(void *data, struct gl_ortho *ortho, bool allow_rotate)
@ -977,7 +998,7 @@ static void gl_update_resize(void *data)
#endif
}
static void gl_update_input_size(void *data, unsigned width, unsigned height, unsigned pitch)
static void gl_update_input_size(void *data, unsigned width, unsigned height, unsigned pitch, bool clear)
{
gl_t *gl = (gl_t*)data;
// Res change. Need to clear out texture.
@ -986,18 +1007,21 @@ static void gl_update_input_size(void *data, unsigned width, unsigned height, un
gl->last_width[gl->tex_index] = width;
gl->last_height[gl->tex_index] = height;
if (clear)
{
#if defined(HAVE_PSGL)
glBufferSubData(GL_TEXTURE_REFERENCE_BUFFER_SCE,
gl->tex_w * gl->tex_h * gl->tex_index * gl->base_size,
gl->tex_w * gl->tex_h * gl->base_size,
gl->empty_buf);
glBufferSubData(GL_TEXTURE_REFERENCE_BUFFER_SCE,
gl->tex_w * gl->tex_h * gl->tex_index * gl->base_size,
gl->tex_w * gl->tex_h * gl->base_size,
gl->empty_buf);
#else
glPixelStorei(GL_UNPACK_ALIGNMENT, get_alignment(width * sizeof(uint32_t)));
glPixelStorei(GL_UNPACK_ALIGNMENT, get_alignment(width * sizeof(uint32_t)));
glTexSubImage2D(GL_TEXTURE_2D,
0, 0, 0, gl->tex_w, gl->tex_h, gl->texture_type,
gl->texture_fmt, gl->empty_buf);
glTexSubImage2D(GL_TEXTURE_2D,
0, 0, 0, gl->tex_w, gl->tex_h, gl->texture_type,
gl->texture_fmt, gl->empty_buf);
#endif
}
GLfloat xamt = (GLfloat)width / gl->tex_w;
GLfloat yamt = (GLfloat)height / gl->tex_h;
@ -1360,16 +1384,36 @@ static bool gl_frame(void *data, const void *frame, unsigned width, unsigned hei
gl->tex_index = (gl->tex_index + 1) & TEXTURES_MASK;
glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]);
gl_update_input_size(gl, width, height, pitch);
#ifdef HAVE_FBO
// Data is already on GPU :) Have to reset some state however incase core changed it.
if (gl->hw_render_fbo_init)
{
gl_update_input_size(gl, width, height, pitch, false);
#ifndef HAVE_OPENGLES
glEnable(GL_TEXTURE_2D);
#endif
glDisable(GL_DEPTH_TEST);
glDisable(GL_DITHER);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
RARCH_PERFORMANCE_INIT(copy_frame);
RARCH_PERFORMANCE_START(copy_frame);
gl_copy_frame(gl, frame, width, height, pitch);
RARCH_PERFORMANCE_STOP(copy_frame);
if (!gl->fbo_inited)
pglBindFramebuffer(GL_FRAMEBUFFER, 0);
gl_set_viewport(gl, gl->win_width, gl->win_height, false, true);
}
else
#endif
{
gl_update_input_size(gl, width, height, pitch, true);
RARCH_PERFORMANCE_INIT(copy_frame);
RARCH_PERFORMANCE_START(copy_frame);
gl_copy_frame(gl, frame, width, height, pitch);
RARCH_PERFORMANCE_STOP(copy_frame);
#ifdef IOS // Apparently the viewport is lost each frame, thanks apple.
gl_set_viewport(gl, gl->win_width, gl->win_height, false, true);
gl_set_viewport(gl, gl->win_width, gl->win_height, false, true);
#endif
}
}
else
glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]);
@ -1514,6 +1558,10 @@ static void gl_free(void *data)
#ifdef HAVE_FBO
gl_deinit_fbo(gl);
if (gl->hw_render_fbo_init)
pglDeleteFramebuffers(TEXTURES, gl->hw_render_fbo);
gl->hw_render_fbo_init = false;
#endif
context_destroy_func();
@ -1814,11 +1862,6 @@ static void *gl_init(const video_info_t *video, const input_driver_t **input, vo
gl->tex_w = RARCH_SCALE_BASE * video->input_scale;
gl->tex_h = RARCH_SCALE_BASE * video->input_scale;
#ifdef HAVE_FBO
// Set up render to texture.
gl_init_fbo(gl, gl->tex_w, gl->tex_h);
#endif
gl->keep_aspect = video->force_aspect;
// Apparently need to set viewport for passes when we aren't using FBOs.
@ -1863,6 +1906,14 @@ static void *gl_init(const video_info_t *video, const input_driver_t **input, vo
gl_init_textures(gl, video);
gl_init_textures_data(gl);
#ifdef HAVE_FBO
// Set up render to texture.
gl_init_fbo(gl, gl->tex_w, gl->tex_h);
if (g_extern.system.hw_render_callback.context_type == RETRO_HW_CONTEXT_OPENGL)
gl_init_hw_render(gl, gl->tex_w, gl->tex_h);
#endif
if (input && input_data)
context_input_driver_func(input, input_data);
@ -1881,6 +1932,11 @@ static void *gl_init(const video_info_t *video, const input_driver_t **input, vo
return NULL;
}
#ifdef HAVE_FBO
if (gl->hw_render_fbo_init)
g_extern.system.hw_render_callback.context_reset();
#endif
return gl;
}
@ -2313,6 +2369,12 @@ static unsigned gl_get_fbo_state(void *data)
gl_t *gl = (gl_t*)data;
return gl->fbo_inited ? FBO_INIT : FBO_DEINIT;
}
static uintptr_t gl_get_current_framebuffer(void *data)
{
gl_t *gl = (gl_t*)data;
return gl->hw_render_fbo[(gl->tex_index + 1) & TEXTURES_MASK];
}
#endif
static void gl_set_aspect_ratio(void *data, unsigned aspectratio_index)
@ -2371,6 +2433,7 @@ static const video_poke_interface_t gl_poke_interface = {
#ifdef HAVE_FBO
gl_set_fbo_state,
gl_get_fbo_state,
gl_get_current_framebuffer,
#endif
gl_set_aspect_ratio,
gl_apply_state_changes,

View File

@ -242,6 +242,9 @@ typedef struct gl
struct gl_fbo_scale fbo_scale[MAX_SHADERS];
int fbo_pass;
bool fbo_inited;
GLuint hw_render_fbo[TEXTURES];
bool hw_render_fbo_init;
#endif
bool should_resize;

View File

@ -678,6 +678,7 @@ static const video_poke_interface_t thread_poke = {
#ifdef HAVE_FBO
thread_set_fbo_state,
thread_get_fbo_state,
NULL,
#endif
thread_set_aspect_ratio,
thread_apply_state_changes,

53
libretro-test-gl/Makefile Normal file
View File

@ -0,0 +1,53 @@
ifeq ($(platform),)
platform = unix
ifeq ($(shell uname -a),)
platform = win
else ifneq ($(findstring MINGW,$(shell uname -a)),)
platform = win
else ifneq ($(findstring Darwin,$(shell uname -a)),)
platform = osx
else ifneq ($(findstring win,$(shell uname -a)),)
platform = win
endif
endif
ifeq ($(platform), unix)
TARGET := libretro.so
fpic := -fPIC
SHARED := -shared -Wl,--version-script=link.T -Wl,--no-undefined
LIBS := -lGL
else ifeq ($(platform), osx)
TARGET := libretro.dylib
fpic := -fPIC
SHARED := -dynamiclib
LIBS := -framework OpenGL
else
CC = gcc
TARGET := retro.dll
SHARED := -shared -static-libgcc -static-libstdc++ -s -Wl,--version-script=link.T -Wl,--no-undefined
LIBS := -lopengl32
endif
ifeq ($(DEBUG), 1)
CFLAGS += -O0 -g
else
CFLAGS += -O3
endif
OBJECTS := libretro-test.o
CFLAGS += -std=gnu99 -Wall -pedantic $(fpic)
all: $(TARGET)
$(TARGET): $(OBJECTS)
$(CC) $(fpic) $(SHARED) $(LIBS) $(INCLUDES) -o $@ $(OBJECTS) -lm
%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
clean:
rm -f $(OBJECTS) $(TARGET)
.PHONY: clean

View File

@ -0,0 +1,193 @@
#include "../libretro.h"
#include <stdint.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <GL/gl.h>
#include <GL/glext.h>
static uint16_t *frame_buf;
void retro_init(void)
{
frame_buf = calloc(320 * 240, sizeof(uint16_t));
}
void retro_deinit(void)
{
free(frame_buf);
frame_buf = NULL;
}
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 = 320,
.base_height = 240,
.max_width = 320,
.max_height = 240,
.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;
}
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 struct retro_hw_render_callback hw_render;
void retro_run(void)
{
input_poll_cb();
static unsigned frame_count = 0;
frame_count = (frame_count + 1) % 60;
glBindFramebuffer(GL_FRAMEBUFFER, hw_render.get_current_framebuffer());
glViewport(0, 0, 320, 240);
glClearColor(frame_count / 120.0, frame_count / 60.0, frame_count / 60.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
video_cb(RETRO_HW_FRAME_BUFFER_VALID, 320, 240, 0);
}
static void context_reset(void)
{
fprintf(stderr, "Context reset!\n");
}
bool retro_load_game(const struct retro_game_info *info)
{
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;
}
hw_render.context_type = RETRO_HW_CONTEXT_OPENGL;
hw_render.context_reset = context_reset;
if (!environ_cb(RETRO_ENVIRONMENT_SET_HW_RENDER, &hw_render))
return false;
(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;
}

5
libretro-test-gl/link.T Normal file
View File

@ -0,0 +1,5 @@
{
global: retro_*;
local: *;
};

View File

@ -421,8 +421,44 @@ enum retro_mod
// Sets an interface which frontend can use to eject and insert disk images.
// This is used for games which consist of multiple images and must be manually
// swapped out by the user (e.g. PSX).
#define RETRO_ENVIRONMENT_SET_HW_RENDER 0x10000
// struct retro_hw_render_callback * --
// NOTE: This call is currently very experimental, and should not be considered part of the public API.
// The interface could be changed or removed at any time.
// Sets an interface to let a libretro core render with hardware acceleration.
// Should be called in retro_load_game().
// If successful, libretro cores will be able to render to a frontend-provided framebuffer.
// The size of this framebuffer will be at least as large as max_width/max_height provided in get_av_info().
// If HW rendering is used, pass only RETRO_HW_FRAME_BUFFER_VALID or NULL to retro_video_refresh_t.
// Pass this to retro_video_refresh_t if rendering to hardware.
// Passing NULL to retro_video_refresh_t is still a frame dupe as normal.
#define RETRO_HW_FRAME_BUFFER_VALID ((void*)-1)
// Invalidates the current HW context.
// If called, all GPU resources must be reinitialized.
// Usually called when frontend reinits video driver.
// Also called first time video driver is initialized, allowing libretro core to init resources.
typedef void (*retro_hw_context_reset_t)(void);
// Gets current framebuffer which is to be rendered to. Could change every frame potentially.
typedef uintptr_t (*retro_hw_get_current_framebuffer_t)(void);
enum retro_hw_context_type
{
RETRO_HW_CONTEXT_NONE = 0,
RETRO_HW_CONTEXT_OPENGL,
RETRO_HW_CONTEXT_DUMMY = INT_MAX
};
struct retro_hw_render_callback
{
enum retro_hw_context_type context_type; // Which API to use. Set by libretro core.
retro_hw_context_reset_t context_reset; // Set by libretro core.
retro_hw_get_current_framebuffer_t get_current_framebuffer; // Set by frontend.
};
// Callback type passed in RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK. Called by the frontend in response to keyboard events.
// down is set if the key is being pressed, or false if it is being released.
// keycode is the RETROK value of the char.