summaryrefslogtreecommitdiffstats
path: root/src/VBox/Frontends/VBoxSDL/VBoxSDLTest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Frontends/VBoxSDL/VBoxSDLTest.cpp')
-rw-r--r--src/VBox/Frontends/VBoxSDL/VBoxSDLTest.cpp478
1 files changed, 478 insertions, 0 deletions
diff --git a/src/VBox/Frontends/VBoxSDL/VBoxSDLTest.cpp b/src/VBox/Frontends/VBoxSDL/VBoxSDLTest.cpp
new file mode 100644
index 00000000..ac8c2d77
--- /dev/null
+++ b/src/VBox/Frontends/VBoxSDL/VBoxSDLTest.cpp
@@ -0,0 +1,478 @@
+/* $Id: VBoxSDLTest.cpp $ */
+/** @file
+ *
+ * VBox frontends: VBoxSDL (simple frontend based on SDL):
+ * VBoxSDL testcases
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program 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 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifdef _MSC_VER
+# pragma warning(push)
+# pragma warning(disable:4121)
+#endif
+#if defined(RT_OS_WINDOWS) /// @todo someone please explain why we don't follow the book!
+# define _SDL_main_h
+#endif
+#include <SDL.h>
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
+
+#include <iprt/assert.h>
+#include <iprt/env.h>
+#include <iprt/initterm.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 RT_OS_WINDOWS
+#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;
+ RTR3InitExe(argc, &argv, 0);
+
+ 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 RT_OS_WINDOWS
+ /* Default to DirectX if nothing else set. "windib" would be possible. */
+ if (!RTEnvExist("SDL_VIDEODRIVER"))
+ {
+ _putenv("SDL_VIDEODRIVER=directx");
+ }
+#endif
+
+#ifdef RT_OS_WINDOWS
+ _putenv("SDL_VIDEO_WINDOW_POS=0,0");
+#else
+ RTEnvSet("SDL_VIDEO_WINDOW_POS", "0,0");
+#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", RTEnvGet("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 = ~0U;
+ guMaxScreenHeight = ~0U;
+ }
+
+ 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));
+ }
+#else
+ NOREF(Rsize); NOREF(Gsize); NOREF(Bsize);
+#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;
+ }
+ }
+}