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 #include <stdlib.h>
25 
26 namespace sw
27 {
28 	static int (*PreviousXErrorHandler)(Display *display, XErrorEvent *event) = 0;
29 	static bool shmBadAccess = false;
30 
31 	// Catches BadAcces errors so we can fall back to not using MIT-SHM
XShmErrorHandler(Display * display,XErrorEvent * event)32 	static int XShmErrorHandler(Display *display, XErrorEvent *event)
33 	{
34 		if(event->error_code == BadAccess)
35 		{
36 			shmBadAccess = true;
37 			return 0;
38 		}
39 		else
40 		{
41 			return PreviousXErrorHandler(display, event);
42 		}
43 	}
44 
FrameBufferX11(Display * display,Window window,int width,int height)45 	FrameBufferX11::FrameBufferX11(Display *display, Window window, int width, int height) : FrameBuffer(width, height, false, false), ownX11(!display), x_display(display), x_window(window)
46 	{
47 		if(!x_display)
48 		{
49 			x_display = libX11->XOpenDisplay(0);
50 			assert(x_display);
51 		}
52 
53 		int screen = DefaultScreen(x_display);
54 		x_gc = libX11->XDefaultGC(x_display, screen);
55 		int depth = libX11->XDefaultDepth(x_display, screen);
56 
57 		XVisualInfo x_visual;
58 		Status status = libX11->XMatchVisualInfo(x_display, screen, 32, TrueColor, &x_visual);
59 		bool match = (status != 0 && x_visual.blue_mask == 0xFF);   // Prefer X8R8G8B8
60 		Visual *visual = match ? x_visual.visual : libX11->XDefaultVisual(x_display, screen);
61 
62 		mit_shm = (libX11->XShmQueryExtension && libX11->XShmQueryExtension(x_display) == True);
63 
64 		if(mit_shm)
65 		{
66 			x_image = libX11->XShmCreateImage(x_display, visual, depth, ZPixmap, 0, &shminfo, width, height);
67 
68 			shminfo.shmid = shmget(IPC_PRIVATE, x_image->bytes_per_line * x_image->height, IPC_CREAT | SHM_R | SHM_W);
69 			shminfo.shmaddr = x_image->data = (char*)shmat(shminfo.shmid, 0, 0);
70 			shminfo.readOnly = False;
71 
72 			PreviousXErrorHandler = libX11->XSetErrorHandler(XShmErrorHandler);
73 			libX11->XShmAttach(x_display, &shminfo);   // May produce a BadAccess error
74 			libX11->XSync(x_display, False);
75 			libX11->XSetErrorHandler(PreviousXErrorHandler);
76 
77 			if(shmBadAccess)
78 			{
79 				mit_shm = false;
80 
81 				XDestroyImage(x_image);
82 				shmdt(shminfo.shmaddr);
83 				shmctl(shminfo.shmid, IPC_RMID, 0);
84 
85 				shmBadAccess = false;
86 			}
87 		}
88 
89 		if(!mit_shm)
90 		{
91 			int bytes_per_line = width * 4;
92 			int bytes_per_image = height * bytes_per_line;
93 			char *buffer = (char*)malloc(bytes_per_image);
94 			memset(buffer, 0, bytes_per_image);
95 
96 			x_image = libX11->XCreateImage(x_display, visual, depth, ZPixmap, 0, buffer, width, height, 32, bytes_per_line);
97 			assert(x_image);
98 
99 			if(!x_image)
100 			{
101 				free(buffer);
102 			}
103 		}
104 	}
105 
~FrameBufferX11()106 	FrameBufferX11::~FrameBufferX11()
107 	{
108 		if(!mit_shm)
109 		{
110 			XDestroyImage(x_image);
111 		}
112 		else
113 		{
114 			libX11->XShmDetach(x_display, &shminfo);
115 			XDestroyImage(x_image);
116 			shmdt(shminfo.shmaddr);
117 			shmctl(shminfo.shmid, IPC_RMID, 0);
118 		}
119 
120 		if(ownX11)
121 		{
122 			libX11->XCloseDisplay(x_display);
123 		}
124 	}
125 
lock()126 	void *FrameBufferX11::lock()
127 	{
128 		if(x_image)
129 		{
130 			stride = x_image->bytes_per_line;
131 			framebuffer = x_image->data;
132 		}
133 
134 		return framebuffer;
135 	}
136 
unlock()137 	void FrameBufferX11::unlock()
138 	{
139 		framebuffer = nullptr;
140 	}
141 
blit(sw::Surface * source,const Rect * sourceRect,const Rect * destRect)142 	void FrameBufferX11::blit(sw::Surface *source, const Rect *sourceRect, const Rect *destRect)
143 	{
144 		copy(source);
145 
146 		if(!mit_shm)
147 		{
148 			libX11->XPutImage(x_display, x_window, x_gc, x_image, 0, 0, 0, 0, width, height);
149 		}
150 		else
151 		{
152 			libX11->XShmPutImage(x_display, x_window, x_gc, x_image, 0, 0, 0, 0, width, height, False);
153 		}
154 
155 		libX11->XSync(x_display, False);
156 
157 		if(false)   // Draw the framerate on screen
158 		{
159 			static double fpsTime = sw::Timer::seconds();
160 			static int frames = -1;
161 
162 			double time = sw::Timer::seconds();
163 			double delta = time - fpsTime;
164 			frames++;
165 
166 			static double FPS = 0.0;
167 			static double maxFPS = 0.0;
168 
169 			if(delta > 1.0)
170 			{
171 				FPS = frames / delta;
172 
173 				fpsTime = time;
174 				frames = 0;
175 
176 				if(FPS > maxFPS)
177 				{
178 					maxFPS = FPS;
179 				}
180 			}
181 
182 			char string[256];
183 			sprintf(string, "FPS: %.2f (max: %.2f)", FPS, maxFPS);
184 			libX11->XDrawString(x_display, x_window, x_gc, 50, 50, string, strlen(string));
185 		}
186 	}
187 }
188 
189 // Chromium can be built with USE_X11 && USE_OZONE. In case of pure USE_X11 builds, we can use this path.
190 // In another case, the decision what framebuffer to create will be done by the FrameBufferFactoryOzone.
191 #if !defined(USE_OZONE)
createFrameBuffer(void * display,Window window,int width,int height)192 NO_SANITIZE_FUNCTION sw::FrameBuffer *createFrameBuffer(void *display, Window window, int width, int height)
193 {
194 	return new sw::FrameBufferX11((::Display*)display, window, width, height);
195 }
196 #endif
197