It is currently Wed May 22, 2013 9:07 pm



Reply to topic  [ 8 posts ] 
 Hardware page flipping on NVIDIA video cards under Linux 
Author Message
Golden Eyed Wiseman! (or woman!)
User avatar

Joined: Sat Jan 09, 2010 2:06 pm
Posts: 1897
Location: Perpignan, France
Following this I've written a new hardware page flipping test application for Linux that should work on most NVIDIA video cards (generation NV03 to NVC0) and with both the proprietary and the free driver (Nouveau).

You can compile it by typing this command :
Code:
gcc -O2 -o pflip-nvidia pflip-nvidia.c

And launch it as root by passing it half the value of your current horizontal resolution (ie. 512 for 1024x768) :
Code:
./pflip-nvidia 512

Then you can type 1 + <enter> to reset the page flipping and 2 + <enter> to set it. If all works well you should see the left half of the screen on the right and vice-versa. You can quit with q + <enter>.

Here is the code (pflip-nvidia.c) :

Code:
/*
NAME
   pflip-nvidia - test hardware page flipping for NVIDIA video cards

SYNOPSIS
   pflip-nvidia <offset>

DESCRIPTION
   pflip-nvidia is used to test hardware page flipping on graphic cards
   supporting standard VGA registers while running an X Server.

   To obtain meaningful results, a virtual screen must be active with an
   horizontal resolution set to the double of the viewport horizontal
   resolution. For example, a 1024x768 viewport with a 2048x768 virtual
   screen.

   The application is controlled using these interactive commands:

   q   quit the application
   1   set the CRTC Start Address Register to 0
   2   set the CRTC Start Address Register to <offset>

OPTIONS
   <offset>
      Set the offset value as a number of pixel columns that should
      correspond to the width of the current viewport. For example
      1920 for a 3840x1080 virtual screen.

AUTHOR
   Frédéric Lopez <http://askyl.blog.free.fr/>

   21 May 2012
*/

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <inttypes.h>
#include <sys/mman.h>

// Define the "address" and "data" registers for VGA CRTC access
#define VGA_CRTC_ADDR_REG 0x3D4
#define VGA_CRTC_DATA_REG 0x3D5

#define PCI_GET_BUS(devbusfn) ((devbusfn >> 8) & 0xff)
#define PCI_GET_DEVICE(devbusfn) ((devbusfn & 0xff) >> 3)
#define PCI_GET_FUNCTION(devbusfn) (devbusfn & 0x7)

// Set the CRTC Start Adress Register
static void set_vga_start(volatile uint8_t *mem, int offset)
{
   // Set the VGA CRTC Start Address Low Register
   mem[VGA_CRTC_ADDR_REG] = 0x0D;
   mem[VGA_CRTC_DATA_REG] = offset & 0xFF;
   // Set the VGA CRTC Start Address High Register
   mem[VGA_CRTC_ADDR_REG] = 0x0C;
   mem[VGA_CRTC_DATA_REG] = offset >> 8;   
}

int32_t pciReadLong(unsigned short devbusfn, long offset)
{
    char file[25];
    FILE *device;
    short bus = PCI_GET_BUS(devbusfn);
    short dev = PCI_GET_DEVICE(devbusfn);
    short function = PCI_GET_FUNCTION(devbusfn);

    snprintf(file, sizeof(file), "/proc/bus/pci/%02x/%02x.%x", bus, dev, function);
    if ((device = fopen(file, "r")) != NULL)
    {
      int32_t buffer;
      fseek(device, offset, SEEK_SET);                    
      fread(&buffer, sizeof(int32_t), 1, device);
      fclose(device);

      return buffer;
    }
   
    return -1;
}

/* Check if the device is a videocard */
int is_video_card(unsigned short devbusfn)
{
    int32_t pci_class = pciReadLong(devbusfn, 0x9);

   /* The device is a VGA card */
    if (((htonl(pci_class) >> 8) & 0xf) == 0x03)
        return 1;
    else
        return 0;
}

/* Get the register offset */
int get_reg_addr()
{
   int dev, dum, reg_addr, i = 0;
    unsigned short devbusfn;
    char buf[256];
    FILE *proc;
   
    proc = fopen("/proc/bus/pci/devices", "r");
    if (!proc)
    {
      perror("Can't open /proc/bus/pci/devices !");
        return -1;
    }

    while (fgets(buf, sizeof(buf) - 1, proc))
    {
        if (sscanf(buf,"%hx %x %x %x",&devbusfn, &dev, &dum, &reg_addr) != 4)
           continue;

        /* The card contains an Nvidia chipset */   
        if ((dev >> 16) == 0x10de)
      {
         if (!is_video_card(devbusfn))
            continue;
         i++;
      }
   }
   
   fclose(proc);
   
    if (i == 0)
       return -1;
       else return reg_addr;
}

int main(int argc, char *argv[])
{
   int key, offset;
   char *p;
   
    int fd;
    void *map;
    uint64_t res;
    char buf[32];
    volatile uint8_t *m;
    int resfd;

   // Test the number of command-line arguments
   if (argc != 2)
   {
      printf("Usage: pflip-nvidia <offset>\n");
      exit(EXIT_FAILURE);
   }

   // Test if the argument is an integer
   errno = 0;
   offset = strtol(argv[1], &p, 10);
   if (errno != 0 || *p != 0 || p == argv[1])
   {
      printf("Error: offset value must be an integer!\n");
      exit(EXIT_FAILURE);
   }

   // Show interactive commands help
   printf("Quit with 'q'.\n");
   printf("Page 1 with '1'.\n");
   printf("Page 2 with '2'.\n");

   // Find video card register offset   
   res = get_reg_addr();
   if (res == -1)
   {
      printf("Error: no NVIDIA video card found!\n");
      exit(EXIT_FAILURE);
   }

   // Open /dev/mem to map NVIDIA registers
   if ((fd = open("/dev/mem", O_RDWR)) < 0) {
      perror("Fail open :(");
      return -1;
   }

   // Map NVIDIA VGA CRTC registers 
   m = map = mmap(0, 0x2000, PROT_READ|PROT_WRITE, MAP_SHARED, fd, res + 0x601000);
   if (map == MAP_FAILED) {
      perror("FAIL mmap :(");
      return -1;
   }
   
   // Main loop
   while ((key = getchar()) != 'q')
   {
      switch (key)
      {
         case '1' :
            set_vga_start(m, 0);
            printf("Page 1.\n");
            break;
         case '2' :
            set_vga_start(m, offset);
            printf("Page 2.\n");
            break;
      }
   }
   
   printf("Done.\n");
   return EXIT_SUCCESS;
}

On my machine (GeForce 6200, Athlon XP 2400+ 32 bits processor) it works well with the proprietary NVIDIA driver. It does also somewhat work with the Nouveau driver but the first 10 lines of the screen are garbled after a page flipping, I must switch and go back from the console (Ctrl-F1 then Ctrl-F7) to get back a normal display. I hope it's only related to an outdated version of Nouveau (the one from Debian/unstable).

If it does work on a good range of NVIDIA GPUs I may be tempted to continue my work on my Genlock modification and my glstereo driver to implement a functional S3D driver for 120Hz displays under Linux.

Feel free to test it and to report back if it does work for you, giving your CPU, GPU and driver (NVIDIA or Nouveau) references.

Thank you.


Sun May 20, 2012 6:07 pm
Profile WWW
3D Angel Eyes (Moderator)
User avatar

Joined: Sat Apr 12, 2008 8:18 pm
Posts: 10034
Great work man! This sounds really good. Unfortunately I don't have a Linux machine setup currently, but I would love to test this myself. Been really wanting to test out the new version of Ubuntu, so I may try this at some point in the future.

_________________
Image


Sun May 20, 2012 6:55 pm
Profile
Cross Eyed!

Joined: Sun Feb 15, 2009 12:50 pm
Posts: 131
Tried it on ubuntu 10.04, nvidia proprietary 195.36.24, geforce 7900gtx

Doesn't appear to work - it says 'Page 1' and 'Page 2' but no viewport change occurs. Tried both outputs on the card, single monitor mode, dual monitor mode...


Sun Jun 10, 2012 11:13 am
Profile
Golden Eyed Wiseman! (or woman!)
User avatar

Joined: Sat Jan 09, 2010 2:06 pm
Posts: 1897
Location: Perpignan, France
Did the previous version used in the modified Genlock work with your GPU ? You can also try these other versions if it was not the case :
http://askyl.blog.free.fr/index.php?pos ... ux-%282%29
http://askyl.blog.free.fr/index.php?pos ... -NVIDIA%29
http://askyl.blog.free.fr/index.php?pos ... ux-%283%29
http://askyl.blog.free.fr/index.php?pos ... ux-%284%29

It should work up to NV40 generation (including the 7900GTX). If it's not the case it may be because you have more than one display connected to your graphics card. It won't work on NV50+ hardware anyway, several people tested these versions with their NV50+ GPUs without success.

Nice to see you again mickeyjaw btw. :)


Mon Jun 11, 2012 3:31 pm
Profile WWW
Cross Eyed!

Joined: Sun Feb 15, 2009 12:50 pm
Posts: 131
Ok, so pflip-vga.c works, but none of the others do...


Tue Jun 12, 2012 2:56 pm
Profile
Golden Eyed Wiseman! (or woman!)
User avatar

Joined: Sat Jan 09, 2010 2:06 pm
Posts: 1897
Location: Perpignan, France
Do you use Nouveau or the NVIDIA binary driver ?


Tue Jun 12, 2012 3:57 pm
Profile WWW
Cross Eyed!

Joined: Sun Feb 15, 2009 12:50 pm
Posts: 131
Binary driver version 195.36.24


Wed Jun 13, 2012 2:32 pm
Profile
Golden Eyed Wiseman! (or woman!)
User avatar

Joined: Sat Jan 09, 2010 2:06 pm
Posts: 1897
Location: Perpignan, France
Weird, all the versions of these pageflip tests did work with my GPU (6600GT). Anyway as I said it didn't work on any card >= NV50 so I've pretty much lot interest in this technique.

I've bought a new machine with an integrated Intel GPU, so I'll probably try to update glstereo for this hardware instead.


Wed Jun 13, 2012 3:03 pm
Profile WWW
Display posts from previous:  Sort by  
Reply to topic   [ 8 posts ] 

Who is online

Users browsing this forum: No registered users and 0 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Jump to:  
Powered by phpBB® Forum Software © phpBB Group
Designed by STSoftware.