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 |
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 | |
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 | |
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 | |
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 | |
137 | void FrameBufferX11::unlock() |
138 | { |
139 | framebuffer = nullptr; |
140 | } |
141 | |
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 | NO_SANITIZE_FUNCTION sw::FrameBuffer *createFrameBuffer(void *display, Window window, int width, int height) |
190 | { |
191 | return new sw::FrameBufferX11((::Display*)display, window, width, height); |
192 | } |
193 | |