diff Renderer/Engine/ps3fb/cp_fb.cc @ 0:04e28d8d3c6f

first commit
author Daiki KINJYO <e085722@ie.u-ryukyu.ac.jp>
date Mon, 08 Nov 2010 01:23:25 +0900
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Renderer/Engine/ps3fb/cp_fb.cc	Mon Nov 08 01:23:25 2010 +0900
@@ -0,0 +1,205 @@
+// cp_fb.c 
+//
+// Copyright (c) 2006, Mike Acton <macton@cellperformance.com>
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 
+// documentation files (the "Software"), to deal in the Software without restriction, including without
+// limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial
+// portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
+// EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+// OR OTHER DEALINGS IN THE SOFTWARE.
+
+// NOTES:
+// From Geert Uytterhoeven 2007-01-26 04:50:44, 
+//      http://patchwork.ozlabs.org/linuxppc/patch?id=9143
+//
+//     "As the actual graphics hardware cannot be accessed directly by Linux,
+//     ps3fb uses a virtual frame buffer in main memory. The actual screen image is
+//     copied to graphics memory by the GPU on every vertical blank, by making a
+//     hypervisor call."
+//
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <string.h>
+#include <errno.h>
+#include <stdint.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <linux/fb.h>
+#include <sys/time.h>
+#include <asm/ps3fb.h>
+#include <linux/vt.h>
+#include <linux/kd.h>
+#include "cp_fb.h"
+
+static inline const char*
+select_error_str( int existing_error, const char* const existing_error_str, int new_error, const char* const new_error_str )
+{
+  // Only report the first error found - any error that follows is probably just a cascading effect.
+  const char* error_str = (char*)( (~(intptr_t)existing_error & (intptr_t)new_error & (intptr_t)new_error_str)
+                                 |  ((intptr_t)existing_error & (intptr_t)existing_error_str) );
+
+  return (error_str);
+}
+
+int
+cp_fb_open( cp_fb* const restrict fb )
+{
+    const char*    error_str      = NULL;
+    int            error          = 0;
+
+    // Open framebuffer device
+
+    const int   fb_fd             = open( "/dev/fb0", O_RDWR );
+    const int   open_fb_error     = (fb_fd >> ((sizeof(int)<<3)-1));
+    const char* open_fb_error_str = "Could not open /dev/fb0. Check permissions.";
+  
+    error_str = select_error_str( error, error_str, open_fb_error, open_fb_error_str );
+    error     = error | open_fb_error;
+
+    // Check for vsync
+
+    struct fb_vblank vblank;
+
+    const int   get_vblank_error     = ioctl(fb_fd, FBIOGET_VBLANK, (unsigned long)&vblank);
+    const char* get_vblank_error_str = "Could not get vblank info (FBIOGET_VBLANK)";
+
+    error_str = select_error_str( error, error_str, get_vblank_error, get_vblank_error_str );
+    error     = error | get_vblank_error;
+
+    const int   has_vsync            = vblank.flags & FB_VBLANK_HAVE_VSYNC;
+    const int   has_vsync_error      = (~(-has_vsync|has_vsync))>>((sizeof(int)<<3)-1);
+    const char* has_vsync_error_str  = "No vsync available (FB_VBLANK_HAVE_VSYNC)";
+
+    error_str = select_error_str( error, error_str, has_vsync_error, has_vsync_error_str );
+    error     = error | has_vsync_error;
+
+    // Get screen resolution and frame count
+
+    struct ps3fb_ioctl_res res;
+
+    const int   screeninfo_error     = ioctl(fb_fd, PS3FB_IOCTL_SCREENINFO, (unsigned long)&res);
+    const char* screeninfo_error_str = "Could not get screen info (PS3_IOCTL_SCREENINFO)";
+
+    error_str = select_error_str( error, error_str, screeninfo_error, screeninfo_error_str );
+    error     = error | screeninfo_error;
+
+    const int   has_at_least_double_buffer           = (res.num_frames - 2) >> ((sizeof(res.num_frames)<<3)-1);
+    const int   has_at_least_double_buffer_error     = ~has_at_least_double_buffer;
+    const char* has_at_least_double_buffer_error_str = "Could not get screen info (PS3_IOCTL_SCREENINFO)";
+
+    error_str = select_error_str( error, error_str, has_at_least_double_buffer_error, has_at_least_double_buffer_error_str );
+    error     = error | has_at_least_double_buffer_error;
+
+    const uint32_t bpp                      = 4; // This is fixed for PS3 fb, and there's not a test for it.
+    const uint32_t frame_size               = res.xres * res.yres * bpp;
+    const uint32_t double_buffer_frame_size = frame_size * 2;
+
+    // const uint32_t frame_top_margin_size    = res.xres * res.yoff * bpp;
+    // const uint32_t frame_bottom_margin_size = frame_top_margin_size;
+    // const uint32_t frame_size               = frame_full_size; /* - ( frame_top_margin_size + frame_bottom_margin_size ); */
+    // const uint32_t double_buffer_frame_size = frame_size * 2;
+
+    const uintptr_t fb_addr           = (uintptr_t)mmap(NULL, double_buffer_frame_size, PROT_READ|PROT_WRITE, MAP_SHARED, fb_fd, 0);
+    const int       fb_mmap_error     = fb_addr >> ((sizeof(uintptr_t)<<3)-1);
+    const char*     fb_mmap_error_str = "Could not get mmap frame buffer";
+
+    error_str = select_error_str( error, error_str, fb_mmap_error, fb_mmap_error_str );
+    error     = error | fb_mmap_error;
+
+    // Take control of frame buffer from kernel
+    ioctl(fb_fd, PS3FB_IOCTL_ON, 0);
+
+    // yoff is the number of lines that cannot be copied to the CRT before the vblank. For the most part this represents
+    // unusable frame buffer space. While it is possible to draw to the area if you draw in the opposite frame buffer's
+    // offset space, which will (due to poor draw timing by ps3fb) be the thing that is actually drawn, it's very 
+    // difficult to work with in practice. So:
+    //
+    //     (1)  The y offset area will be treated as "off limits".
+    //     (2)  An equivalent border will be created at the bottom, so the frame looks balanced even though it is
+    //          not entirely full screen. 
+
+    // xoff is the number of lines that cannot be copied to the CRT before the hblank.
+    // Similar to the y offset space, the x offset space is displayed on the wrong (previous) line. So:
+    //
+    //     (1)  The x offset area will be treated as "off limits".
+    //     (2)  An equivalent border will be created at the right, so the frame looks balanced even though it is
+    //          not entirely full screen. 
+
+    uintptr_t draw_start_addr = fb_addr;
+    uintptr_t draw_next_addr  = draw_start_addr + ( res.yres * res.xres * bpp );
+    uintptr_t drawable_h      = res.yres - ( 2 * res.yoff );
+    uintptr_t drawable_w      = res.xres - ( 2 * res.xoff );
+
+    // xoff is the number of lines that cannot be copied to the CRT before the hblank. This area is much easier to use. 
+    // Similar to the y offset space, the x offset space is displayed on the wrong (previous) line. So:
+    // In principle, it is possible to steal back the x offset space by shifting back the line address to the 
+    // start of the border of the previous line. Like so:
+    //
+    //     (1)  One additional line will be taken from the height so the a complete horizontal line can be started
+    //          early.
+    //     (2)  The frame buffer address returned in cp_fb will be offset by (xres-xoff) in order for the remaining
+    //          space to represent a rectangular area of drawable memory.
+    //
+    //     i.e. 
+    //     uintptr_t draw_start_addr = fb_addr + ( ( res.xres - res.xoff ) * bpp );
+    //     uintptr_t draw_next_addr  = draw_start_addr + ( res.yres * res.xres * bpp );
+    //     uintptr_t drawable_h      = res.yres - 1 - ( 2 * res.yoff );
+    //     uintptr_t drawable_w      = res.xres;
+    //
+    //     But I wouldn't recommend it, since on some CRTs the effect of this would be that the frame does not appear
+    //     square.
+
+    fb->stride        = res.xres;
+    fb->w             = drawable_w;
+    fb->h             = drawable_h;
+    fb->fd            = fb_fd;
+    fb->start_addr    = fb_addr;
+    fb->size          = double_buffer_frame_size;
+    fb->draw_addr[0]  = draw_start_addr;
+    fb->draw_addr[1]  = draw_next_addr;
+
+    // Clear out the whole buffer. Any unused space is black. It's also convinient to start with a cleared frame
+    // buffer for the user.
+
+    memset((void*)fb_addr, 0x00, double_buffer_frame_size );
+
+    return (error);
+}
+
+void
+cp_fb_close( const cp_fb* const restrict fb )
+{
+    // Give frame buffer control back to the kernel
+    ioctl(fb->fd, PS3FB_IOCTL_OFF, 0);
+
+    munmap( (void*)fb->start_addr, fb->size );
+
+    close(fb->fd);
+}
+
+void
+cp_fb_wait_vsync( cp_fb* const restrict fb )
+{
+    unsigned long crt = 0;
+
+    ioctl(fb->fd, FBIO_WAITFORVSYNC, &crt );
+}
+
+void
+cp_fb_flip( cp_fb* const restrict fb, unsigned long field_ndx )
+{
+    ioctl(fb->fd, PS3FB_IOCTL_FSEL,  &field_ndx );
+}