blob: 7d77f7a53fa5d660fcaf3e11ef37639347324657 [file] [log] [blame]
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001//
2// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6
7// Surface.cpp: Implements the egl::Surface class, representing a drawing surface
8// such as the client area of a window, including any back buffers.
9// Implements EGLSurface and related functionality. [EGL 1.4] section 2.2 page 3.
10
11#include <tchar.h>
12
13#include "libEGL/Surface.h"
14
15#include "common/debug.h"
16#include "libGLESv2/Texture.h"
17
18#include "libEGL/main.h"
19#include "libEGL/Display.h"
20
21#include <dwmapi.h>
22
23namespace egl
24{
25
26namespace
27{
28const int versionWindowsVista = MAKEWORD(0x00, 0x06);
29const int versionWindows7 = MAKEWORD(0x01, 0x06);
30
31// Return the version of the operating system in a format suitable for ordering
32// comparison.
33int getComparableOSVersion()
34{
35 DWORD version = GetVersion();
36 int majorVersion = LOBYTE(LOWORD(version));
37 int minorVersion = HIBYTE(LOWORD(version));
38 return MAKEWORD(minorVersion, majorVersion);
39}
40}
41
42Surface::Surface(Display *display, const Config *config, HWND window, EGLint postSubBufferSupported)
43 : mDisplay(display), mConfig(config), mWindow(window), mPostSubBufferSupported(postSubBufferSupported)
44{
45 mSwapChain = NULL;
46 mBackBuffer = NULL;
47 mDepthStencil = NULL;
48 mRenderTarget = NULL;
49 mOffscreenTexture = NULL;
50 mShareHandle = NULL;
51 mTexture = NULL;
52 mTextureFormat = EGL_NO_TEXTURE;
53 mTextureTarget = EGL_NO_TEXTURE;
54
55 mPixelAspectRatio = (EGLint)(1.0 * EGL_DISPLAY_SCALING); // FIXME: Determine actual pixel aspect ratio
56 mRenderBuffer = EGL_BACK_BUFFER;
57 mSwapBehavior = EGL_BUFFER_PRESERVED;
58 mSwapInterval = -1;
59 setSwapInterval(1);
60
61 subclassWindow();
62}
63
64Surface::Surface(Display *display, const Config *config, HANDLE shareHandle, EGLint width, EGLint height, EGLenum textureFormat, EGLenum textureType)
65 : mDisplay(display), mWindow(NULL), mConfig(config), mShareHandle(shareHandle), mWidth(width), mHeight(height), mPostSubBufferSupported(EGL_FALSE)
66{
67 mSwapChain = NULL;
68 mBackBuffer = NULL;
69 mDepthStencil = NULL;
70 mRenderTarget = NULL;
71 mOffscreenTexture = NULL;
72 mWindowSubclassed = false;
73 mTexture = NULL;
74 mTextureFormat = textureFormat;
75 mTextureTarget = textureType;
76
77 mPixelAspectRatio = (EGLint)(1.0 * EGL_DISPLAY_SCALING); // FIXME: Determine actual pixel aspect ratio
78 mRenderBuffer = EGL_BACK_BUFFER;
79 mSwapBehavior = EGL_BUFFER_PRESERVED;
80 mSwapInterval = -1;
81 setSwapInterval(1);
82}
83
84Surface::~Surface()
85{
86 unsubclassWindow();
87 release();
88}
89
90bool Surface::initialize()
91{
92 ASSERT(!mSwapChain && !mOffscreenTexture && !mDepthStencil);
93
94 if (!resetSwapChain())
95 return false;
96
97 // Modify present parameters for this window, if we are composited,
98 // to minimize the amount of queuing done by DWM between our calls to
99 // present and the actual screen.
100 if (mWindow && (getComparableOSVersion() >= versionWindowsVista)) {
101 BOOL isComposited;
102 HRESULT result = DwmIsCompositionEnabled(&isComposited);
103 if (SUCCEEDED(result) && isComposited) {
104 DWM_PRESENT_PARAMETERS presentParams;
105 memset(&presentParams, 0, sizeof(presentParams));
106 presentParams.cbSize = sizeof(DWM_PRESENT_PARAMETERS);
107 presentParams.cBuffer = 2;
108
109 result = DwmSetPresentParameters(mWindow, &presentParams);
110 if (FAILED(result))
111 ERR("Unable to set present parameters: 0x%08X", result);
112 }
113 }
114
115 return true;
116}
117
118void Surface::release()
119{
120 if (mSwapChain)
121 {
122 mSwapChain->Release();
123 mSwapChain = NULL;
124 }
125
126 if (mBackBuffer)
127 {
128 mBackBuffer->Release();
129 mBackBuffer = NULL;
130 }
131
132 if (mDepthStencil)
133 {
134 mDepthStencil->Release();
135 mDepthStencil = NULL;
136 }
137
138 if (mRenderTarget)
139 {
140 mRenderTarget->Release();
141 mRenderTarget = NULL;
142 }
143
144 if (mOffscreenTexture)
145 {
146 mOffscreenTexture->Release();
147 mOffscreenTexture = NULL;
148 }
149
150 if (mTexture)
151 {
152 mTexture->releaseTexImage();
153 mTexture = NULL;
154 }
155
156 mShareHandle = NULL;
157}
158
159bool Surface::resetSwapChain()
160{
161 if (!mWindow)
162 {
163 return resetSwapChain(mWidth, mHeight);
164 }
165
166 RECT windowRect;
167 if (!GetClientRect(getWindowHandle(), &windowRect))
168 {
169 ASSERT(false);
170
171 ERR("Could not retrieve the window dimensions");
172 return false;
173 }
174
175 return resetSwapChain(windowRect.right - windowRect.left, windowRect.bottom - windowRect.top);
176}
177
178bool Surface::resetSwapChain(int backbufferWidth, int backbufferHeight)
179{
180 IDirect3DDevice9 *device = mDisplay->getDevice();
181
182 if (device == NULL)
183 {
184 return false;
185 }
186
187 // Evict all non-render target textures to system memory and release all resources
188 // before reallocating them to free up as much video memory as possible.
189 device->EvictManagedResources();
190
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000191 HRESULT result;
192
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000193 // Release specific resources to free up memory for the new render target, while the
194 // old render target still exists for the purpose of preserving its contents.
195 if (mSwapChain)
196 {
197 mSwapChain->Release();
198 mSwapChain = NULL;
199 }
200
201 if (mBackBuffer)
202 {
203 mBackBuffer->Release();
204 mBackBuffer = NULL;
205 }
206
207 if (mOffscreenTexture)
208 {
209 mOffscreenTexture->Release();
210 mOffscreenTexture = NULL;
211 }
212
213 if (mDepthStencil)
214 {
215 mDepthStencil->Release();
216 mDepthStencil = NULL;
217 }
218
219 mShareHandle = NULL;
220 HANDLE *pShareHandle = NULL;
221 if (!mWindow && mDisplay->shareHandleSupported())
222 {
223 pShareHandle = &mShareHandle;
224 }
225
apatrick@chromium.org0c71fd42012-08-10 18:08:47 +0000226 result = device->CreateTexture(backbufferWidth, backbufferHeight, 1, D3DUSAGE_RENDERTARGET,
227 mConfig->mRenderTargetFormat, D3DPOOL_DEFAULT, &mOffscreenTexture, pShareHandle);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000228 if (FAILED(result))
229 {
230 ERR("Could not create offscreen texture: %08lX", result);
231 release();
232
233 if(isDeviceLostError(result))
234 {
235 mDisplay->notifyDeviceLost();
236 return false;
237 }
238 else
239 {
240 return error(EGL_BAD_ALLOC, false);
241 }
242 }
243
244 IDirect3DSurface9 *oldRenderTarget = mRenderTarget;
245
246 result = mOffscreenTexture->GetSurfaceLevel(0, &mRenderTarget);
247 ASSERT(SUCCEEDED(result));
248
249 if (oldRenderTarget)
250 {
251 RECT rect =
252 {
253 0, 0,
254 mWidth, mHeight
255 };
256
apatrick@chromium.org0c71fd42012-08-10 18:08:47 +0000257 if (rect.right > static_cast<LONG>(backbufferWidth))
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000258 {
apatrick@chromium.org0c71fd42012-08-10 18:08:47 +0000259 rect.right = backbufferWidth;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000260 }
261
apatrick@chromium.org0c71fd42012-08-10 18:08:47 +0000262 if (rect.bottom > static_cast<LONG>(backbufferHeight))
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000263 {
apatrick@chromium.org0c71fd42012-08-10 18:08:47 +0000264 rect.bottom = backbufferHeight;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000265 }
266
267 mDisplay->endScene();
268
269 result = device->StretchRect(oldRenderTarget, &rect, mRenderTarget, &rect, D3DTEXF_NONE);
270 ASSERT(SUCCEEDED(result));
271
272 oldRenderTarget->Release();
273 }
274
275 if (mWindow)
276 {
apatrick@chromium.org0c71fd42012-08-10 18:08:47 +0000277 D3DPRESENT_PARAMETERS presentParameters = {0};
278 presentParameters.AutoDepthStencilFormat = mConfig->mDepthStencilFormat;
279 presentParameters.BackBufferCount = 1;
280 presentParameters.BackBufferFormat = mConfig->mRenderTargetFormat;
281 presentParameters.EnableAutoDepthStencil = FALSE;
282 presentParameters.Flags = 0;
283 presentParameters.hDeviceWindow = getWindowHandle();
284 presentParameters.MultiSampleQuality = 0; // FIXME: Unimplemented
285 presentParameters.MultiSampleType = D3DMULTISAMPLE_NONE; // FIXME: Unimplemented
286 presentParameters.PresentationInterval = mPresentInterval;
287 presentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
288 presentParameters.Windowed = TRUE;
289 presentParameters.BackBufferWidth = backbufferWidth;
290 presentParameters.BackBufferHeight = backbufferHeight;
291
292 // http://crbug.com/140239
apatrick@chromium.org85e44192012-08-17 20:58:01 +0000293 // http://crbug.com/143434
294 //
295 // Some AMD/Intel switchable systems / drivers appear to round swap chain surfaces to a multiple of 64 pixels in width
296 // when using the integrated Intel. This rounds the width up rather than down.
297 //
298 // Some non-switchable AMD GPUs / drivers do not respect the source rectangle to Present. Therefore, when the vendor ID
299 // is not Intel, the back buffer width must be exactly the same width as the window or horizontal scaling will occur.
300 D3DADAPTER_IDENTIFIER9* adapterIdentifier = mDisplay->getAdapterIdentifier();
301 if (adapterIdentifier->VendorId == VENDOR_ID_INTEL)
302 {
303 presentParameters.BackBufferWidth = (presentParameters.BackBufferWidth + 63) / 64 * 64;
304 }
apatrick@chromium.org0c71fd42012-08-10 18:08:47 +0000305
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000306 result = device->CreateAdditionalSwapChain(&presentParameters, &mSwapChain);
307
308 if (FAILED(result))
309 {
310 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_INVALIDCALL || result == D3DERR_DEVICELOST);
311
312 ERR("Could not create additional swap chains or offscreen surfaces: %08lX", result);
313 release();
314
315 if(isDeviceLostError(result))
316 {
317 mDisplay->notifyDeviceLost();
318 return false;
319 }
320 else
321 {
322 return error(EGL_BAD_ALLOC, false);
323 }
324 }
325
326 result = mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &mBackBuffer);
327 ASSERT(SUCCEEDED(result));
328 }
329
330 if (mConfig->mDepthStencilFormat != D3DFMT_UNKNOWN)
331 {
apatrick@chromium.org0c71fd42012-08-10 18:08:47 +0000332 result = device->CreateDepthStencilSurface(backbufferWidth, backbufferHeight, mConfig->mDepthStencilFormat, D3DMULTISAMPLE_NONE,
333 0, FALSE, &mDepthStencil, NULL);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000334
335 if (FAILED(result))
336 {
337 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_INVALIDCALL);
338
339 ERR("Could not create depthstencil surface for new swap chain: 0x%08X", result);
340 release();
341
342 if(isDeviceLostError(result))
343 {
344 mDisplay->notifyDeviceLost();
345 return false;
346 }
347 else
348 {
349 return error(EGL_BAD_ALLOC, false);
350 }
351 }
352 }
353
apatrick@chromium.org0c71fd42012-08-10 18:08:47 +0000354 mWidth = backbufferWidth;
355 mHeight = backbufferHeight;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000356
357 mPresentIntervalDirty = false;
358 return true;
359}
360
361bool Surface::swapRect(EGLint x, EGLint y, EGLint width, EGLint height)
362{
363 if (!mSwapChain)
364 {
365 return true;
366 }
367
368 if (x + width > mWidth)
369 {
370 width = mWidth - x;
371 }
372
373 if (y + height > mHeight)
374 {
375 height = mHeight - y;
376 }
377
378 if (width == 0 || height == 0)
379 {
380 return true;
381 }
382
383 IDirect3DDevice9 *device = mDisplay->getDevice();
384
385 // Disable all pipeline operations
386 device->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
387 device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
388 device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
389 device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
390 device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
391 device->SetRenderState(D3DRS_STENCILENABLE, FALSE);
392 device->SetRenderState(D3DRS_CLIPPLANEENABLE, 0);
393 device->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALPHA | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_RED);
394 device->SetRenderState(D3DRS_SRGBWRITEENABLE, FALSE);
395 device->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
396 device->SetPixelShader(NULL);
397 device->SetVertexShader(NULL);
398
399 device->SetRenderTarget(0, mBackBuffer);
400 device->SetDepthStencilSurface(NULL);
401
402 device->SetTexture(0, mOffscreenTexture);
403 device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
404 device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
405 device->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
406 device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
407 device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
408 device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
409 device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
410 device->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1);
411
412 D3DVIEWPORT9 viewport = {0, 0, mWidth, mHeight, 0.0f, 1.0f};
413 device->SetViewport(&viewport);
414
415 float x1 = x - 0.5f;
416 float y1 = (mHeight - y - height) - 0.5f;
417 float x2 = (x + width) - 0.5f;
418 float y2 = (mHeight - y) - 0.5f;
419
420 float u1 = x / float(mWidth);
421 float v1 = y / float(mHeight);
422 float u2 = (x + width) / float(mWidth);
423 float v2 = (y + height) / float(mHeight);
424
425 float quad[4][6] = {{x1, y1, 0.0f, 1.0f, u1, v2},
426 {x2, y1, 0.0f, 1.0f, u2, v2},
427 {x2, y2, 0.0f, 1.0f, u2, v1},
428 {x1, y2, 0.0f, 1.0f, u1, v1}}; // x, y, z, rhw, u, v
429
430 mDisplay->startScene();
431 device->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, quad, 6 * sizeof(float));
432 mDisplay->endScene();
433
434 device->SetTexture(0, NULL);
435
436 RECT rect =
437 {
438 x, mHeight - y - height,
439 x + width, mHeight - y
440 };
441
442 HRESULT result = mSwapChain->Present(&rect, &rect, NULL, NULL, 0);
443
444 gl::Context *context = static_cast<gl::Context*>(glGetCurrentContext());
445 if (context)
446 {
447 context->markAllStateDirty();
448 }
449
450 if (isDeviceLostError(result))
451 {
452 mDisplay->notifyDeviceLost();
453 return false;
454 }
455
456 if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY || result == D3DERR_DRIVERINTERNALERROR)
457 {
458 return error(EGL_BAD_ALLOC, false);
459 }
460
461 ASSERT(SUCCEEDED(result));
462
463 checkForOutOfDateSwapChain();
464
465 return true;
466}
467
468HWND Surface::getWindowHandle()
469{
470 return mWindow;
471}
472
473
474#define kSurfaceProperty _TEXT("Egl::SurfaceOwner")
475#define kParentWndProc _TEXT("Egl::SurfaceParentWndProc")
476
477static LRESULT CALLBACK SurfaceWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
478{
479 if (message == WM_SIZE)
480 {
481 Surface* surf = reinterpret_cast<Surface*>(GetProp(hwnd, kSurfaceProperty));
482 if(surf)
483 {
484 surf->checkForOutOfDateSwapChain();
485 }
486 }
487 WNDPROC prevWndFunc = reinterpret_cast<WNDPROC >(GetProp(hwnd, kParentWndProc));
488 return CallWindowProc(prevWndFunc, hwnd, message, wparam, lparam);
489}
490
491void Surface::subclassWindow()
492{
493 if (!mWindow)
494 {
495 return;
496 }
497
498 DWORD processId;
499 DWORD threadId = GetWindowThreadProcessId(mWindow, &processId);
500 if (processId != GetCurrentProcessId() || threadId != GetCurrentThreadId())
501 {
502 return;
503 }
504
505 SetLastError(0);
506 LONG_PTR oldWndProc = SetWindowLongPtr(mWindow, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(SurfaceWindowProc));
507 if(oldWndProc == 0 && GetLastError() != ERROR_SUCCESS)
508 {
509 mWindowSubclassed = false;
510 return;
511 }
512
513 SetProp(mWindow, kSurfaceProperty, reinterpret_cast<HANDLE>(this));
514 SetProp(mWindow, kParentWndProc, reinterpret_cast<HANDLE>(oldWndProc));
515 mWindowSubclassed = true;
516}
517
518void Surface::unsubclassWindow()
519{
520 if(!mWindowSubclassed)
521 {
522 return;
523 }
524
525 // un-subclass
526 LONG_PTR parentWndFunc = reinterpret_cast<LONG_PTR>(GetProp(mWindow, kParentWndProc));
527
528 // Check the windowproc is still SurfaceWindowProc.
529 // If this assert fails, then it is likely the application has subclassed the
530 // hwnd as well and did not unsubclass before destroying its EGL context. The
531 // application should be modified to either subclass before initializing the
532 // EGL context, or to unsubclass before destroying the EGL context.
533 if(parentWndFunc)
534 {
535 LONG_PTR prevWndFunc = SetWindowLongPtr(mWindow, GWLP_WNDPROC, parentWndFunc);
536 ASSERT(prevWndFunc == reinterpret_cast<LONG_PTR>(SurfaceWindowProc));
537 }
538
539 RemoveProp(mWindow, kSurfaceProperty);
540 RemoveProp(mWindow, kParentWndProc);
541 mWindowSubclassed = false;
542}
543
544bool Surface::checkForOutOfDateSwapChain()
545{
546 RECT client;
547 if (!GetClientRect(getWindowHandle(), &client))
548 {
549 ASSERT(false);
550 return false;
551 }
552
553 // Grow the buffer now, if the window has grown. We need to grow now to avoid losing information.
554 int clientWidth = client.right - client.left;
555 int clientHeight = client.bottom - client.top;
556 bool sizeDirty = clientWidth != getWidth() || clientHeight != getHeight();
557
558 if (sizeDirty || mPresentIntervalDirty)
559 {
560 resetSwapChain(clientWidth, clientHeight);
561 if (static_cast<egl::Surface*>(getCurrentDrawSurface()) == this)
562 {
563 glMakeCurrent(glGetCurrentContext(), static_cast<egl::Display*>(getCurrentDisplay()), this);
564 }
565
566 return true;
567 }
568 return false;
569}
570
571DWORD Surface::convertInterval(EGLint interval)
572{
573 switch(interval)
574 {
575 case 0: return D3DPRESENT_INTERVAL_IMMEDIATE;
576 case 1: return D3DPRESENT_INTERVAL_ONE;
577 case 2: return D3DPRESENT_INTERVAL_TWO;
578 case 3: return D3DPRESENT_INTERVAL_THREE;
579 case 4: return D3DPRESENT_INTERVAL_FOUR;
580 default: UNREACHABLE();
581 }
582
583 return D3DPRESENT_INTERVAL_DEFAULT;
584}
585
586bool Surface::swap()
587{
588 return swapRect(0, 0, mWidth, mHeight);
589}
590
591bool Surface::postSubBuffer(EGLint x, EGLint y, EGLint width, EGLint height)
592{
593 if (!mPostSubBufferSupported)
594 {
595 // Spec is not clear about how this should be handled.
596 return true;
597 }
598
599 return swapRect(x, y, width, height);
600}
601
602EGLint Surface::getWidth() const
603{
604 return mWidth;
605}
606
607EGLint Surface::getHeight() const
608{
609 return mHeight;
610}
611
612EGLint Surface::isPostSubBufferSupported() const
613{
614 return mPostSubBufferSupported;
615}
616
617// Increments refcount on surface.
618// caller must Release() the returned surface
619IDirect3DSurface9 *Surface::getRenderTarget()
620{
621 if (mRenderTarget)
622 {
623 mRenderTarget->AddRef();
624 }
625
626 return mRenderTarget;
627}
628
629// Increments refcount on surface.
630// caller must Release() the returned surface
631IDirect3DSurface9 *Surface::getDepthStencil()
632{
633 if (mDepthStencil)
634 {
635 mDepthStencil->AddRef();
636 }
637
638 return mDepthStencil;
639}
640
641IDirect3DTexture9 *Surface::getOffscreenTexture()
642{
643 if (mOffscreenTexture)
644 {
645 mOffscreenTexture->AddRef();
646 }
647
648 return mOffscreenTexture;
649}
650
651void Surface::setSwapInterval(EGLint interval)
652{
653 if (mSwapInterval == interval)
654 {
655 return;
656 }
657
658 mSwapInterval = interval;
659 mSwapInterval = std::max(mSwapInterval, mDisplay->getMinSwapInterval());
660 mSwapInterval = std::min(mSwapInterval, mDisplay->getMaxSwapInterval());
661
662 mPresentInterval = convertInterval(mSwapInterval);
663 mPresentIntervalDirty = true;
664}
665
666EGLenum Surface::getTextureFormat() const
667{
668 return mTextureFormat;
669}
670
671EGLenum Surface::getTextureTarget() const
672{
673 return mTextureTarget;
674}
675
676void Surface::setBoundTexture(gl::Texture2D *texture)
677{
678 mTexture = texture;
679}
680
681gl::Texture2D *Surface::getBoundTexture() const
682{
683 return mTexture;
684}
685
686D3DFORMAT Surface::getFormat() const
687{
688 return mConfig->mRenderTargetFormat;
689}
690}