Home | History | Annotate | Download | only in Main
      1 // Copyright 2016 The SwiftShader Authors. All Rights Reserved.
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //    http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 #include "FrameBufferX11.hpp"
     16 
     17 #include "libX11.hpp"
     18 #include "Common/Timer.hpp"
     19 
     20 #include <sys/ipc.h>
     21 #include <sys/shm.h>
     22 #include <string.h>
     23 #include <assert.h>
     24 
     25 namespace sw
     26 {
     27 	static int (*PreviousXErrorHandler)(Display *display, XErrorEvent *event) = 0;
     28 	static bool shmBadAccess = false;
     29 
     30 	// Catches BadAcces errors so we can fall back to not using MIT-SHM
     31 	static int XShmErrorHandler(Display *display, XErrorEvent *event)
     32 	{
     33 		if(event->error_code == BadAccess)
     34 		{
     35 			shmBadAccess = true;
     36 			return 0;
     37 		}
     38 		else
     39 		{
     40 			return PreviousXErrorHandler(display, event);
     41 		}
     42 	}
     43 
     44 	FrameBufferX11::FrameBufferX11(Display *display, Window window, int width, int height) : FrameBuffer(width, height, false, false), ownX11(!display), x_display(display), x_window(window)
     45 	{
     46 		if(!x_display)
     47 		{
     48 			x_display = libX11->XOpenDisplay(0);
     49 		}
     50 
     51 		int screen = DefaultScreen(x_display);
     52 		x_gc = libX11->XDefaultGC(x_display, screen);
     53 		int depth = libX11->XDefaultDepth(x_display, screen);
     54 
     55 		Status status = libX11->XMatchVisualInfo(x_display, screen, 32, TrueColor, &x_visual);
     56 		bool match = (status != 0 && x_visual.blue_mask == 0xFF);   // Prefer X8R8G8B8
     57 		Visual *visual = match ? x_visual.visual : libX11->XDefaultVisual(x_display, screen);
     58 
     59 		mit_shm = (libX11->XShmQueryExtension && libX11->XShmQueryExtension(x_display) == True);
     60 
     61 		if(mit_shm)
     62 		{
     63 			x_image = libX11->XShmCreateImage(x_display, visual, depth, ZPixmap, 0, &shminfo, width, height);
     64 
     65 			shminfo.shmid = shmget(IPC_PRIVATE, x_image->bytes_per_line * x_image->height, IPC_CREAT | SHM_R | SHM_W);
     66 			shminfo.shmaddr = x_image->data = buffer = (char*)shmat(shminfo.shmid, 0, 0);
     67 			shminfo.readOnly = False;
     68 
     69 			PreviousXErrorHandler = libX11->XSetErrorHandler(XShmErrorHandler);
     70 			libX11->XShmAttach(x_display, &shminfo);   // May produce a BadAccess error
     71 			libX11->XSync(x_display, False);
     72 			libX11->XSetErrorHandler(PreviousXErrorHandler);
     73 
     74 			if(shmBadAccess)
     75 			{
     76 				mit_shm = false;
     77 
     78 				XDestroyImage(x_image);
     79 				shmdt(shminfo.shmaddr);
     80 				shmctl(shminfo.shmid, IPC_RMID, 0);
     81 
     82 				shmBadAccess = false;
     83 			}
     84 		}
     85 
     86 		if(!mit_shm)
     87 		{
     88 			int bytes_per_line = width * 4;
     89 			int bytes_per_image = height * bytes_per_line;
     90 			buffer = new char[bytes_per_image];
     91 			memset(buffer, 0, bytes_per_image);
     92 			x_image = libX11->XCreateImage(x_display, visual, depth, ZPixmap, 0, buffer, width, height, 32, bytes_per_line);
     93 		}
     94 	}
     95 
     96 	FrameBufferX11::~FrameBufferX11()
     97 	{
     98 		if(!mit_shm)
     99 		{
    100 			x_image->data = 0;
    101 			XDestroyImage(x_image);
    102 
    103 			delete[] buffer;
    104 			buffer = 0;
    105 		}
    106 		else
    107 		{
    108 			libX11->XShmDetach(x_display, &shminfo);
    109 			XDestroyImage(x_image);
    110 			shmdt(shminfo.shmaddr);
    111 			shmctl(shminfo.shmid, IPC_RMID, 0);
    112 		}
    113 
    114 		if(ownX11)
    115 		{
    116 			libX11->XCloseDisplay(x_display);
    117 		}
    118 	}
    119 
    120 	void *FrameBufferX11::lock()
    121 	{
    122 		stride = x_image->bytes_per_line;
    123 		framebuffer = buffer;
    124 
    125 		return framebuffer;
    126 	}
    127 
    128 	void FrameBufferX11::unlock()
    129 	{
    130 		framebuffer = nullptr;
    131 	}
    132 
    133 	void FrameBufferX11::blit(sw::Surface *source, const Rect *sourceRect, const Rect *destRect)
    134 	{
    135 		copy(source);
    136 
    137 		if(!mit_shm)
    138 		{
    139 			libX11->XPutImage(x_display, x_window, x_gc, x_image, 0, 0, 0, 0, width, height);
    140 		}
    141 		else
    142 		{
    143 			libX11->XShmPutImage(x_display, x_window, x_gc, x_image, 0, 0, 0, 0, width, height, False);
    144 		}
    145 
    146 		libX11->XSync(x_display, False);
    147 
    148 		if(false)   // Draw the framerate on screen
    149 		{
    150 			static double fpsTime = sw::Timer::seconds();
    151 			static int frames = -1;
    152 
    153 			double time = sw::Timer::seconds();
    154 			double delta = time - fpsTime;
    155 			frames++;
    156 
    157 			static double FPS = 0.0;
    158 			static double maxFPS = 0.0;
    159 
    160 			if(delta > 1.0)
    161 			{
    162 				FPS = frames / delta;
    163 
    164 				fpsTime = time;
    165 				frames = 0;
    166 
    167 				if(FPS > maxFPS)
    168 				{
    169 					maxFPS = FPS;
    170 				}
    171 			}
    172 
    173 			char string[256];
    174 			sprintf(string, "FPS: %.2f (max: %.2f)", FPS, maxFPS);
    175 			libX11->XDrawString(x_display, x_window, x_gc, 50, 50, string, strlen(string));
    176 		}
    177 	}
    178 }
    179 
    180 NO_SANITIZE_FUNCTION sw::FrameBuffer *createFrameBuffer(void *display, Window window, int width, int height)
    181 {
    182 	return new sw::FrameBufferX11((::Display*)display, window, width, height);
    183 }
    184