Commit the new X11 user interface.

Streamline the design/layout of the UI code. An excellent,
unexpected side-effect of this commit resulted in a significant
increase in performance...
This commit is contained in:
Tyler J. Stachecki 2015-05-16 12:52:43 -04:00
parent bf38ee1879
commit d348c23e2f
12 changed files with 393 additions and 259 deletions

View file

@ -261,6 +261,7 @@ if (DEFINED UNIX)
find_package(X11 REQUIRED)
include_directories(${X11_xf86vmode_INCLUDE_PATH})
include_directories(${PROJECT_SOURCE_DIR}/os/x11)
include_directories(${PROJECT_SOURCE_DIR}/os/posix)
set(EXTRA_OS_LIBS ${X11_X11_LIB} ${X11_Xxf86vm_LIB})
set(EXTRA_OS_EXE "")
@ -270,6 +271,7 @@ if (DEFINED UNIX)
${PROJECT_SOURCE_DIR}/os/x11/gl_config.c
${PROJECT_SOURCE_DIR}/os/x11/gl_window.c
${PROJECT_SOURCE_DIR}/os/posix/alloc.c
${PROJECT_SOURCE_DIR}/os/posix/timer.c
${PROJECT_SOURCE_DIR}/os/posix/rom_file.c
)

View file

@ -14,7 +14,7 @@
#include "os/windows/keycodes.h"
#else
#include "os/unix/x11/keycodes.h"
#include "os/x11/keycodes.h"
#endif
#endif

68
os/posix/thread.h Normal file
View file

@ -0,0 +1,68 @@
//
// os/posix/thread.h: Multi-threading functions.
//
// CEN64: Cycle-Accurate Nintendo 64 Simulator.
// Copyright (C) 2014, Tyler J. Stachecki.
//
// This file is subject to the terms and conditions defined in
// 'LICENSE', which is part of this source code package.
//
#ifndef CEN64_OS_POSIX_THREAD
#define CEN64_OS_POSIX_THREAD
#include "common.h"
#include <pthread.h>
#define CEN64_THREAD_RETURN_TYPE void*
#define CEN64_THREAD_RETURN_VAL NULL
// Type definitions.
typedef pthread_t cen64_thread;
typedef void* (*cen64_thread_func)(void *arg);
typedef pthread_mutex_t cen64_mutex;
//
// Threads.
//
// Creates a thread and starts executing 'f' within the thread.
static inline int cen64_thread_create(cen64_thread *t,
cen64_thread_func f, void *arg) {
return pthread_create(t, NULL, f, arg);
}
//
// Join a thread created with cen64_thread_create. Use this to
// effectively "free" the resources acquired for the thread.
//
static inline int cen64_thread_join(cen64_thread *t) {
return pthread_join(*t, NULL);
}
//
// Mutexes.
//
// Allocates resources for/initializes a mutex.
static inline int cen64_mutex_create(cen64_mutex *m) {
return pthread_mutex_init(m, NULL);
}
// Releases resources acquired by cen64_mutex_create.
static inline int cen64_mutex_destroy(cen64_mutex *m) {
return pthread_mutex_destroy(m);
}
// Locks the mutex passed as an argument.
static inline int cen64_mutex_lock(cen64_mutex *m) {
return pthread_mutex_lock(m);
}
// Unlocks the mutex passed as an argument.
static inline int cen64_mutex_unlock(cen64_mutex *m) {
return pthread_mutex_unlock(m);
}
#endif

36
os/posix/timer.c Normal file
View file

@ -0,0 +1,36 @@
//
// os/posix/timer.c: Functions for obtaining the system clock time.
//
// CEN64: Cycle-Accurate Nintendo 64 Simulator.
// Copyright (C) 2014, Tyler J. Stachecki.
//
// This file is subject to the terms and conditions defined in
// 'LICENSE', which is part of this source code package.
//
#include "common.h"
#include "os/posix/timer.h"
#include <time.h>
#include <sys/time.h>
// Computes the difference, in ns, between two times.
unsigned long long compute_time_difference(
const cen64_time *now, const cen64_time *before) {
#if defined(__APPLE__)
return (now->tv_sec - before->tv_sec) * NS_PER_SEC +
(now->tv_usec - before->tv_usec) * NS_PER_USEC;
#else
return (now->tv_sec - before->tv_sec) * NS_PER_SEC +
(now->tv_nsec - before->tv_nsec);
#endif
}
// Gets the time from the most monotonic source possible.
void get_time(cen64_time *t) {
#if defined(__APPLE__)
gettimeofday(t, NULL);
#else
clock_gettime(GETTIME_SOURCE, t);
#endif
}

38
os/posix/timer.h Normal file
View file

@ -0,0 +1,38 @@
//
// os/posix/timer.h: Functions for obtaining the system clock time.
//
// CEN64: Cycle-Accurate Nintendo 64 Simulator.
// Copyright (C) 2014, Tyler J. Stachecki.
//
// This file is subject to the terms and conditions defined in
// 'LICENSE', which is part of this source code package.
//
#ifndef CEN64_OS_POSIX_TIMER
#define CEN64_OS_POSIX_TIMER
#include "common.h"
#define NS_PER_SEC 1000000000ULL
#if defined(CLOCK_MONOTONIC_PRECISE)
#define GETTIME_SOURCE CLOCK_MONOTONIC_PRECISE
#else
#define GETTIME_SOURCE CLOCK_MONOTONIC_RAW
#endif
#ifdef __APPLE__
#include <time.h>
typedef struct timeval cen64_time;
#else
#include <time.h>
typedef struct timespec cen64_time;
#endif
cen64_cold unsigned long long compute_time_difference(
const cen64_time *now, const cen64_time *before);
cen64_cold void get_time(cen64_time *t);
#endif

View file

@ -14,7 +14,6 @@
#include "os/common/alloc.h"
#include "os/gl_window.h"
#include "os/main.h"
#include "os/unix/x11/glx_window.h"
#include <fcntl.h>
#include <signal.h>
#include <stddef.h>
@ -36,34 +35,10 @@ int main(int argc, const char *argv[]) {
return cen64_main(argc, argv);
}
// Grabs the input lock.
void os_acquire_input(struct gl_window *gl_window) {
struct glx_window *glx_window = (struct glx_window *) (gl_window->window);
pthread_mutex_lock(&glx_window->event_lock);
}
// Releases the input lock.
void os_release_input(struct gl_window *gl_window) {
struct glx_window *glx_window = (struct glx_window *) (gl_window->window);
pthread_mutex_unlock(&glx_window->event_lock);
}
// Informs the simulation thread if an exit was requested.
#if 0
bool os_exit_requested(struct gl_window *gl_window) {
struct glx_window *glx_window = (struct glx_window *) (gl_window->window);
return glx_window_exit_requested(glx_window);
}
#endif
// Allocates memory for a new device, runs it.
int os_main(struct cen64_device *device, struct cen64_options *options) {
struct gl_window_hints hints;
struct glx_window window;
pthread_t device_thread;
cen64_thread thread;
#if 0
// Spawn the user interface (or signal handler).
@ -96,7 +71,15 @@ int os_main(struct cen64_device *device, struct cen64_options *options) {
}
}
device_run(device);
if (cen64_thread_create(&thread, run_device_thread, device)) {
printf("Failed to create the main emulation thread.\n");
device_destroy(device);
return 1;
}
cen64_gl_window_thread(device);
cen64_thread_join(&thread);
// device_run(device);
#if 0
// Start the device thread, hand over control to the UI thread on success.
if ((pthread_create(&device_thread, NULL, run_device_thread, device)) == 0) {

View file

@ -1,63 +0,0 @@
//
// os/unix/x11/glx_window.h
//
// Convenience functions for managing rendering windows.
//
// This file is subject to the terms and conditions defined in
// 'LICENSE', which is part of this source code package.
//
#ifndef __unix_x11_glx_window_h__
#define __unix_x11_glx_window_h__
#include "common.h"
#include "os/gl_window.h"
#include <pthread.h>
#include <GL/glx.h>
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/xf86vmode.h>
struct glx_window {
Display *display;
XVisualInfo *visual_info;
XF86VidModeModeInfo old_mode;
Atom wm_delete_message;
XSetWindowAttributes attr;
Window window;
int screen;
GLXContext context;
// Constant state used by threads.
int select_pipefds[2];
uint8_t fixed_pad[CACHE_LINE_SIZE];
// Locks and whatnot for events.
pthread_mutex_t event_lock;
char went_fullscreen;
bool exit_requested;
uint8_t event_pad[CACHE_LINE_SIZE];
// Locks and whatnot for rendering.
pthread_mutex_t render_lock;
pthread_cond_t render_cv;
unsigned frame_xres, frame_yres, frame_xskip, frame_type;
uint8_t frame_data[MAX_FRAME_DATA_SIZE];
bool frame_pending;
uint8_t render_pad[CACHE_LINE_SIZE];
};
cen64_cold bool glx_window_exit_requested(struct glx_window *window);
cen64_cold void glx_window_render_frame(struct glx_window *window, const void *data,
unsigned xres, unsigned yres, unsigned xskip, unsigned type);
#endif

View file

@ -1,83 +0,0 @@
//
// os/unix/x11/keycodes.h
//
// Definitions for keycodes.
//
// This file is subject to the terms and conditions defined in
// 'LICENSE', which is part of this source code package.
//
#ifndef __unix_x11_keycodes_h__
#define __unix_x11_keycodes_h__
#include <X11/keysym.h>
// Letter keys.
#define CEN64_KEY_A XK_a
#define CEN64_KEY_B XK_b
#define CEN64_KEY_C XK_c
#define CEN64_KEY_D XK_d
#define CEN64_KEY_E XK_e
#define CEN64_KEY_F XK_f
#define CEN64_KEY_G XK_g
#define CEN64_KEY_H XK_h
#define CEN64_KEY_I XK_i
#define CEN64_KEY_J XK_j
#define CEN64_KEY_K XK_k
#define CEN64_KEY_L XK_l
#define CEN64_KEY_M XK_m
#define CEN64_KEY_N XK_n
#define CEN64_KEY_O XK_o
#define CEN64_KEY_P XK_p
#define CEN64_KEY_Q XK_q
#define CEN64_KEY_R XK_r
#define CEN64_KEY_S XK_s
#define CEN64_KEY_T XK_t
#define CEN64_KEY_U XK_u
#define CEN64_KEY_V XK_v
#define CEN64_KEY_W XK_w
#define CEN64_KEY_X XK_x
#define CEN64_KEY_Y XK_y
#define CEN64_KEY_Z XK_z
// Number keys.
#define CEN64_KEY_0 XK_0
#define CEN64_KEY_1 XK_1
#define CEN64_KEY_2 XK_2
#define CEN64_KEY_3 XK_3
#define CEN64_KEY_4 XK_4
#define CEN64_KEY_5 XK_5
#define CEN64_KEY_6 XK_6
#define CEN64_KEY_7 XK_7
#define CEN64_KEY_8 XK_8
#define CEN64_KEY_9 XK_9
// Directional keys.
#define CEN64_KEY_LEFT XK_Left
#define CEN64_KEY_UP XK_Up
#define CEN64_KEY_RIGHT XK_Right
#define CEN64_KEY_DOWN XK_Down
// Other keys.
#define CEN64_KEY_BSLASH XK_backslash
#define CEN64_KEY_COMMA XK_comma
#define CEN64_KEY_EQUALS XK_equal
#define CEN64_KEY_FSLASH XK_fslash
#define CEN64_KEY_MINUS XK_minus
#define CEN64_KEY_LALT XK_Alt_L
#define CEN64_KEY_LBRACKET XK_bracketleft
#define CEN64_KEY_LCTRL XK_Control_L
#define CEN64_KEY_LSHIFT XK_Shift_L
#define CEN64_KEY_LSUPER XK_Super_L
#define CEN64_KEY_PERIOD XK_period
#define CEN64_KEY_RALT XK_Alt_R
#define CEN64_KEY_RBRACKET XK_bracketright
#define CEN64_KEY_RCTRL XK_Control_R
#define CEN64_KEY_RETURN XK_Return
#define CEN64_KEY_RSHIFT XK_Shift_R
#define CEN64_KEY_RSUPER XK_Super_R
#define CEN64_KEY_SEMICOLON XK_semicolon
#define CEN64_KEY_SLASH XK_slash
#define CEN64_KEY_SPACE XK_space
#endif

View file

@ -9,34 +9,91 @@
//
#include "common.h"
#include "device/device.h"
#include "gl_common.h"
#include "gl_config.h"
#include "gl_display.h"
#include "gl_screen.h"
#include "gl_window.h"
#include "timer.h"
#include "vi/controller.h"
#include "vi/render.h"
#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>
#include <X11/Xlib.h>
static int cen64_gl_window_create_objects(cen64_gl_window window);
// Creates an (initially hidden) cen64_gl_window.
cen64_gl_window cen64_gl_window_create(
cen64_gl_display display, cen64_gl_screen screen,
const cen64_gl_config *config, const char *title) {
cen64_gl_window window;
if ((window = malloc(sizeof(*window))) == NULL)
return CEN64_GL_WINDOW_BAD;
// Get the visual info for the framebuffer configuration.
if ((window->visual_info = glXGetVisualFromFBConfig(
display, *config)) == NULL) {
close(window->pipefds[0]);
close(window->pipefds[1]);
cen64_mutex_destroy(&window->event_mutex);
free(window);
return CEN64_GL_WINDOW_BAD;
}
// Create synchronization primitives for the window.
if (cen64_gl_window_create_objects(window)) {
XFree(window->visual_info);
free(window);
return CEN64_GL_WINDOW_BAD;
}
// Create a colormap using the visual info.
window->attr.colormap = XCreateColormap(display, XRootWindow(
display, screen), window->visual_info->visual, AllocNone);
// Select the events we'd like to receive, create the window.
window->attr.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask |
ButtonPressMask | StructureNotifyMask;
window->window = XCreateWindow(display, XRootWindow(display, screen),
0, 0, 640, 480, 0, window->visual_info->depth, InputOutput,
window->visual_info->visual, CWBorderPixel | CWColormap | CWEventMask,
&window->attr);
// Now that we created the window, setup any atoms/properties we need.
XSetStandardProperties(display, window->window, title,
NULL, None, NULL, 0, NULL);
window->wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False);
XSetWMProtocols(display, window->window, &window->wm_delete_window, 1);
window->exit_requested = false;
window->display = display;
window->screen = screen;
return window;
}
// Handles events that come from X11.
bool cen64_gl_window_pump_events(struct vi_controller *vi) {
bool released, exit_requested;
bool released, exit_requested = false;
XEvent event;
if (!XPending(vi->display))
return false;
exit_requested = false;
//pthread_mutex_lock(&glx_window->event_lock);
cen64_mutex_lock(&vi->window->event_mutex);
do {
XNextEvent(vi->display, &event);
switch (event.type) {
case ClientMessage:
//if ((unsigned) event.xclient.data.l[0] == glx_window->wm_delete_message)
// glx_window->exit_requested = exit_requested = true;
vi->window->exit_requested = exit_requested = true;
break;
case ConfigureNotify:
@ -70,7 +127,101 @@ bool cen64_gl_window_pump_events(struct vi_controller *vi) {
}
} while (XPending(vi->display));
//pthread_mutex_unlock(&glx_window->event_lock);
cen64_mutex_unlock(&vi->window->event_mutex);
return exit_requested;
}
// Allocate mutexes, pipes, etc. for the UI/window.
int cen64_gl_window_create_objects(cen64_gl_window window) {
if (cen64_mutex_create(&window->event_mutex)) {
return 1;
}
if (cen64_mutex_create(&window->render_mutex)) {
cen64_mutex_destroy(&window->event_mutex);
return 1;
}
if (pipe(window->pipefds) < 0) {
cen64_mutex_destroy(&window->render_mutex);
cen64_mutex_destroy(&window->event_mutex);
return 1;
}
return 0;
}
// Thread that controls the user interface, etc.
int cen64_gl_window_thread(struct cen64_device *device) {
struct vi_controller *vi = &device->vi;
cen64_time last_update_time;
cen64_gl_window window;
int frame_count;
int max_fds, x11_fd;
fd_set fdset;
// We listen for UI updates using a POSIX pipe, and X11
// events/messages using the connection fd. select() is
// then used to efficiently multiplex between the two,
// so setup a fd_set, pipes, etc. required for all this.
x11_fd = ConnectionNumber(vi->display);
max_fds = x11_fd > vi->window->pipefds[0] ? x11_fd: vi->window->pipefds[0];
max_fds++;
FD_ZERO(&fdset);
FD_SET(vi->window->pipefds[0], &fdset);
FD_SET(x11_fd, &fdset);
// Okay, main UI thread loop starts here.
for (frame_count = 0, get_time(&last_update_time) ; ;) {
fd_set ready_to_read = fdset;
// Multiplex between all of our event sources...
if (select(max_fds, &ready_to_read, NULL, NULL, NULL) > 0) {
// Did we get a X11 event?
if (FD_ISSET(x11_fd, &ready_to_read)) {
if (unlikely(cen64_gl_window_pump_events(vi)))
break;
}
// Did we get a UI event?
if (FD_ISSET(vi->window->pipefds[0], &ready_to_read)) {
read(vi->window->pipefds[0], &window, sizeof(window));
cen64_mutex_lock(&window->render_mutex);
gl_window_render_frame(vi, window->frame_buffer,
window->frame_hres, window->frame_vres,
window->frame_hskip, window->frame_type);
cen64_mutex_unlock(&window->render_mutex);
// Update the window title every 60 VIs
// to display the current VI/s rate.
if (++frame_count == 60) {
char title[128];
cen64_time current_time;
float ns;
// Compute time spent rendering last 60 frames, reset timer/counter.
get_time(&current_time);
ns = compute_time_difference(&current_time, &last_update_time);
last_update_time = current_time;
frame_count = 0;
sprintf(title,
"CEN64 ["CEN64_COMPILER" - "CEN64_ARCH_DIR"/"CEN64_ARCH_SUPPORT"]"
" - %.1f VI/s", (60 / (ns / NS_PER_SEC)));
cen64_gl_window_set_title(window, title);
}
}
}
}
return 0;
}

View file

@ -10,14 +10,16 @@
#ifndef CEN64_OS_X11_GL_WINDOW
#define CEN64_OS_X11_GL_WINDOW
#include "common.h"
#include "gl_common.h"
#include "gl_config.h"
#include "gl_display.h"
#include "gl_screen.h"
#include <stddef.h>
#include <stdlib.h>
#include "thread.h"
#include <unistd.h>
#include <X11/Xlib.h>
#define FRAMEBUF_SZ (640 * 480 * 4)
#define CEN64_GL_WINDOW_BAD (NULL)
struct cen64_gl_window {
cen64_gl_display display;
@ -27,53 +29,27 @@ struct cen64_gl_window {
Atom wm_delete_window;
XSetWindowAttributes attr;
XVisualInfo *visual_info;
int pipefds[2];
cen64_mutex render_mutex;
uint8_t frame_buffer[FRAMEBUF_SZ];
unsigned frame_hres, frame_vres;
unsigned frame_hskip, frame_type;
cen64_mutex event_mutex;
bool exit_requested;
};
typedef struct cen64_gl_window *cen64_gl_window;
struct vi_controller;
// Forward declaration.
struct cen64_device;
// Creates a (hidden) cen64_gl_window.
static inline cen64_gl_window cen64_gl_window_create(
// Creates an (initially hidden) cen64_gl_window.
cen64_cold cen64_gl_window cen64_gl_window_create(
cen64_gl_display display, cen64_gl_screen screen,
const cen64_gl_config *config, const char *title) {
cen64_gl_window window;
if ((window = malloc(sizeof(*window))) == NULL)
return CEN64_GL_WINDOW_BAD;
// Get the visual info for the framebuffer configuration.
if ((window->visual_info = glXGetVisualFromFBConfig(
display, *config)) == NULL) {
free(window);
return CEN64_GL_WINDOW_BAD;
}
// Create a colormap using the visual info.
window->attr.colormap = XCreateColormap(display, XRootWindow(
display, screen), window->visual_info->visual, AllocNone);
// Select the events we'd like to receive, create the window.
window->attr.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask |
ButtonPressMask | StructureNotifyMask;
window->window = XCreateWindow(display, XRootWindow(display, screen),
0, 0, 640, 480, 0, window->visual_info->depth, InputOutput,
window->visual_info->visual, CWBorderPixel | CWColormap | CWEventMask,
&window->attr);
// Now that we created the window, setup any atoms/properties we need.
XSetStandardProperties(display, window->window, title,
NULL, None, NULL, 0, NULL);
window->wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False);
XSetWMProtocols(display, window->window, &window->wm_delete_window, 1);
window->display = display;
window->screen = screen;
return window;
}
const cen64_gl_config *config, const char *title);
// Releases resources allocated by cen64_gl_window_create.
static inline void cen64_gl_window_destroy(cen64_gl_window window) {
@ -81,17 +57,34 @@ static inline void cen64_gl_window_destroy(cen64_gl_window window) {
XFreeColormap(window->display, window->attr.colormap);
XFree(window->visual_info);
close(window->pipefds[0]);
close(window->pipefds[1]);
cen64_mutex_destroy(&window->render_mutex);
cen64_mutex_destroy(&window->event_mutex);
free(window);
}
// Handles events that come from X11.
bool cen64_gl_window_pump_events(struct vi_controller *vi);
// Pushes a notification to the UI queue to indicate a frame is ready.
static inline void cen64_gl_window_push_frame(cen64_gl_window window) {
write(window->pipefds[1], &window, sizeof(window));
}
// Swaps the front and back buffers of the cen65_gl_window.
// Sets the title of the cen64_gl_window.
static inline void cen64_gl_window_set_title(
cen64_gl_window window, const char *title) {
XStoreName(window->display, window->window, title);
XFlush(window->display);
}
// Swaps the front and back buffers of the cen64_gl_window.
static inline void cen64_gl_window_swap_buffers(cen64_gl_window window) {
glXSwapBuffers(window->display, window->window);
}
// Thread that controls the user interface, etc.
int cen64_gl_window_thread(struct cen64_device *device);
// Unhides the cen64_gl_window.
static inline void cen64_gl_window_unhide(cen64_gl_window window) {
XMapRaised(window->display, window->window);

View file

@ -11,10 +11,11 @@
#include "common.h"
#include "bus/address.h"
#include "bus/controller.h"
#include "os/main.h"
#include "gl_window.h"
#include "ri/controller.h"
#include "si/cic.h"
#include "si/controller.h"
#include "thread.h"
#include "vi/controller.h"
#include "vr4300/interface.h"
#include <assert.h>
@ -73,6 +74,7 @@ int pif_perform_command(struct si_controller *si,
unsigned channel, uint8_t *send_buf, uint8_t send_bytes,
uint8_t *recv_buf, uint8_t recv_bytes) {
uint8_t command = send_buf[0];
struct bus_controller *bus;
switch(command) {
// Read status/reset.
@ -107,9 +109,11 @@ int pif_perform_command(struct si_controller *si,
case 0x01:
switch(channel) {
case 0:
//os_acquire_input(&si->bus->vi->gl_window);
memcpy(&bus, si, sizeof(bus));
cen64_mutex_lock(&bus->vi->window->event_mutex);
memcpy(recv_buf, si->input, sizeof(si->input));
//os_release_input(&si->bus->vi->gl_window);
cen64_mutex_unlock(&bus->vi->window->event_mutex);
break;
default:

View file

@ -15,6 +15,7 @@
#include "os/main.h"
#include "ri/controller.h"
#include "vi/controller.h"
#include "vi/render.h"
#include "vi/window.h"
#include "vr4300/interface.h"
@ -53,19 +54,16 @@ int read_vi_regs(void *opaque, uint32_t address, uint32_t *word) {
// Advances the controller by one clock cycle.
void vi_cycle(struct vi_controller *vi) {
struct render_area *ra = &vi->render_area;
int hskip, vres, hres;
float hcoeff, vcoeff;
unsigned type;
cen64_gl_window window;
const uint8_t *buffer;
uint32_t offset;
struct render_area *ra = &vi->render_area;
struct bus_controller *bus;
float hcoeff, vcoeff;
if (likely(vi->counter-- != 0))
return;
offset = vi->regs[VI_ORIGIN_REG] & 0xFFFFFF;
buffer = vi->bus->ri->ram + offset;
window = vi->window;
// Calculate the bounding positions.
ra->x.start = vi->regs[VI_H_START_REG] >> 16 & 0x3FF;
@ -76,30 +74,37 @@ void vi_cycle(struct vi_controller *vi) {
hcoeff = (float) (vi->regs[VI_X_SCALE_REG] & 0xFFF) / (1 << 10);
vcoeff = (float) (vi->regs[VI_Y_SCALE_REG] & 0xFFF) / (1 << 10);
// Calculate the height and width of the frame.
vres = ra->height =((ra->y.end - ra->y.start) >> 1) * vcoeff;
hres = ra->width = ((ra->x.end - ra->x.start)) * hcoeff;
hskip = ra->hskip = vi->regs[VI_WIDTH_REG] - ra->width;
type = vi->regs[VI_STATUS_REG] & 0x3;
if (hres <= 0 || vres <= 0)
type = 0;
// Interact with the user interface?
cen64_gl_window_pump_events(vi);
gl_window_render_frame(vi, buffer, hres, vres, hskip, type);
#if 0
if (likely(vi->gl_window.window)) {
if (os_exit_requested(&vi->gl_window))
if (likely(window)) {
cen64_mutex_lock(&window->event_mutex);
if (unlikely(window->exit_requested)) {
cen64_mutex_unlock(&window->event_mutex);
device_exit(vi->bus);
}
os_render_frame(&vi->gl_window, buffer, hres, vres, hskip, type);
cen64_mutex_unlock(&window->event_mutex);
cen64_mutex_lock(&window->render_mutex);
// Calculate the height and width of the frame.
window->frame_vres = ra->height =((ra->y.end - ra->y.start) >> 1) * vcoeff;
window->frame_hres = ra->width = ((ra->x.end - ra->x.start)) * hcoeff;
window->frame_hskip = ra->hskip = vi->regs[VI_WIDTH_REG] - ra->width;
window->frame_type = vi->regs[VI_STATUS_REG] & 0x3;
if (window->frame_hres <= 0 || window->frame_vres <= 0)
window->frame_type = 0;
// Copy the frame data into a temporary buffer.
memcpy(&bus, vi, sizeof(bus));
memcpy(vi->window->frame_buffer,
bus->ri->ram + (vi->regs[VI_ORIGIN_REG] & 0xFFFFFF),
sizeof(vi->window->frame_buffer));
cen64_mutex_unlock(&vi->window->render_mutex);
cen64_gl_window_push_frame(window);
}
else if (device_exit_requested)
device_exit(vi->bus);
#endif
// Raise an interrupt to indicate refresh.
signal_rcp_interrupt(vi->bus->vr4300, MI_INTR_VI);
vi->counter = VI_COUNTER_START;