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// Surface.cpp: Implements the egl::Surface class, representing a drawing surface
16// such as the client area of a window, including any back buffers.
17// Implements EGLSurface and related functionality. [EGL 1.4] section 2.2 page 3.
18
19#include "Surface.h"
20
21#include "main.h"
22#include "Display.h"
23#include "Texture.hpp"
24#include "common/Image.hpp"
25#include "Context.hpp"
26#include "common/debug.h"
27#include "Main/FrameBuffer.hpp"
28
29#if defined(__linux__) && !defined(__ANDROID__)
30#include "Main/libX11.hpp"
31#elif defined(_WIN32)
32#include <tchar.h>
33#elif defined(__APPLE__)
34#include "OSXUtils.hpp"
35#endif
36
37#include <algorithm>
38
39namespace egl
40{
41
42Surface::Surface(const Display *display, const Config *config) : display(display), config(config)
43{
44	backBuffer = nullptr;
45	depthStencil = nullptr;
46	texture = nullptr;
47
48	width = 0;
49	height = 0;
50	largestPBuffer = EGL_FALSE;
51	pixelAspectRatio = (EGLint)(1.0 * EGL_DISPLAY_SCALING);   // FIXME: Determine actual pixel aspect ratio
52	renderBuffer = EGL_BACK_BUFFER;
53	swapBehavior = EGL_BUFFER_PRESERVED;
54	textureFormat = EGL_NO_TEXTURE;
55	textureTarget = EGL_NO_TEXTURE;
56	swapInterval = -1;
57	setSwapInterval(1);
58}
59
60Surface::~Surface()
61{
62	Surface::deleteResources();
63}
64
65bool Surface::initialize()
66{
67	ASSERT(!backBuffer && !depthStencil);
68
69	if(libGLES_CM)
70	{
71		backBuffer = libGLES_CM->createBackBuffer(width, height, config);
72	}
73	else if(libGLESv2)
74	{
75		backBuffer = libGLESv2->createBackBuffer(width, height, config);
76	}
77
78	if(!backBuffer)
79	{
80		ERR("Could not create back buffer");
81		deleteResources();
82		return error(EGL_BAD_ALLOC, false);
83	}
84
85	if(config->mDepthStencilFormat != sw::FORMAT_NULL)
86	{
87		if(libGLES_CM)
88		{
89			depthStencil = libGLES_CM->createDepthStencil(width, height, config->mDepthStencilFormat, config->mSamples, false);
90		}
91		else if(libGLESv2)
92		{
93			depthStencil = libGLESv2->createDepthStencil(width, height, config->mDepthStencilFormat, config->mSamples, false);
94		}
95
96		if(!depthStencil)
97		{
98			ERR("Could not create depth/stencil buffer for surface");
99			deleteResources();
100			return error(EGL_BAD_ALLOC, false);
101		}
102	}
103
104	return true;
105}
106
107void Surface::deleteResources()
108{
109	if(depthStencil)
110	{
111		depthStencil->release();
112		depthStencil = nullptr;
113	}
114
115	if(texture)
116	{
117		texture->releaseTexImage();
118		texture = nullptr;
119	}
120
121	if(backBuffer)
122	{
123		backBuffer->release();
124		backBuffer = nullptr;
125	}
126}
127
128egl::Image *Surface::getRenderTarget()
129{
130	if(backBuffer)
131	{
132		backBuffer->addRef();
133	}
134
135	return backBuffer;
136}
137
138egl::Image *Surface::getDepthStencil()
139{
140	if(depthStencil)
141	{
142		depthStencil->addRef();
143	}
144
145	return depthStencil;
146}
147
148void Surface::setSwapBehavior(EGLenum swapBehavior)
149{
150	this->swapBehavior = swapBehavior;
151}
152
153void Surface::setSwapInterval(EGLint interval)
154{
155	if(swapInterval == interval)
156	{
157		return;
158	}
159
160	swapInterval = interval;
161	swapInterval = std::max(swapInterval, display->getMinSwapInterval());
162	swapInterval = std::min(swapInterval, display->getMaxSwapInterval());
163}
164
165EGLint Surface::getConfigID() const
166{
167	return config->mConfigID;
168}
169
170EGLenum Surface::getSurfaceType() const
171{
172	return config->mSurfaceType;
173}
174
175sw::Format Surface::getInternalFormat() const
176{
177	return config->mRenderTargetFormat;
178}
179
180EGLint Surface::getWidth() const
181{
182	return width;
183}
184
185EGLint Surface::getHeight() const
186{
187	return height;
188}
189
190EGLint Surface::getPixelAspectRatio() const
191{
192	return pixelAspectRatio;
193}
194
195EGLenum Surface::getRenderBuffer() const
196{
197	return renderBuffer;
198}
199
200EGLenum Surface::getSwapBehavior() const
201{
202	return swapBehavior;
203}
204
205EGLenum Surface::getTextureFormat() const
206{
207	return textureFormat;
208}
209
210EGLenum Surface::getTextureTarget() const
211{
212	return textureTarget;
213}
214
215EGLBoolean Surface::getLargestPBuffer() const
216{
217	return largestPBuffer;
218}
219
220void Surface::setBoundTexture(egl::Texture *texture)
221{
222	this->texture = texture;
223}
224
225egl::Texture *Surface::getBoundTexture() const
226{
227	return texture;
228}
229
230WindowSurface::WindowSurface(Display *display, const Config *config, EGLNativeWindowType window)
231	: Surface(display, config), window(window)
232{
233	frameBuffer = nullptr;
234}
235
236WindowSurface::~WindowSurface()
237{
238	WindowSurface::deleteResources();
239}
240
241bool WindowSurface::initialize()
242{
243	ASSERT(!frameBuffer && !backBuffer && !depthStencil);
244
245	return checkForResize();
246}
247
248void WindowSurface::swap()
249{
250	if(backBuffer && frameBuffer)
251	{
252		void *source = backBuffer->lockInternal(0, 0, 0, sw::LOCK_READONLY, sw::PUBLIC);
253		frameBuffer->flip(source, backBuffer->sw::Surface::getInternalFormat(), backBuffer->getInternalPitchB());
254		backBuffer->unlockInternal();
255
256		checkForResize();
257	}
258}
259
260EGLNativeWindowType WindowSurface::getWindowHandle() const
261{
262	return window;
263}
264
265bool WindowSurface::checkForResize()
266{
267	#if defined(_WIN32)
268		RECT client;
269		if(!GetClientRect(window, &client))
270		{
271			ASSERT(false);
272			return false;
273		}
274
275		int windowWidth = client.right - client.left;
276		int windowHeight = client.bottom - client.top;
277	#elif defined(__ANDROID__)
278		int windowWidth;  window->query(window, NATIVE_WINDOW_WIDTH, &windowWidth);
279		int windowHeight; window->query(window, NATIVE_WINDOW_HEIGHT, &windowHeight);
280	#elif defined(__linux__)
281		XWindowAttributes windowAttributes;
282		libX11->XGetWindowAttributes((::Display*)display->getNativeDisplay(), window, &windowAttributes);
283
284		int windowWidth = windowAttributes.width;
285		int windowHeight = windowAttributes.height;
286	#elif defined(__APPLE__)
287		int windowWidth;
288		int windowHeight;
289		sw::OSX::GetNativeWindowSize(window, windowWidth, windowHeight);
290	#else
291		#error "WindowSurface::checkForResize unimplemented for this platform"
292	#endif
293
294	if((windowWidth != width) || (windowHeight != height))
295	{
296		bool success = reset(windowWidth, windowHeight);
297
298		if(getCurrentDrawSurface() == this)
299		{
300			getCurrentContext()->makeCurrent(this);
301		}
302
303		return success;
304	}
305
306	return true;   // Success
307}
308
309void WindowSurface::deleteResources()
310{
311	delete frameBuffer;
312	frameBuffer = nullptr;
313
314	Surface::deleteResources();
315}
316
317bool WindowSurface::reset(int backBufferWidth, int backBufferHeight)
318{
319	width = backBufferWidth;
320	height = backBufferHeight;
321
322	deleteResources();
323
324	if(window)
325	{
326		if(libGLES_CM)
327		{
328			frameBuffer = libGLES_CM->createFrameBuffer(display->getNativeDisplay(), window, width, height);
329		}
330		else if(libGLESv2)
331		{
332			frameBuffer = libGLESv2->createFrameBuffer(display->getNativeDisplay(), window, width, height);
333		}
334
335		if(!frameBuffer)
336		{
337			ERR("Could not create frame buffer");
338			deleteResources();
339			return error(EGL_BAD_ALLOC, false);
340		}
341	}
342
343	return Surface::initialize();
344}
345
346PBufferSurface::PBufferSurface(Display *display, const Config *config, EGLint width, EGLint height, EGLenum textureFormat, EGLenum textureType, EGLBoolean largestPBuffer)
347	: Surface(display, config)
348{
349	this->width = width;
350	this->height = height;
351	this->largestPBuffer = largestPBuffer;
352}
353
354PBufferSurface::~PBufferSurface()
355{
356	PBufferSurface::deleteResources();
357}
358
359void PBufferSurface::swap()
360{
361	// No effect
362}
363
364EGLNativeWindowType PBufferSurface::getWindowHandle() const
365{
366	UNREACHABLE(-1);   // Should not be called. Only WindowSurface has a window handle.
367
368	return 0;
369}
370
371void PBufferSurface::deleteResources()
372{
373	Surface::deleteResources();
374}
375
376}
377