VBoxSDLTest.cpp revision 1e4b4f4c0ada2641b0a319e67fc2f4339eff5a9d
/** @file
*
* VBox frontends: VBoxSDL (simple frontend based on SDL):
* VBoxSDL testcases
*/
/*
* Copyright (C) 2006 InnoTek Systemberatung GmbH
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* you can redistribute it and/or modify it under the terms of the GNU
* General Public License as published by the Free Software Foundation,
* in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
* distribution. VirtualBox OSE is distributed in the hope that it will
* be useful, but WITHOUT ANY WARRANTY of any kind.
*
* If you received this file as part of a commercial VirtualBox
* distribution, then only the terms of your commercial VirtualBox
* license agreement apply instead of the previous paragraph.
*/
#if defined(__WIN__) ///@todo someone please explain why we don't follow the book!
# define _SDL_main_h
#endif
#include <SDL.h>
#include <iprt/assert.h>
#include <iprt/stream.h>
#include <iprt/string.h>
#include <iprt/time.h>
#include <stdlib.h>
#include <signal.h>
#ifdef VBOX_OPENGL
#include "SDL_opengl.h"
#endif
#ifdef __WIN__
#define ESC_NORM
#define ESC_BOLD
#else
#define ESC_NORM "\033[m"
#define ESC_BOLD "\033[1m"
#endif
static SDL_Surface *gSurfVRAM; /* SDL virtual framebuffer surface */
static void *gPtrVRAM; /* allocated virtual framebuffer */
static SDL_Surface *gScreen; /* SDL screen surface */
static unsigned long guGuestXRes; /* virtual framebuffer width */
static unsigned long guGuestYRes; /* virtual framebuffer height */
static unsigned long guGuestBpp; /* virtual framebuffer bits per pixel */
static unsigned long guMaxScreenWidth; /* max screen width SDL allows */
static unsigned long guMaxScreenHeight; /* max screen height SDL allows */
static int gfResizable = 1; /* SDL window is resizable */
static int gfFullscreen = 0; /* use fullscreen mode */
#ifdef VBOX_OPENGL
static unsigned long guTextureWidth; /* width of OpenGL texture */
static unsigned long guTextureHeight; /* height of OpenGL texture */
static unsigned int gTexture;
static int gfOpenGL; /* use OpenGL as backend */
#endif
static unsigned int guLoop = 1000; /* Number of frame redrawings for each test */
static void bench(unsigned long w, unsigned long h, unsigned long bpp);
static void benchExecute(void);
static int checkSDL(const char *fn, int rc);
static void checkEvents(void);
int
main(int argc, char **argv)
{
int rc;
for (int i = 1; i < argc; i++)
{
#ifdef VBOX_OPENGL
if (strcmp(argv[i], "-gl") == 0)
{
gfOpenGL = 1;
continue;
}
#endif
if (strcmp(argv[i], "-loop") == 0 && ++i < argc)
{
guLoop = atoi(argv[i]);
continue;
}
RTPrintf("Unrecognized option '%s'\n", argv[i]);
return -1;
}
#ifdef __WIN__
/* Default to DirectX if nothing else set. "windib" would be possible. */
if (!getenv("SDL_VIDEODRIVER"))
{
_putenv("SDL_VIDEODRIVER=directx");
}
#endif
#ifdef __WIN__
_putenv("SDL_VIDEO_WINDOW_POS=0,0");
#else
setenv("SDL_VIDEO_WINDOW_POS", "0,0", 1);
#endif
rc = SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE);
if (rc != 0)
{
RTPrintf("Error: SDL_InitSubSystem failed with message '%s'\n", SDL_GetError());
return -1;
}
/* output what SDL is capable of */
const SDL_VideoInfo *videoInfo = SDL_GetVideoInfo();
if (!videoInfo)
{
RTPrintf("No SDL video info available!\n");
return -1;
}
RTPrintf("SDL capabilities:\n");
RTPrintf(" Hardware surface support: %s\n", videoInfo->hw_available ? "yes" : "no");
RTPrintf(" Window manager available: %s\n", videoInfo->wm_available ? "yes" : "no");
RTPrintf(" Screen to screen blits accelerated: %s\n", videoInfo->blit_hw ? "yes" : "no");
RTPrintf(" Screen to screen colorkey blits accelerated: %s\n", videoInfo->blit_hw_CC ? "yes" : "no");
RTPrintf(" Screen to screen alpha blits accelerated: %s\n", videoInfo->blit_hw_A ? "yes" : "no");
RTPrintf(" Memory to screen blits accelerated: %s\n", videoInfo->blit_sw ? "yes" : "no");
RTPrintf(" Memory to screen colorkey blits accelerated: %s\n", videoInfo->blit_sw_CC ? "yes" : "no");
RTPrintf(" Memory to screen alpha blits accelerated: %s\n", videoInfo->blit_sw_A ? "yes" : "no");
RTPrintf(" Color fills accelerated: %s\n", videoInfo->blit_fill ? "yes" : "no");
RTPrintf(" Video memory in kilobytes: %d\n", videoInfo->video_mem);
RTPrintf(" Optimal bpp mode: %d\n", videoInfo->vfmt->BitsPerPixel);
char buf[256];
RTPrintf("Video driver SDL_VIDEODRIVER / active: %s/%s\n", getenv("SDL_VIDEODRIVER"),
SDL_VideoDriverName(buf, sizeof(buf)));
RTPrintf("\n"
"Starting tests. Any key pressed inside the SDL window will abort this\n"
"program at the end of the current test. Iterations = %u\n", guLoop);
#ifdef VBOX_OPENGL
RTPrintf("\n========== "ESC_BOLD"OpenGL is %s"ESC_NORM" ==========\n",
gfOpenGL ? "ON" : "OFF");
#endif
bench( 640, 480, 16); bench( 640, 480, 24); bench( 640, 480, 32);
bench(1024, 768, 16); bench(1024, 768, 24); bench(1024, 768, 32);
bench(1280, 1024, 16); bench(1280, 1024, 24); bench(1280, 1024, 32);
RTPrintf("\nSuccess!\n");
return 0;
}
/**
* Method that does the actual resize of the guest framebuffer and
* then changes the SDL framebuffer setup.
*/
static void bench(unsigned long w, unsigned long h, unsigned long bpp)
{
Uint32 Rmask, Gmask, Bmask, Amask = 0;
Uint32 Rsize, Gsize, Bsize;
Uint32 newWidth, newHeight;
guGuestXRes = w;
guGuestYRes = h;
guGuestBpp = bpp;
RTPrintf("\n");
/* a different format we support directly? */
switch (guGuestBpp)
{
case 16:
{
Rmask = 0xF800;
Gmask = 0x07E0;
Bmask = 0x001F;
Amask = 0x0000;
Rsize = 5;
Gsize = 6;
Bsize = 5;
break;
}
case 24:
{
Rmask = 0x00FF0000;
Gmask = 0x0000FF00;
Bmask = 0x000000FF;
Amask = 0x00000000;
Rsize = 8;
Gsize = 8;
Bsize = 8;
break;
}
default:
Rmask = 0x00FF0000;
Gmask = 0x0000FF00;
Bmask = 0x000000FF;
Amask = 0x00000000;
Rsize = 8;
Gsize = 8;
Bsize = 8;
break;
}
int sdlFlags = SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL;
#ifdef VBOX_OPENGL
if (gfOpenGL)
sdlFlags |= SDL_OPENGL;
#endif
if (gfResizable)
sdlFlags |= SDL_RESIZABLE;
if (gfFullscreen)
sdlFlags |= SDL_FULLSCREEN;
/*
* Now we have to check whether there are video mode restrictions
*/
SDL_Rect **modes;
/* Get available fullscreen/hardware modes */
modes = SDL_ListModes(NULL, sdlFlags);
if (modes == NULL)
{
RTPrintf("Error: SDL_ListModes failed with message '%s'\n", SDL_GetError());
return;
}
/* -1 means that any mode is possible (usually non fullscreen) */
if (modes != (SDL_Rect **)-1)
{
/*
* according to the SDL documentation, the API guarantees that
* the modes are sorted from larger to smaller, so we just
* take the first entry as the maximum.
*/
guMaxScreenWidth = modes[0]->w;
guMaxScreenHeight = modes[0]->h;
}
else
{
/* no restriction */
guMaxScreenWidth = ~0;
guMaxScreenHeight = ~0;
}
newWidth = RT_MIN(guMaxScreenWidth, guGuestXRes);
newHeight = RT_MIN(guMaxScreenHeight, guGuestYRes);
/*
* Now set the screen resolution and get the surface pointer
* @todo BPP is not supported!
*/
#ifdef VBOX_OPENGL
if (gfOpenGL)
{
checkSDL("SDL_GL_SetAttribute", SDL_GL_SetAttribute(SDL_GL_RED_SIZE, Rsize));
checkSDL("SDL_GL_SetAttribute", SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, Gsize));
checkSDL("SDL_GL_SetAttribute", SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, Bsize));
checkSDL("SDL_GL_SetAttribute", SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 0));
}
#endif
RTPrintf("Testing "ESC_BOLD"%ldx%ld@%ld"ESC_NORM"\n", guGuestXRes, guGuestYRes, guGuestBpp);
gScreen = SDL_SetVideoMode(newWidth, newHeight, 0, sdlFlags);
if (!gScreen)
{
RTPrintf("SDL_SetVideoMode failed (%s)\n", SDL_GetError());
return;
}
/* first free the current surface */
if (gSurfVRAM)
{
SDL_FreeSurface(gSurfVRAM);
gSurfVRAM = NULL;
}
if (gPtrVRAM)
{
free(gPtrVRAM);
gPtrVRAM = NULL;
}
if (gScreen->format->BitsPerPixel != guGuestBpp)
{
/* Create a source surface from guest VRAM. */
int bytes_per_pixel = (guGuestBpp + 7) / 8;
gPtrVRAM = malloc(guGuestXRes * guGuestYRes * bytes_per_pixel);
gSurfVRAM = SDL_CreateRGBSurfaceFrom(gPtrVRAM, guGuestXRes, guGuestYRes, guGuestBpp,
bytes_per_pixel * guGuestXRes,
Rmask, Gmask, Bmask, Amask);
}
else
{
/* Create a software surface for which SDL allocates the RAM */
gSurfVRAM = SDL_CreateRGBSurface(SDL_SWSURFACE, guGuestXRes, guGuestYRes, guGuestBpp,
Rmask, Gmask, Bmask, Amask);
}
if (!gSurfVRAM)
{
RTPrintf("Failed to allocate surface %ldx%ld@%ld\n",
guGuestXRes, guGuestYRes, guGuestBpp);
return;
}
RTPrintf(" gScreen=%dx%d@%d (surface: %s)\n",
gScreen->w, gScreen->h, gScreen->format->BitsPerPixel,
(gScreen->flags & SDL_HWSURFACE) == 0 ? "software" : "hardware");
SDL_Rect rect = { 0, 0, (Uint16)guGuestXRes, (Uint16)guGuestYRes };
checkSDL("SDL_FillRect",
SDL_FillRect(gSurfVRAM, &rect,
SDL_MapRGB(gSurfVRAM->format, 0x5F, 0x6F, 0x1F)));
#ifdef VBOX_OPENGL
if (gfOpenGL)
{
int r, g, b, d, o;
SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &r);
SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &g);
SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &b);
SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &d);
SDL_GL_GetAttribute(SDL_GL_DOUBLEBUFFER, &o);
RTPrintf(" OpenGL ctxt red=%d, green=%d, blue=%d, depth=%d, dbl=%d", r, g, b, d, o);
glEnable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glGenTextures(1, &gTexture);
glBindTexture(GL_TEXTURE_2D, gTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
for (guTextureWidth = 32; guTextureWidth < newWidth; guTextureWidth <<= 1);
for (guTextureHeight = 32; guTextureHeight < newHeight; guTextureHeight <<= 1);
RTPrintf(", tex %ldx%ld\n", guTextureWidth, guTextureHeight);
switch (guGuestBpp)
{
case 16: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB5, guTextureWidth, guTextureHeight, 0,
GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 0);
break;
case 24: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, guTextureWidth, guTextureHeight, 0,
GL_BGR, GL_UNSIGNED_BYTE, 0);
break;
case 32: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, guTextureWidth, guTextureHeight, 0,
GL_BGRA, GL_UNSIGNED_BYTE, 0);
break;
default: RTPrintf("guGuestBpp=%d?\n", guGuestBpp);
return;
}
glViewport(0, 0, newWidth, newHeight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, newWidth, newHeight, 0.0, -1.0, 1.0);
}
#endif
checkEvents();
benchExecute();
#ifdef VBOX_OPENGL
if (gfOpenGL)
{
glDeleteTextures(1, &gTexture);
}
#endif
}
static void benchExecute()
{
SDL_Rect rect = { 0, 0, (Uint16)guGuestXRes, (Uint16)guGuestYRes };
RTTIMESPEC t1, t2;
RTTimeNow(&t1);
for (unsigned i=0; i<guLoop; i++)
{
#ifdef VBOX_OPENGL
if (!gfOpenGL)
{
#endif
/* SDL backend */
checkSDL("SDL_BlitSurface", SDL_BlitSurface(gSurfVRAM, &rect, gScreen, &rect));
if ((gScreen->flags & SDL_HWSURFACE) == 0)
SDL_UpdateRect(gScreen, rect.x, rect.y, rect.w, rect.h);
#ifdef VBOX_OPENGL
}
else
{
/* OpenGL backend */
glBindTexture(GL_TEXTURE_2D, gTexture);
glPixelStorei(GL_UNPACK_SKIP_PIXELS, rect.x);
glPixelStorei(GL_UNPACK_SKIP_ROWS, rect.y);
glPixelStorei(GL_UNPACK_ROW_LENGTH, gSurfVRAM->pitch / gSurfVRAM->format->BytesPerPixel);
switch (gSurfVRAM->format->BitsPerPixel)
{
case 16: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, rect.w, rect.h,
GL_RGB, GL_UNSIGNED_SHORT_5_6_5, gSurfVRAM->pixels);
break;
case 24: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, rect.w, rect.h,
GL_BGR, GL_UNSIGNED_BYTE, gSurfVRAM->pixels);
break;
case 32: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, rect.w, rect.h,
GL_BGRA, GL_UNSIGNED_BYTE, gSurfVRAM->pixels);
break;
default: RTPrintf("BitsPerPixel=%d?\n", gSurfVRAM->format->BitsPerPixel);
return;
}
GLfloat tx = (GLfloat)((float)rect.w) / guTextureWidth;
GLfloat ty = (GLfloat)((float)rect.h) / guTextureHeight;
glBegin(GL_QUADS);
glColor4f(1.0, 1.0, 1.0, 1.0);
glTexCoord2f(0.0, 0.0); glVertex2i(rect.x, rect.y );
glTexCoord2f(0.0, ty); glVertex2i(rect.x, rect.y + rect.h);
glTexCoord2f(tx, ty); glVertex2i(rect.x + rect.w, rect.y + rect.h);
glTexCoord2f(tx, 0.0); glVertex2i(rect.x + rect.w, rect.y );
glEnd();
glFlush();
}
#endif
}
RTTimeNow(&t2);
int64_t ms = RTTimeSpecGetMilli(&t2) - RTTimeSpecGetMilli(&t1);
printf(" %.1fms/frame\n", (double)ms / guLoop);
}
static int checkSDL(const char *fn, int rc)
{
if (rc == -1)
RTPrintf(""ESC_BOLD"%s() failed:"ESC_NORM" '%s'\n", fn, SDL_GetError());
return rc;
}
static void checkEvents(void)
{
SDL_Event event;
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_KEYDOWN:
RTPrintf("\nKey pressed, exiting ...\n");
exit(-1);
break;
}
}
}