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 Surface class, representing a drawing surface
16// such as the client area of a window, including any back buffers.
17
18#include "Surface.h"
19
20#include "main.h"
21#include "Display.h"
22#include "Image.hpp"
23#include "Context.h"
24#include "common/debug.h"
25#include "Main/FrameBuffer.hpp"
26
27#if defined(_WIN32)
28#include <tchar.h>
29#endif
30
31#include <algorithm>
32
33namespace gl
34{
35
36Surface::Surface(Display *display, NativeWindowType window)
37	: mDisplay(display), mWindow(window)
38{
39	frameBuffer = 0;
40	backBuffer = 0;
41
42	mDepthStencil = nullptr;
43	mTextureFormat = GL_NONE;
44	mTextureTarget = GL_NONE;
45
46	mSwapInterval = -1;
47	setSwapInterval(1);
48}
49
50Surface::Surface(Display *display, GLint width, GLint height, GLenum textureFormat, GLenum textureType)
51	: mDisplay(display), mWindow(nullptr), mWidth(width), mHeight(height)
52{
53	frameBuffer = 0;
54	backBuffer = 0;
55
56	mDepthStencil = nullptr;
57	mWindowSubclassed = false;
58	mTextureFormat = textureFormat;
59	mTextureTarget = textureType;
60
61	mSwapInterval = -1;
62	setSwapInterval(1);
63}
64
65Surface::~Surface()
66{
67	release();
68}
69
70bool Surface::initialize()
71{
72	ASSERT(!frameBuffer && !backBuffer && !mDepthStencil);
73
74	return reset();
75}
76
77void Surface::release()
78{
79	if(mDepthStencil)
80	{
81		mDepthStencil->release();
82		mDepthStencil = nullptr;
83	}
84
85	if(backBuffer)
86	{
87		backBuffer->release();
88		backBuffer = 0;
89	}
90
91	delete frameBuffer;
92	frameBuffer = 0;
93}
94
95bool Surface::reset()
96{
97	if(!mWindow)
98	{
99		return reset(mWidth, mHeight);
100	}
101
102	// FIXME: Wrap into an abstract Window class
103	#if defined(_WIN32)
104		RECT windowRect;
105		GetClientRect(mWindow, &windowRect);
106
107		return reset(windowRect.right - windowRect.left, windowRect.bottom - windowRect.top);
108	#else
109		XWindowAttributes windowAttributes;
110		XGetWindowAttributes(mDisplay->getNativeDisplay(), mWindow, &windowAttributes);
111
112		return reset(windowAttributes.width, windowAttributes.height);
113	#endif
114}
115
116bool Surface::reset(int backBufferWidth, int backBufferHeight)
117{
118	release();
119
120	if(mWindow)
121	{
122		frameBuffer = ::createFrameBuffer(mDisplay->getNativeDisplay(), mWindow, backBufferWidth, backBufferHeight);
123
124		if(!frameBuffer)
125		{
126			ERR("Could not create frame buffer");
127			release();
128			return error(GL_OUT_OF_MEMORY, false);
129		}
130	}
131
132	backBuffer = new Image(0, backBufferWidth, backBufferHeight, GL_RGB, GL_UNSIGNED_BYTE);
133
134	if(!backBuffer)
135	{
136		ERR("Could not create back buffer");
137		release();
138		return error(GL_OUT_OF_MEMORY, false);
139	}
140
141	if(true)   // Always provide a depth/stencil buffer
142	{
143		mDepthStencil = new Image(0, backBufferWidth, backBufferHeight, sw::FORMAT_D24S8, 1, false, true);
144
145		if(!mDepthStencil)
146		{
147			ERR("Could not create depth/stencil buffer for surface");
148			release();
149			return error(GL_OUT_OF_MEMORY, false);
150		}
151	}
152
153	mWidth = backBufferWidth;
154	mHeight = backBufferHeight;
155
156	return true;
157}
158
159void Surface::swap()
160{
161	if(backBuffer)
162	{
163		void *source = backBuffer->lockInternal(0, 0, 0, sw::LOCK_READONLY, sw::PUBLIC);
164		frameBuffer->flip(source, backBuffer->Surface::getInternalFormat(), backBuffer->getInternalPitchB());
165		backBuffer->unlockInternal();
166
167		checkForResize();
168	}
169}
170
171Image *Surface::getRenderTarget()
172{
173	if(backBuffer)
174	{
175		backBuffer->addRef();
176	}
177
178	return backBuffer;
179}
180
181Image *Surface::getDepthStencil()
182{
183	if(mDepthStencil)
184	{
185		mDepthStencil->addRef();
186	}
187
188	return mDepthStencil;
189}
190
191void Surface::setSwapInterval(GLint interval)
192{
193	if(mSwapInterval == interval)
194	{
195		return;
196	}
197
198	mSwapInterval = interval;
199	mSwapInterval = std::max(mSwapInterval, mDisplay->getMinSwapInterval());
200	mSwapInterval = std::min(mSwapInterval, mDisplay->getMaxSwapInterval());
201}
202
203GLint Surface::getWidth() const
204{
205	return mWidth;
206}
207
208GLint Surface::getHeight() const
209{
210	return mHeight;
211}
212
213GLenum Surface::getTextureFormat() const
214{
215	return mTextureFormat;
216}
217
218GLenum Surface::getTextureTarget() const
219{
220	return mTextureTarget;
221}
222
223bool Surface::checkForResize()
224{
225	#if defined(_WIN32)
226		RECT client;
227		if(!GetClientRect(mWindow, &client))
228		{
229			ASSERT(false);
230			return false;
231		}
232
233		int clientWidth = client.right - client.left;
234		int clientHeight = client.bottom - client.top;
235	#else
236		XWindowAttributes windowAttributes;
237		XGetWindowAttributes(mDisplay->getNativeDisplay(), mWindow, &windowAttributes);
238
239		int clientWidth = windowAttributes.width;
240		int clientHeight = windowAttributes.height;
241	#endif
242
243	bool sizeDirty = clientWidth != getWidth() || clientHeight != getHeight();
244
245	if(sizeDirty)
246	{
247		reset(clientWidth, clientHeight);
248
249		if(getCurrentDrawSurface() == this)
250		{
251			getContext()->makeCurrent(this);
252		}
253
254		return true;
255	}
256
257	return false;
258}
259}
260