blob: 8a244cef6b0ec1bef0d511b7ed7f937c2b69ef1c [file] [log] [blame]
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001//
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002// Copyright (c) 2002-2012 The ANGLE Project Authors. All rights reserved.
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00003// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6
7// Texture.cpp: Implements the gl::Texture class and its derived classes
8// Texture2D and TextureCubeMap. Implements GL texture objects and related
9// functionality. [OpenGL ES 2.0.24] section 3.7 page 63.
10
11#include "libGLESv2/Texture.h"
12
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000013#include <algorithm>
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000014
15#include "common/debug.h"
16
17#include "libEGL/Display.h"
18
19#include "libGLESv2/main.h"
20#include "libGLESv2/mathutil.h"
21#include "libGLESv2/utilities.h"
22#include "libGLESv2/Blit.h"
23#include "libGLESv2/Framebuffer.h"
24
25namespace gl
26{
27unsigned int TextureStorage::mCurrentTextureSerial = 1;
28
daniel@transgaming.com6b1a0a02012-10-17 18:22:47 +000029static D3DFORMAT ConvertTextureInternalFormat(GLint internalformat)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000030{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +000031 switch (internalformat)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000032 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +000033 case GL_DEPTH_COMPONENT16:
34 case GL_DEPTH_COMPONENT32_OES:
35 case GL_DEPTH24_STENCIL8_OES:
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000036 return D3DFMT_INTZ;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +000037 case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
38 case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000039 return D3DFMT_DXT1;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +000040 case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000041 return D3DFMT_DXT3;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +000042 case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000043 return D3DFMT_DXT5;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +000044 case GL_RGBA32F_EXT:
45 case GL_RGB32F_EXT:
46 case GL_ALPHA32F_EXT:
47 case GL_LUMINANCE32F_EXT:
48 case GL_LUMINANCE_ALPHA32F_EXT:
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000049 return D3DFMT_A32B32G32R32F;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +000050 case GL_RGBA16F_EXT:
51 case GL_RGB16F_EXT:
52 case GL_ALPHA16F_EXT:
53 case GL_LUMINANCE16F_EXT:
54 case GL_LUMINANCE_ALPHA16F_EXT:
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000055 return D3DFMT_A16B16G16R16F;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +000056 case GL_LUMINANCE8_EXT:
57 if (getContext()->supportsLuminanceTextures())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000058 {
59 return D3DFMT_L8;
60 }
daniel@transgaming.com6452adf2012-10-17 18:22:35 +000061 break;
62 case GL_LUMINANCE8_ALPHA8_EXT:
63 if (getContext()->supportsLuminanceAlphaTextures())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000064 {
65 return D3DFMT_A8L8;
66 }
daniel@transgaming.com6452adf2012-10-17 18:22:35 +000067 break;
68 case GL_RGB8_OES:
daniel@transgaming.com51278032012-10-17 18:22:51 +000069 case GL_RGB565:
daniel@transgaming.com6452adf2012-10-17 18:22:35 +000070 return D3DFMT_X8R8G8B8;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000071 }
72
73 return D3DFMT_A8R8G8B8;
74}
75
76static bool IsTextureFormatRenderable(D3DFORMAT format)
77{
78 if (format == D3DFMT_INTZ)
79 {
80 return true;
81 }
82 switch(format)
83 {
84 case D3DFMT_L8:
85 case D3DFMT_A8L8:
86 case D3DFMT_DXT1:
87 case D3DFMT_DXT3:
88 case D3DFMT_DXT5:
89 return false;
90 case D3DFMT_A8R8G8B8:
91 case D3DFMT_X8R8G8B8:
92 case D3DFMT_A16B16G16R16F:
93 case D3DFMT_A32B32G32R32F:
94 return true;
95 default:
96 UNREACHABLE();
97 }
98
99 return false;
100}
101
102static inline DWORD GetTextureUsage(D3DFORMAT d3dfmt, GLenum glusage, bool forceRenderable)
103{
104 DWORD d3dusage = 0;
105
106 if (d3dfmt == D3DFMT_INTZ)
107 {
108 d3dusage |= D3DUSAGE_DEPTHSTENCIL;
109 }
110 else if(forceRenderable || (IsTextureFormatRenderable(d3dfmt) && (glusage == GL_FRAMEBUFFER_ATTACHMENT_ANGLE)))
111 {
112 d3dusage |= D3DUSAGE_RENDERTARGET;
113 }
114 return d3dusage;
115}
116
daniel@transgaming.com4bb04be2012-10-17 18:29:55 +0000117static void MakeValidSize(bool isImage, bool isCompressed, GLsizei *requestWidth, GLsizei *requestHeight, int *levelOffset)
118{
jbauman@chromium.org68715282012-07-12 23:28:41 +0000119 int upsampleCount = 0;
120
121 if (isCompressed)
122 {
123 // Don't expand the size of full textures that are at least 4x4
124 // already.
125 if (isImage || *requestWidth < 4 || *requestHeight < 4)
126 {
127 while (*requestWidth % 4 != 0 || *requestHeight % 4 != 0)
128 {
129 *requestWidth <<= 1;
130 *requestHeight <<= 1;
131 upsampleCount++;
132 }
133 }
134 }
135 *levelOffset = upsampleCount;
136}
137
daniel@transgaming.com4bb04be2012-10-17 18:29:55 +0000138static void CopyLockableSurfaces(IDirect3DSurface9 *dest, IDirect3DSurface9 *source)
139{
140 D3DLOCKED_RECT sourceLock = {0};
141 D3DLOCKED_RECT destLock = {0};
142
143 source->LockRect(&sourceLock, NULL, 0);
144 dest->LockRect(&destLock, NULL, 0);
145
146 if (sourceLock.pBits && destLock.pBits)
147 {
148 D3DSURFACE_DESC desc;
149 source->GetDesc(&desc);
150
151 int rows = dx::IsCompressedFormat(desc.Format) ? desc.Height / 4 : desc.Height;
152 int bytes = dx::ComputeRowSize(desc.Format, desc.Width);
153 ASSERT(bytes <= sourceLock.Pitch && bytes <= destLock.Pitch);
154
155 for(int i = 0; i < rows; i++)
156 {
157 memcpy((char*)destLock.pBits + destLock.Pitch * i, (char*)sourceLock.pBits + sourceLock.Pitch * i, bytes);
158 }
159
160 source->UnlockRect();
161 dest->UnlockRect();
162 }
163 else UNREACHABLE();
164}
165
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000166Image::Image()
167{
168 mWidth = 0;
169 mHeight = 0;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000170 mInternalFormat = GL_NONE;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000171
172 mSurface = NULL;
173
174 mDirty = false;
175
176 mD3DPool = D3DPOOL_SYSTEMMEM;
177 mD3DFormat = D3DFMT_UNKNOWN;
178}
179
180Image::~Image()
181{
182 if (mSurface)
183 {
184 mSurface->Release();
185 }
186}
187
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000188bool Image::redefine(GLint internalformat, GLsizei width, GLsizei height, bool forceRelease)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000189{
190 if (mWidth != width ||
191 mHeight != height ||
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000192 mInternalFormat != internalformat ||
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000193 forceRelease)
194 {
195 mWidth = width;
196 mHeight = height;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000197 mInternalFormat = internalformat;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000198 // compute the d3d format that will be used
daniel@transgaming.com6b1a0a02012-10-17 18:22:47 +0000199 mD3DFormat = ConvertTextureInternalFormat(internalformat);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000200
201 if (mSurface)
202 {
203 mSurface->Release();
204 mSurface = NULL;
205 }
206
207 return true;
208 }
209
210 return false;
211}
212
213void Image::createSurface()
214{
215 if(mSurface)
216 {
217 return;
218 }
219
220 IDirect3DTexture9 *newTexture = NULL;
221 IDirect3DSurface9 *newSurface = NULL;
222 const D3DPOOL poolToUse = D3DPOOL_SYSTEMMEM;
223 const D3DFORMAT d3dFormat = getD3DFormat();
224 ASSERT(d3dFormat != D3DFMT_INTZ); // We should never get here for depth textures
225
226 if (mWidth != 0 && mHeight != 0)
227 {
228 int levelToFetch = 0;
229 GLsizei requestWidth = mWidth;
230 GLsizei requestHeight = mHeight;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000231 MakeValidSize(true, IsCompressed(mInternalFormat), &requestWidth, &requestHeight, &levelToFetch);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000232
daniel@transgaming.com621ce052012-10-31 17:52:29 +0000233 // D3D9_REPLACE
daniel@transgaming.comf1122172012-10-31 18:07:20 +0000234 IDirect3DDevice9 *device = getDisplay()->getRenderer()->getDevice();
235 HRESULT result = device->CreateTexture(requestWidth, requestHeight, levelToFetch + 1, NULL, d3dFormat,
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000236 poolToUse, &newTexture, NULL);
237
238 if (FAILED(result))
239 {
240 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
241 ERR("Creating image surface failed.");
242 return error(GL_OUT_OF_MEMORY);
243 }
244
245 newTexture->GetSurfaceLevel(levelToFetch, &newSurface);
246 newTexture->Release();
247 }
248
249 mSurface = newSurface;
250 mDirty = false;
251 mD3DPool = poolToUse;
252}
253
254HRESULT Image::lock(D3DLOCKED_RECT *lockedRect, const RECT *rect)
255{
256 createSurface();
257
258 HRESULT result = D3DERR_INVALIDCALL;
259
260 if (mSurface)
261 {
262 result = mSurface->LockRect(lockedRect, rect, 0);
263 ASSERT(SUCCEEDED(result));
264
265 mDirty = true;
266 }
267
268 return result;
269}
270
271void Image::unlock()
272{
273 if (mSurface)
274 {
275 HRESULT result = mSurface->UnlockRect();
276 ASSERT(SUCCEEDED(result));
277 }
278}
279
280bool Image::isRenderableFormat() const
281{
282 return IsTextureFormatRenderable(getD3DFormat());
283}
284
285D3DFORMAT Image::getD3DFormat() const
286{
287 // this should only happen if the image hasn't been redefined first
288 // which would be a bug by the caller
289 ASSERT(mD3DFormat != D3DFMT_UNKNOWN);
290
291 return mD3DFormat;
292}
293
294IDirect3DSurface9 *Image::getSurface()
295{
296 createSurface();
297
298 return mSurface;
299}
300
301void Image::setManagedSurface(IDirect3DSurface9 *surface)
302{
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000303 D3DSURFACE_DESC desc;
304 surface->GetDesc(&desc);
305 ASSERT(desc.Pool == D3DPOOL_MANAGED);
306
daniel@transgaming.coma841b112012-10-17 18:30:14 +0000307 if ((GLsizei)desc.Width == mWidth && (GLsizei)desc.Height == mHeight)
308 {
309 if (mSurface)
310 {
311 CopyLockableSurfaces(surface, mSurface);
312 mSurface->Release();
313 }
314
315 mSurface = surface;
316 mD3DPool = desc.Pool;
317 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000318}
319
320void Image::updateSurface(IDirect3DSurface9 *destSurface, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
321{
322 IDirect3DSurface9 *sourceSurface = getSurface();
323
324 if (sourceSurface && sourceSurface != destSurface)
325 {
326 RECT rect;
327 rect.left = xoffset;
328 rect.top = yoffset;
329 rect.right = xoffset + width;
330 rect.bottom = yoffset + height;
331
daniel@transgaming.come0adbd82012-10-17 18:29:52 +0000332 POINT point = {rect.left, rect.top};
daniel@transgaming.com853b5772012-10-17 18:30:10 +0000333
334 if (mD3DPool == D3DPOOL_MANAGED)
335 {
336 D3DSURFACE_DESC desc;
337 sourceSurface->GetDesc(&desc);
338
339 IDirect3DSurface9 *surf = 0;
340 HRESULT result = getDevice()->CreateOffscreenPlainSurface(desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &surf, NULL);
341
342 if (SUCCEEDED(result))
343 {
344 CopyLockableSurfaces(surf, sourceSurface);
345 result = getDevice()->UpdateSurface(surf, &rect, destSurface, &point);
346 ASSERT(SUCCEEDED(result));
347 surf->Release();
348 }
349 }
350 else
351 {
352 // UpdateSurface: source must be SYSTEMMEM, dest must be DEFAULT pools
daniel@transgaming.com621ce052012-10-31 17:52:29 +0000353 // D3D9_REPLACE
daniel@transgaming.comf1122172012-10-31 18:07:20 +0000354 IDirect3DDevice9 *device = getDisplay()->getRenderer()->getDevice();
355 HRESULT result = device->UpdateSurface(sourceSurface, &rect, destSurface, &point);
daniel@transgaming.com853b5772012-10-17 18:30:10 +0000356 ASSERT(SUCCEEDED(result));
357 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000358 }
359}
360
361// Store the pixel rectangle designated by xoffset,yoffset,width,height with pixels stored as format/type at input
362// into the target pixel rectangle.
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000363void Image::loadData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000364 GLint unpackAlignment, const void *input)
365{
366 RECT lockRect =
367 {
368 xoffset, yoffset,
369 xoffset + width, yoffset + height
370 };
371
372 D3DLOCKED_RECT locked;
373 HRESULT result = lock(&locked, &lockRect);
374 if (FAILED(result))
375 {
376 return;
377 }
378
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000379
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000380 GLsizei inputPitch = ComputePitch(width, mInternalFormat, unpackAlignment);
381
382 switch (mInternalFormat)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000383 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000384 case GL_ALPHA8_EXT:
385 if (supportsSSE2())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000386 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000387 loadAlphaDataSSE2(width, height, inputPitch, input, locked.Pitch, locked.pBits);
388 }
389 else
390 {
391 loadAlphaData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000392 }
393 break;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000394 case GL_LUMINANCE8_EXT:
395 loadLuminanceData(width, height, inputPitch, input, locked.Pitch, locked.pBits, getD3DFormat() == D3DFMT_L8);
396 break;
397 case GL_ALPHA32F_EXT:
398 loadAlphaFloatData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
399 break;
400 case GL_LUMINANCE32F_EXT:
401 loadLuminanceFloatData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
402 break;
403 case GL_ALPHA16F_EXT:
404 loadAlphaHalfFloatData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
405 break;
406 case GL_LUMINANCE16F_EXT:
407 loadLuminanceHalfFloatData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
408 break;
409 case GL_LUMINANCE8_ALPHA8_EXT:
410 loadLuminanceAlphaData(width, height, inputPitch, input, locked.Pitch, locked.pBits, getD3DFormat() == D3DFMT_A8L8);
411 break;
412 case GL_LUMINANCE_ALPHA32F_EXT:
413 loadLuminanceAlphaFloatData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
414 break;
415 case GL_LUMINANCE_ALPHA16F_EXT:
416 loadLuminanceAlphaHalfFloatData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
417 break;
418 case GL_RGB8_OES:
419 loadRGBUByteData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
420 break;
421 case GL_RGB565:
422 loadRGB565Data(width, height, inputPitch, input, locked.Pitch, locked.pBits);
423 break;
424 case GL_RGBA8_OES:
425 if (supportsSSE2())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000426 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000427 loadRGBAUByteDataSSE2(width, height, inputPitch, input, locked.Pitch, locked.pBits);
428 }
429 else
430 {
431 loadRGBAUByteData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000432 }
433 break;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000434 case GL_RGBA4:
435 loadRGBA4444Data(width, height, inputPitch, input, locked.Pitch, locked.pBits);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000436 break;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000437 case GL_RGB5_A1:
438 loadRGBA5551Data(width, height, inputPitch, input, locked.Pitch, locked.pBits);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000439 break;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000440 case GL_BGRA8_EXT:
441 loadBGRAData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000442 break;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000443 // float textures are converted to RGBA, not BGRA, as they're stored that way in D3D
444 case GL_RGB32F_EXT:
445 loadRGBFloatData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000446 break;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000447 case GL_RGB16F_EXT:
448 loadRGBHalfFloatData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
449 break;
450 case GL_RGBA32F_EXT:
451 loadRGBAFloatData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
452 break;
453 case GL_RGBA16F_EXT:
454 loadRGBAHalfFloatData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
455 break;
456 default: UNREACHABLE();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000457 }
458
459 unlock();
460}
461
462void Image::loadAlphaData(GLsizei width, GLsizei height,
463 int inputPitch, const void *input, size_t outputPitch, void *output) const
464{
465 const unsigned char *source = NULL;
466 unsigned char *dest = NULL;
467
468 for (int y = 0; y < height; y++)
469 {
470 source = static_cast<const unsigned char*>(input) + y * inputPitch;
471 dest = static_cast<unsigned char*>(output) + y * outputPitch;
472 for (int x = 0; x < width; x++)
473 {
474 dest[4 * x + 0] = 0;
475 dest[4 * x + 1] = 0;
476 dest[4 * x + 2] = 0;
477 dest[4 * x + 3] = source[x];
478 }
479 }
480}
481
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000482void Image::loadAlphaFloatData(GLsizei width, GLsizei height,
483 int inputPitch, const void *input, size_t outputPitch, void *output) const
484{
485 const float *source = NULL;
486 float *dest = NULL;
487
488 for (int y = 0; y < height; y++)
489 {
490 source = reinterpret_cast<const float*>(static_cast<const unsigned char*>(input) + y * inputPitch);
491 dest = reinterpret_cast<float*>(static_cast<unsigned char*>(output) + y * outputPitch);
492 for (int x = 0; x < width; x++)
493 {
494 dest[4 * x + 0] = 0;
495 dest[4 * x + 1] = 0;
496 dest[4 * x + 2] = 0;
497 dest[4 * x + 3] = source[x];
498 }
499 }
500}
501
502void Image::loadAlphaHalfFloatData(GLsizei width, GLsizei height,
503 int inputPitch, const void *input, size_t outputPitch, void *output) const
504{
505 const unsigned short *source = NULL;
506 unsigned short *dest = NULL;
507
508 for (int y = 0; y < height; y++)
509 {
510 source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
511 dest = reinterpret_cast<unsigned short*>(static_cast<unsigned char*>(output) + y * outputPitch);
512 for (int x = 0; x < width; x++)
513 {
514 dest[4 * x + 0] = 0;
515 dest[4 * x + 1] = 0;
516 dest[4 * x + 2] = 0;
517 dest[4 * x + 3] = source[x];
518 }
519 }
520}
521
522void Image::loadLuminanceData(GLsizei width, GLsizei height,
523 int inputPitch, const void *input, size_t outputPitch, void *output, bool native) const
524{
525 const unsigned char *source = NULL;
526 unsigned char *dest = NULL;
527
528 for (int y = 0; y < height; y++)
529 {
530 source = static_cast<const unsigned char*>(input) + y * inputPitch;
531 dest = static_cast<unsigned char*>(output) + y * outputPitch;
532
533 if (!native) // BGRA8 destination format
534 {
535 for (int x = 0; x < width; x++)
536 {
537 dest[4 * x + 0] = source[x];
538 dest[4 * x + 1] = source[x];
539 dest[4 * x + 2] = source[x];
540 dest[4 * x + 3] = 0xFF;
541 }
542 }
543 else // L8 destination format
544 {
545 memcpy(dest, source, width);
546 }
547 }
548}
549
550void Image::loadLuminanceFloatData(GLsizei width, GLsizei height,
551 int inputPitch, const void *input, size_t outputPitch, void *output) const
552{
553 const float *source = NULL;
554 float *dest = NULL;
555
556 for (int y = 0; y < height; y++)
557 {
558 source = reinterpret_cast<const float*>(static_cast<const unsigned char*>(input) + y * inputPitch);
559 dest = reinterpret_cast<float*>(static_cast<unsigned char*>(output) + y * outputPitch);
560 for (int x = 0; x < width; x++)
561 {
562 dest[4 * x + 0] = source[x];
563 dest[4 * x + 1] = source[x];
564 dest[4 * x + 2] = source[x];
565 dest[4 * x + 3] = 1.0f;
566 }
567 }
568}
569
570void Image::loadLuminanceHalfFloatData(GLsizei width, GLsizei height,
571 int inputPitch, const void *input, size_t outputPitch, void *output) const
572{
573 const unsigned short *source = NULL;
574 unsigned short *dest = NULL;
575
576 for (int y = 0; y < height; y++)
577 {
578 source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
579 dest = reinterpret_cast<unsigned short*>(static_cast<unsigned char*>(output) + y * outputPitch);
580 for (int x = 0; x < width; x++)
581 {
582 dest[4 * x + 0] = source[x];
583 dest[4 * x + 1] = source[x];
584 dest[4 * x + 2] = source[x];
585 dest[4 * x + 3] = 0x3C00; // SEEEEEMMMMMMMMMM, S = 0, E = 15, M = 0: 16bit flpt representation of 1
586 }
587 }
588}
589
590void Image::loadLuminanceAlphaData(GLsizei width, GLsizei height,
591 int inputPitch, const void *input, size_t outputPitch, void *output, bool native) const
592{
593 const unsigned char *source = NULL;
594 unsigned char *dest = NULL;
595
596 for (int y = 0; y < height; y++)
597 {
598 source = static_cast<const unsigned char*>(input) + y * inputPitch;
599 dest = static_cast<unsigned char*>(output) + y * outputPitch;
600
601 if (!native) // BGRA8 destination format
602 {
603 for (int x = 0; x < width; x++)
604 {
605 dest[4 * x + 0] = source[2*x+0];
606 dest[4 * x + 1] = source[2*x+0];
607 dest[4 * x + 2] = source[2*x+0];
608 dest[4 * x + 3] = source[2*x+1];
609 }
610 }
611 else
612 {
613 memcpy(dest, source, width * 2);
614 }
615 }
616}
617
618void Image::loadLuminanceAlphaFloatData(GLsizei width, GLsizei height,
619 int inputPitch, const void *input, size_t outputPitch, void *output) const
620{
621 const float *source = NULL;
622 float *dest = NULL;
623
624 for (int y = 0; y < height; y++)
625 {
626 source = reinterpret_cast<const float*>(static_cast<const unsigned char*>(input) + y * inputPitch);
627 dest = reinterpret_cast<float*>(static_cast<unsigned char*>(output) + y * outputPitch);
628 for (int x = 0; x < width; x++)
629 {
630 dest[4 * x + 0] = source[2*x+0];
631 dest[4 * x + 1] = source[2*x+0];
632 dest[4 * x + 2] = source[2*x+0];
633 dest[4 * x + 3] = source[2*x+1];
634 }
635 }
636}
637
638void Image::loadLuminanceAlphaHalfFloatData(GLsizei width, GLsizei height,
639 int inputPitch, const void *input, size_t outputPitch, void *output) const
640{
641 const unsigned short *source = NULL;
642 unsigned short *dest = NULL;
643
644 for (int y = 0; y < height; y++)
645 {
646 source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
647 dest = reinterpret_cast<unsigned short*>(static_cast<unsigned char*>(output) + y * outputPitch);
648 for (int x = 0; x < width; x++)
649 {
650 dest[4 * x + 0] = source[2*x+0];
651 dest[4 * x + 1] = source[2*x+0];
652 dest[4 * x + 2] = source[2*x+0];
653 dest[4 * x + 3] = source[2*x+1];
654 }
655 }
656}
657
658void Image::loadRGBUByteData(GLsizei width, GLsizei height,
659 int inputPitch, const void *input, size_t outputPitch, void *output) const
660{
661 const unsigned char *source = NULL;
662 unsigned char *dest = NULL;
663
664 for (int y = 0; y < height; y++)
665 {
666 source = static_cast<const unsigned char*>(input) + y * inputPitch;
667 dest = static_cast<unsigned char*>(output) + y * outputPitch;
668 for (int x = 0; x < width; x++)
669 {
670 dest[4 * x + 0] = source[x * 3 + 2];
671 dest[4 * x + 1] = source[x * 3 + 1];
672 dest[4 * x + 2] = source[x * 3 + 0];
673 dest[4 * x + 3] = 0xFF;
674 }
675 }
676}
677
678void Image::loadRGB565Data(GLsizei width, GLsizei height,
679 int inputPitch, const void *input, size_t outputPitch, void *output) const
680{
681 const unsigned short *source = NULL;
682 unsigned char *dest = NULL;
683
684 for (int y = 0; y < height; y++)
685 {
686 source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
687 dest = static_cast<unsigned char*>(output) + y * outputPitch;
688 for (int x = 0; x < width; x++)
689 {
690 unsigned short rgba = source[x];
691 dest[4 * x + 0] = ((rgba & 0x001F) << 3) | ((rgba & 0x001F) >> 2);
692 dest[4 * x + 1] = ((rgba & 0x07E0) >> 3) | ((rgba & 0x07E0) >> 9);
693 dest[4 * x + 2] = ((rgba & 0xF800) >> 8) | ((rgba & 0xF800) >> 13);
694 dest[4 * x + 3] = 0xFF;
695 }
696 }
697}
698
699void Image::loadRGBFloatData(GLsizei width, GLsizei height,
700 int inputPitch, const void *input, size_t outputPitch, void *output) const
701{
702 const float *source = NULL;
703 float *dest = NULL;
704
705 for (int y = 0; y < height; y++)
706 {
707 source = reinterpret_cast<const float*>(static_cast<const unsigned char*>(input) + y * inputPitch);
708 dest = reinterpret_cast<float*>(static_cast<unsigned char*>(output) + y * outputPitch);
709 for (int x = 0; x < width; x++)
710 {
711 dest[4 * x + 0] = source[x * 3 + 0];
712 dest[4 * x + 1] = source[x * 3 + 1];
713 dest[4 * x + 2] = source[x * 3 + 2];
714 dest[4 * x + 3] = 1.0f;
715 }
716 }
717}
718
719void Image::loadRGBHalfFloatData(GLsizei width, GLsizei height,
720 int inputPitch, const void *input, size_t outputPitch, void *output) const
721{
722 const unsigned short *source = NULL;
723 unsigned short *dest = NULL;
724
725 for (int y = 0; y < height; y++)
726 {
727 source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
728 dest = reinterpret_cast<unsigned short*>(static_cast<unsigned char*>(output) + y * outputPitch);
729 for (int x = 0; x < width; x++)
730 {
731 dest[4 * x + 0] = source[x * 3 + 0];
732 dest[4 * x + 1] = source[x * 3 + 1];
733 dest[4 * x + 2] = source[x * 3 + 2];
734 dest[4 * x + 3] = 0x3C00; // SEEEEEMMMMMMMMMM, S = 0, E = 15, M = 0: 16bit flpt representation of 1
735 }
736 }
737}
738
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000739void Image::loadRGBAUByteData(GLsizei width, GLsizei height,
740 int inputPitch, const void *input, size_t outputPitch, void *output) const
741{
742 const unsigned int *source = NULL;
743 unsigned int *dest = NULL;
744 for (int y = 0; y < height; y++)
745 {
746 source = reinterpret_cast<const unsigned int*>(static_cast<const unsigned char*>(input) + y * inputPitch);
747 dest = reinterpret_cast<unsigned int*>(static_cast<unsigned char*>(output) + y * outputPitch);
748
749 for (int x = 0; x < width; x++)
750 {
751 unsigned int rgba = source[x];
752 dest[x] = (_rotl(rgba, 16) & 0x00ff00ff) | (rgba & 0xff00ff00);
753 }
754 }
755}
756
757void Image::loadRGBA4444Data(GLsizei width, GLsizei height,
758 int inputPitch, const void *input, size_t outputPitch, void *output) const
759{
760 const unsigned short *source = NULL;
761 unsigned char *dest = NULL;
762
763 for (int y = 0; y < height; y++)
764 {
765 source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
766 dest = static_cast<unsigned char*>(output) + y * outputPitch;
767 for (int x = 0; x < width; x++)
768 {
769 unsigned short rgba = source[x];
770 dest[4 * x + 0] = ((rgba & 0x00F0) << 0) | ((rgba & 0x00F0) >> 4);
771 dest[4 * x + 1] = ((rgba & 0x0F00) >> 4) | ((rgba & 0x0F00) >> 8);
772 dest[4 * x + 2] = ((rgba & 0xF000) >> 8) | ((rgba & 0xF000) >> 12);
773 dest[4 * x + 3] = ((rgba & 0x000F) << 4) | ((rgba & 0x000F) >> 0);
774 }
775 }
776}
777
778void Image::loadRGBA5551Data(GLsizei width, GLsizei height,
779 int inputPitch, const void *input, size_t outputPitch, void *output) const
780{
781 const unsigned short *source = NULL;
782 unsigned char *dest = NULL;
783
784 for (int y = 0; y < height; y++)
785 {
786 source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
787 dest = static_cast<unsigned char*>(output) + y * outputPitch;
788 for (int x = 0; x < width; x++)
789 {
790 unsigned short rgba = source[x];
791 dest[4 * x + 0] = ((rgba & 0x003E) << 2) | ((rgba & 0x003E) >> 3);
792 dest[4 * x + 1] = ((rgba & 0x07C0) >> 3) | ((rgba & 0x07C0) >> 8);
793 dest[4 * x + 2] = ((rgba & 0xF800) >> 8) | ((rgba & 0xF800) >> 13);
794 dest[4 * x + 3] = (rgba & 0x0001) ? 0xFF : 0;
795 }
796 }
797}
798
799void Image::loadRGBAFloatData(GLsizei width, GLsizei height,
800 int inputPitch, const void *input, size_t outputPitch, void *output) const
801{
802 const float *source = NULL;
803 float *dest = NULL;
804
805 for (int y = 0; y < height; y++)
806 {
807 source = reinterpret_cast<const float*>(static_cast<const unsigned char*>(input) + y * inputPitch);
808 dest = reinterpret_cast<float*>(static_cast<unsigned char*>(output) + y * outputPitch);
809 memcpy(dest, source, width * 16);
810 }
811}
812
813void Image::loadRGBAHalfFloatData(GLsizei width, GLsizei height,
814 int inputPitch, const void *input, size_t outputPitch, void *output) const
815{
816 const unsigned char *source = NULL;
817 unsigned char *dest = NULL;
818
819 for (int y = 0; y < height; y++)
820 {
821 source = static_cast<const unsigned char*>(input) + y * inputPitch;
822 dest = static_cast<unsigned char*>(output) + y * outputPitch;
823 memcpy(dest, source, width * 8);
824 }
825}
826
827void Image::loadBGRAData(GLsizei width, GLsizei height,
828 int inputPitch, const void *input, size_t outputPitch, void *output) const
829{
830 const unsigned char *source = NULL;
831 unsigned char *dest = NULL;
832
833 for (int y = 0; y < height; y++)
834 {
835 source = static_cast<const unsigned char*>(input) + y * inputPitch;
836 dest = static_cast<unsigned char*>(output) + y * outputPitch;
837 memcpy(dest, source, width*4);
838 }
839}
840
841void Image::loadCompressedData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
842 const void *input) {
843 ASSERT(xoffset % 4 == 0);
844 ASSERT(yoffset % 4 == 0);
845
846 RECT lockRect = {
847 xoffset, yoffset,
848 xoffset + width, yoffset + height
849 };
850
851 D3DLOCKED_RECT locked;
852 HRESULT result = lock(&locked, &lockRect);
853 if (FAILED(result))
854 {
855 return;
856 }
857
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000858 GLsizei inputSize = ComputeCompressedSize(width, height, mInternalFormat);
859 GLsizei inputPitch = ComputeCompressedPitch(width, mInternalFormat);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000860 int rows = inputSize / inputPitch;
861 for (int i = 0; i < rows; ++i)
862 {
863 memcpy((void*)((BYTE*)locked.pBits + i * locked.Pitch), (void*)((BYTE*)input + i * inputPitch), inputPitch);
864 }
865
866 unlock();
867}
868
869// This implements glCopyTex[Sub]Image2D for non-renderable internal texture formats and incomplete textures
870void Image::copy(GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, IDirect3DSurface9 *renderTarget)
871{
daniel@transgaming.comf1122172012-10-31 18:07:20 +0000872 IDirect3DDevice9 *device = getDisplay()->getRenderer()->getDevice(); // D3D9_REPLACE
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000873 IDirect3DSurface9 *renderTargetData = NULL;
874 D3DSURFACE_DESC description;
875 renderTarget->GetDesc(&description);
876
877 HRESULT result = device->CreateOffscreenPlainSurface(description.Width, description.Height, description.Format, D3DPOOL_SYSTEMMEM, &renderTargetData, NULL);
878
879 if (FAILED(result))
880 {
881 ERR("Could not create matching destination surface.");
882 return error(GL_OUT_OF_MEMORY);
883 }
884
885 result = device->GetRenderTargetData(renderTarget, renderTargetData);
886
887 if (FAILED(result))
888 {
889 ERR("GetRenderTargetData unexpectedly failed.");
890 renderTargetData->Release();
891 return error(GL_OUT_OF_MEMORY);
892 }
893
894 RECT sourceRect = {x, y, x + width, y + height};
895 RECT destRect = {xoffset, yoffset, xoffset + width, yoffset + height};
896
daniel@transgaming.com8ea69422012-10-17 18:30:03 +0000897 D3DLOCKED_RECT sourceLock = {0};
898 result = renderTargetData->LockRect(&sourceLock, &sourceRect, 0);
899
900 if (FAILED(result))
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000901 {
daniel@transgaming.com8ea69422012-10-17 18:30:03 +0000902 ERR("Failed to lock the source surface (rectangle might be invalid).");
903 renderTargetData->Release();
904 return error(GL_OUT_OF_MEMORY);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000905 }
daniel@transgaming.com8ea69422012-10-17 18:30:03 +0000906
907 D3DLOCKED_RECT destLock = {0};
908 result = lock(&destLock, &destRect);
909
910 if (FAILED(result))
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000911 {
daniel@transgaming.com8ea69422012-10-17 18:30:03 +0000912 ERR("Failed to lock the destination surface (rectangle might be invalid).");
913 renderTargetData->UnlockRect();
914 renderTargetData->Release();
915 return error(GL_OUT_OF_MEMORY);
916 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000917
daniel@transgaming.com8ea69422012-10-17 18:30:03 +0000918 if (destLock.pBits && sourceLock.pBits)
919 {
920 unsigned char *source = (unsigned char*)sourceLock.pBits;
921 unsigned char *dest = (unsigned char*)destLock.pBits;
922
923 switch (description.Format)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000924 {
daniel@transgaming.com8ea69422012-10-17 18:30:03 +0000925 case D3DFMT_X8R8G8B8:
926 case D3DFMT_A8R8G8B8:
927 switch(getD3DFormat())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000928 {
929 case D3DFMT_X8R8G8B8:
930 case D3DFMT_A8R8G8B8:
daniel@transgaming.com8ea69422012-10-17 18:30:03 +0000931 for(int y = 0; y < height; y++)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000932 {
daniel@transgaming.com8ea69422012-10-17 18:30:03 +0000933 memcpy(dest, source, 4 * width);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000934
daniel@transgaming.com8ea69422012-10-17 18:30:03 +0000935 source += sourceLock.Pitch;
936 dest += destLock.Pitch;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000937 }
938 break;
daniel@transgaming.com8ea69422012-10-17 18:30:03 +0000939 case D3DFMT_L8:
940 for(int y = 0; y < height; y++)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000941 {
daniel@transgaming.com8ea69422012-10-17 18:30:03 +0000942 for(int x = 0; x < width; x++)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000943 {
daniel@transgaming.com8ea69422012-10-17 18:30:03 +0000944 dest[x] = source[x * 4 + 2];
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000945 }
daniel@transgaming.com8ea69422012-10-17 18:30:03 +0000946
947 source += sourceLock.Pitch;
948 dest += destLock.Pitch;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000949 }
950 break;
daniel@transgaming.com8ea69422012-10-17 18:30:03 +0000951 case D3DFMT_A8L8:
952 for(int y = 0; y < height; y++)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000953 {
daniel@transgaming.com8ea69422012-10-17 18:30:03 +0000954 for(int x = 0; x < width; x++)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000955 {
daniel@transgaming.com8ea69422012-10-17 18:30:03 +0000956 dest[x * 2 + 0] = source[x * 4 + 2];
957 dest[x * 2 + 1] = source[x * 4 + 3];
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000958 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000959
daniel@transgaming.com8ea69422012-10-17 18:30:03 +0000960 source += sourceLock.Pitch;
961 dest += destLock.Pitch;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000962 }
963 break;
964 default:
965 UNREACHABLE();
966 }
daniel@transgaming.com8ea69422012-10-17 18:30:03 +0000967 break;
968 case D3DFMT_R5G6B5:
969 switch(getD3DFormat())
970 {
971 case D3DFMT_X8R8G8B8:
972 for(int y = 0; y < height; y++)
973 {
974 for(int x = 0; x < width; x++)
975 {
976 unsigned short rgb = ((unsigned short*)source)[x];
977 unsigned char red = (rgb & 0xF800) >> 8;
978 unsigned char green = (rgb & 0x07E0) >> 3;
979 unsigned char blue = (rgb & 0x001F) << 3;
980 dest[x + 0] = blue | (blue >> 5);
981 dest[x + 1] = green | (green >> 6);
982 dest[x + 2] = red | (red >> 5);
983 dest[x + 3] = 0xFF;
984 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000985
daniel@transgaming.com8ea69422012-10-17 18:30:03 +0000986 source += sourceLock.Pitch;
987 dest += destLock.Pitch;
988 }
989 break;
990 case D3DFMT_L8:
991 for(int y = 0; y < height; y++)
992 {
993 for(int x = 0; x < width; x++)
994 {
995 unsigned char red = source[x * 2 + 1] & 0xF8;
996 dest[x] = red | (red >> 5);
997 }
998
999 source += sourceLock.Pitch;
1000 dest += destLock.Pitch;
1001 }
1002 break;
1003 default:
1004 UNREACHABLE();
1005 }
1006 break;
1007 case D3DFMT_A1R5G5B5:
1008 switch(getD3DFormat())
1009 {
1010 case D3DFMT_X8R8G8B8:
1011 for(int y = 0; y < height; y++)
1012 {
1013 for(int x = 0; x < width; x++)
1014 {
1015 unsigned short argb = ((unsigned short*)source)[x];
1016 unsigned char red = (argb & 0x7C00) >> 7;
1017 unsigned char green = (argb & 0x03E0) >> 2;
1018 unsigned char blue = (argb & 0x001F) << 3;
1019 dest[x + 0] = blue | (blue >> 5);
1020 dest[x + 1] = green | (green >> 5);
1021 dest[x + 2] = red | (red >> 5);
1022 dest[x + 3] = 0xFF;
1023 }
1024
1025 source += sourceLock.Pitch;
1026 dest += destLock.Pitch;
1027 }
1028 break;
1029 case D3DFMT_A8R8G8B8:
1030 for(int y = 0; y < height; y++)
1031 {
1032 for(int x = 0; x < width; x++)
1033 {
1034 unsigned short argb = ((unsigned short*)source)[x];
1035 unsigned char red = (argb & 0x7C00) >> 7;
1036 unsigned char green = (argb & 0x03E0) >> 2;
1037 unsigned char blue = (argb & 0x001F) << 3;
1038 unsigned char alpha = (signed short)argb >> 15;
1039 dest[x + 0] = blue | (blue >> 5);
1040 dest[x + 1] = green | (green >> 5);
1041 dest[x + 2] = red | (red >> 5);
1042 dest[x + 3] = alpha;
1043 }
1044
1045 source += sourceLock.Pitch;
1046 dest += destLock.Pitch;
1047 }
1048 break;
1049 case D3DFMT_L8:
1050 for(int y = 0; y < height; y++)
1051 {
1052 for(int x = 0; x < width; x++)
1053 {
1054 unsigned char red = source[x * 2 + 1] & 0x7C;
1055 dest[x] = (red << 1) | (red >> 4);
1056 }
1057
1058 source += sourceLock.Pitch;
1059 dest += destLock.Pitch;
1060 }
1061 break;
1062 case D3DFMT_A8L8:
1063 for(int y = 0; y < height; y++)
1064 {
1065 for(int x = 0; x < width; x++)
1066 {
1067 unsigned char red = source[x * 2 + 1] & 0x7C;
1068 dest[x * 2 + 0] = (red << 1) | (red >> 4);
1069 dest[x * 2 + 1] = (signed char)source[x * 2 + 1] >> 7;
1070 }
1071
1072 source += sourceLock.Pitch;
1073 dest += destLock.Pitch;
1074 }
1075 break;
1076 default:
1077 UNREACHABLE();
1078 }
1079 break;
1080 default:
1081 UNREACHABLE();
1082 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001083 }
1084
daniel@transgaming.com8ea69422012-10-17 18:30:03 +00001085 unlock();
1086 renderTargetData->UnlockRect();
1087
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001088 renderTargetData->Release();
1089
1090 mDirty = true;
1091}
1092
daniel@transgaming.com2e38b802012-10-17 18:30:06 +00001093namespace
1094{
1095struct L8
1096{
1097 unsigned char L;
1098
1099 static void average(L8 *dst, const L8 *src1, const L8 *src2)
1100 {
1101 dst->L = ((src1->L ^ src2->L) >> 1) + (src1->L & src2->L);
1102 }
1103};
1104
1105struct A8L8
1106{
1107 unsigned char L;
1108 unsigned char A;
1109
1110 static void average(A8L8 *dst, const A8L8 *src1, const A8L8 *src2)
1111 {
1112 *(unsigned short*)dst = (((*(unsigned short*)src1 ^ *(unsigned short*)src2) & 0xFEFE) >> 1) + (*(unsigned short*)src1 & *(unsigned short*)src2);
1113 }
1114};
1115
1116struct A8R8G8B8
1117{
1118 unsigned char B;
1119 unsigned char G;
1120 unsigned char R;
1121 unsigned char A;
1122
1123 static void average(A8R8G8B8 *dst, const A8R8G8B8 *src1, const A8R8G8B8 *src2)
1124 {
1125 *(unsigned int*)dst = (((*(unsigned int*)src1 ^ *(unsigned int*)src2) & 0xFEFEFEFE) >> 1) + (*(unsigned int*)src1 & *(unsigned int*)src2);
1126 }
1127};
1128
1129struct A16B16G16R16F
1130{
1131 unsigned short R;
1132 unsigned short G;
1133 unsigned short B;
1134 unsigned short A;
1135
1136 static void average(A16B16G16R16F *dst, const A16B16G16R16F *src1, const A16B16G16R16F *src2)
1137 {
1138 dst->R = float32ToFloat16((float16ToFloat32(src1->R) + float16ToFloat32(src2->R)) * 0.5f);
1139 dst->G = float32ToFloat16((float16ToFloat32(src1->G) + float16ToFloat32(src2->G)) * 0.5f);
1140 dst->B = float32ToFloat16((float16ToFloat32(src1->B) + float16ToFloat32(src2->B)) * 0.5f);
1141 dst->A = float32ToFloat16((float16ToFloat32(src1->A) + float16ToFloat32(src2->A)) * 0.5f);
1142 }
1143};
1144
1145struct A32B32G32R32F
1146{
1147 float R;
1148 float G;
1149 float B;
1150 float A;
1151
1152 static void average(A32B32G32R32F *dst, const A32B32G32R32F *src1, const A32B32G32R32F *src2)
1153 {
1154 dst->R = (src1->R + src2->R) * 0.5f;
1155 dst->G = (src1->G + src2->G) * 0.5f;
1156 dst->B = (src1->B + src2->B) * 0.5f;
1157 dst->A = (src1->A + src2->A) * 0.5f;
1158 }
1159};
1160
1161template <typename T>
1162void GenerateMip(unsigned int sourceWidth, unsigned int sourceHeight,
1163 const unsigned char *sourceData, int sourcePitch,
1164 unsigned char *destData, int destPitch)
1165{
1166 unsigned int mipWidth = std::max(1U, sourceWidth >> 1);
1167 unsigned int mipHeight = std::max(1U, sourceHeight >> 1);
1168
1169 if (sourceHeight == 1)
1170 {
1171 ASSERT(sourceWidth != 1);
1172
1173 const T *src = (const T*)sourceData;
1174 T *dst = (T*)destData;
1175
1176 for (unsigned int x = 0; x < mipWidth; x++)
1177 {
1178 T::average(&dst[x], &src[x * 2], &src[x * 2 + 1]);
1179 }
1180 }
1181 else if (sourceWidth == 1)
1182 {
1183 ASSERT(sourceHeight != 1);
1184
1185 for (unsigned int y = 0; y < mipHeight; y++)
1186 {
1187 const T *src0 = (const T*)(sourceData + y * 2 * sourcePitch);
1188 const T *src1 = (const T*)(sourceData + y * 2 * sourcePitch + sourcePitch);
1189 T *dst = (T*)(destData + y * destPitch);
1190
1191 T::average(dst, src0, src1);
1192 }
1193 }
1194 else
1195 {
1196 for (unsigned int y = 0; y < mipHeight; y++)
1197 {
1198 const T *src0 = (const T*)(sourceData + y * 2 * sourcePitch);
1199 const T *src1 = (const T*)(sourceData + y * 2 * sourcePitch + sourcePitch);
1200 T *dst = (T*)(destData + y * destPitch);
1201
1202 for (unsigned int x = 0; x < mipWidth; x++)
1203 {
1204 T tmp0;
1205 T tmp1;
1206
1207 T::average(&tmp0, &src0[x * 2], &src0[x * 2 + 1]);
1208 T::average(&tmp1, &src1[x * 2], &src1[x * 2 + 1]);
1209 T::average(&dst[x], &tmp0, &tmp1);
1210 }
1211 }
1212 }
1213}
1214
1215void GenerateMip(IDirect3DSurface9 *destSurface, IDirect3DSurface9 *sourceSurface)
1216{
1217 D3DSURFACE_DESC destDesc;
1218 HRESULT result = destSurface->GetDesc(&destDesc);
1219 ASSERT(SUCCEEDED(result));
1220
1221 D3DSURFACE_DESC sourceDesc;
1222 result = sourceSurface->GetDesc(&sourceDesc);
1223 ASSERT(SUCCEEDED(result));
1224
1225 ASSERT(sourceDesc.Format == destDesc.Format);
daniel@transgaming.com2e38b802012-10-17 18:30:06 +00001226 ASSERT(sourceDesc.Width == 1 || sourceDesc.Width / 2 == destDesc.Width);
1227 ASSERT(sourceDesc.Height == 1 || sourceDesc.Height / 2 == destDesc.Height);
1228
1229 D3DLOCKED_RECT sourceLocked = {0};
1230 result = sourceSurface->LockRect(&sourceLocked, NULL, D3DLOCK_READONLY);
1231 ASSERT(SUCCEEDED(result));
1232
1233 D3DLOCKED_RECT destLocked = {0};
1234 result = destSurface->LockRect(&destLocked, NULL, 0);
1235 ASSERT(SUCCEEDED(result));
1236
1237 const unsigned char *sourceData = reinterpret_cast<const unsigned char*>(sourceLocked.pBits);
1238 unsigned char *destData = reinterpret_cast<unsigned char*>(destLocked.pBits);
1239
1240 if (sourceData && destData)
1241 {
1242 switch (sourceDesc.Format)
1243 {
1244 case D3DFMT_L8:
1245 GenerateMip<L8>(sourceDesc.Width, sourceDesc.Height, sourceData, sourceLocked.Pitch, destData, destLocked.Pitch);
1246 break;
1247 case D3DFMT_A8L8:
1248 GenerateMip<A8L8>(sourceDesc.Width, sourceDesc.Height, sourceData, sourceLocked.Pitch, destData, destLocked.Pitch);
1249 break;
1250 case D3DFMT_A8R8G8B8:
1251 case D3DFMT_X8R8G8B8:
1252 GenerateMip<A8R8G8B8>(sourceDesc.Width, sourceDesc.Height, sourceData, sourceLocked.Pitch, destData, destLocked.Pitch);
1253 break;
1254 case D3DFMT_A16B16G16R16F:
1255 GenerateMip<A16B16G16R16F>(sourceDesc.Width, sourceDesc.Height, sourceData, sourceLocked.Pitch, destData, destLocked.Pitch);
1256 break;
1257 case D3DFMT_A32B32G32R32F:
1258 GenerateMip<A32B32G32R32F>(sourceDesc.Width, sourceDesc.Height, sourceData, sourceLocked.Pitch, destData, destLocked.Pitch);
1259 break;
1260 default:
1261 UNREACHABLE();
1262 break;
1263 }
1264
1265 destSurface->UnlockRect();
1266 sourceSurface->UnlockRect();
1267 }
1268}
1269}
1270
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001271TextureStorage::TextureStorage(DWORD usage)
1272 : mD3DUsage(usage),
daniel@transgaming.com621ce052012-10-31 17:52:29 +00001273 mD3DPool(getDisplay()->getRenderer()->getTexturePool(usage)), // D3D9_REPLACE
jbauman@chromium.org68715282012-07-12 23:28:41 +00001274 mTextureSerial(issueTextureSerial()),
1275 mLodOffset(0)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001276{
1277}
1278
1279TextureStorage::~TextureStorage()
1280{
1281}
1282
1283bool TextureStorage::isRenderTarget() const
1284{
1285 return (mD3DUsage & (D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL)) != 0;
1286}
1287
1288bool TextureStorage::isManaged() const
1289{
1290 return (mD3DPool == D3DPOOL_MANAGED);
1291}
1292
1293D3DPOOL TextureStorage::getPool() const
1294{
1295 return mD3DPool;
1296}
1297
1298DWORD TextureStorage::getUsage() const
1299{
1300 return mD3DUsage;
1301}
1302
1303unsigned int TextureStorage::getTextureSerial() const
1304{
1305 return mTextureSerial;
1306}
1307
1308unsigned int TextureStorage::issueTextureSerial()
1309{
1310 return mCurrentTextureSerial++;
1311}
1312
jbauman@chromium.org68715282012-07-12 23:28:41 +00001313int TextureStorage::getLodOffset() const
1314{
1315 return mLodOffset;
1316}
1317
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001318Texture::Texture(GLuint id) : RefCountObject(id)
1319{
daniel@transgaming.comebf139f2012-10-31 18:07:32 +00001320 mSamplerState.minFilter = GL_NEAREST_MIPMAP_LINEAR;
1321 mSamplerState.magFilter = GL_LINEAR;
1322 mSamplerState.wrapS = GL_REPEAT;
1323 mSamplerState.wrapT = GL_REPEAT;
1324 mSamplerState.maxAnisotropy = 1.0f;
1325 mSamplerState.lodOffset = 0;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001326 mDirtyParameters = true;
1327 mUsage = GL_NONE;
1328
1329 mDirtyImages = true;
1330
1331 mImmutable = false;
1332}
1333
1334Texture::~Texture()
1335{
1336}
1337
1338// Returns true on successful filter state update (valid enum parameter)
1339bool Texture::setMinFilter(GLenum filter)
1340{
1341 switch (filter)
1342 {
1343 case GL_NEAREST:
1344 case GL_LINEAR:
1345 case GL_NEAREST_MIPMAP_NEAREST:
1346 case GL_LINEAR_MIPMAP_NEAREST:
1347 case GL_NEAREST_MIPMAP_LINEAR:
1348 case GL_LINEAR_MIPMAP_LINEAR:
1349 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +00001350 if (mSamplerState.minFilter != filter)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001351 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +00001352 mSamplerState.minFilter = filter;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001353 mDirtyParameters = true;
1354 }
1355 return true;
1356 }
1357 default:
1358 return false;
1359 }
1360}
1361
1362// Returns true on successful filter state update (valid enum parameter)
1363bool Texture::setMagFilter(GLenum filter)
1364{
1365 switch (filter)
1366 {
1367 case GL_NEAREST:
1368 case GL_LINEAR:
1369 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +00001370 if (mSamplerState.magFilter != filter)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001371 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +00001372 mSamplerState.magFilter = filter;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001373 mDirtyParameters = true;
1374 }
1375 return true;
1376 }
1377 default:
1378 return false;
1379 }
1380}
1381
1382// Returns true on successful wrap state update (valid enum parameter)
1383bool Texture::setWrapS(GLenum wrap)
1384{
1385 switch (wrap)
1386 {
1387 case GL_REPEAT:
1388 case GL_CLAMP_TO_EDGE:
1389 case GL_MIRRORED_REPEAT:
1390 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +00001391 if (mSamplerState.wrapS != wrap)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001392 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +00001393 mSamplerState.wrapS = wrap;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001394 mDirtyParameters = true;
1395 }
1396 return true;
1397 }
1398 default:
1399 return false;
1400 }
1401}
1402
1403// Returns true on successful wrap state update (valid enum parameter)
1404bool Texture::setWrapT(GLenum wrap)
1405{
1406 switch (wrap)
1407 {
1408 case GL_REPEAT:
1409 case GL_CLAMP_TO_EDGE:
1410 case GL_MIRRORED_REPEAT:
1411 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +00001412 if (mSamplerState.wrapT != wrap)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001413 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +00001414 mSamplerState.wrapT = wrap;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001415 mDirtyParameters = true;
1416 }
1417 return true;
1418 }
1419 default:
1420 return false;
1421 }
1422}
1423
daniel@transgaming.com07ab8412012-07-12 15:17:09 +00001424// Returns true on successful max anisotropy update (valid anisotropy value)
1425bool Texture::setMaxAnisotropy(float textureMaxAnisotropy, float contextMaxAnisotropy)
1426{
1427 textureMaxAnisotropy = std::min(textureMaxAnisotropy, contextMaxAnisotropy);
1428 if (textureMaxAnisotropy < 1.0f)
1429 {
1430 return false;
1431 }
daniel@transgaming.comebf139f2012-10-31 18:07:32 +00001432 if (mSamplerState.maxAnisotropy != textureMaxAnisotropy)
daniel@transgaming.com07ab8412012-07-12 15:17:09 +00001433 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +00001434 mSamplerState.maxAnisotropy = textureMaxAnisotropy;
daniel@transgaming.com07ab8412012-07-12 15:17:09 +00001435 mDirtyParameters = true;
1436 }
1437 return true;
1438}
1439
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001440// Returns true on successful usage state update (valid enum parameter)
1441bool Texture::setUsage(GLenum usage)
1442{
1443 switch (usage)
1444 {
1445 case GL_NONE:
1446 case GL_FRAMEBUFFER_ATTACHMENT_ANGLE:
1447 mUsage = usage;
1448 return true;
1449 default:
1450 return false;
1451 }
1452}
1453
1454GLenum Texture::getMinFilter() const
1455{
daniel@transgaming.comebf139f2012-10-31 18:07:32 +00001456 return mSamplerState.minFilter;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001457}
1458
1459GLenum Texture::getMagFilter() const
1460{
daniel@transgaming.comebf139f2012-10-31 18:07:32 +00001461 return mSamplerState.magFilter;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001462}
1463
1464GLenum Texture::getWrapS() const
1465{
daniel@transgaming.comebf139f2012-10-31 18:07:32 +00001466 return mSamplerState.wrapS;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001467}
1468
1469GLenum Texture::getWrapT() const
1470{
daniel@transgaming.comebf139f2012-10-31 18:07:32 +00001471 return mSamplerState.wrapT;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001472}
1473
daniel@transgaming.com07ab8412012-07-12 15:17:09 +00001474float Texture::getMaxAnisotropy() const
1475{
daniel@transgaming.comebf139f2012-10-31 18:07:32 +00001476 return mSamplerState.maxAnisotropy;
1477}
1478
1479int Texture::getLodOffset()
1480{
1481 TextureStorage *texture = getStorage(false);
1482 return texture ? texture->getLodOffset() : 0;
1483}
1484
1485void Texture::getSamplerState(SamplerState *sampler)
1486{
1487 *sampler = mSamplerState;
1488 sampler->lodOffset = getLodOffset();
daniel@transgaming.com07ab8412012-07-12 15:17:09 +00001489}
1490
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001491GLenum Texture::getUsage() const
1492{
1493 return mUsage;
1494}
1495
daniel@transgaming.comca9a3c82012-10-26 18:55:07 +00001496bool Texture::isMipmapFiltered() const
1497{
daniel@transgaming.comebf139f2012-10-31 18:07:32 +00001498 switch (mSamplerState.minFilter)
daniel@transgaming.comca9a3c82012-10-26 18:55:07 +00001499 {
1500 case GL_NEAREST:
1501 case GL_LINEAR:
1502 return false;
1503 case GL_NEAREST_MIPMAP_NEAREST:
1504 case GL_LINEAR_MIPMAP_NEAREST:
1505 case GL_NEAREST_MIPMAP_LINEAR:
1506 case GL_LINEAR_MIPMAP_LINEAR:
1507 return true;
1508 default: UNREACHABLE();
1509 return false;
1510 }
1511}
1512
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001513void Texture::setImage(GLint unpackAlignment, const void *pixels, Image *image)
1514{
1515 if (pixels != NULL)
1516 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001517 image->loadData(0, 0, image->getWidth(), image->getHeight(), unpackAlignment, pixels);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001518 mDirtyImages = true;
1519 }
1520}
1521
1522void Texture::setCompressedImage(GLsizei imageSize, const void *pixels, Image *image)
1523{
1524 if (pixels != NULL)
1525 {
1526 image->loadCompressedData(0, 0, image->getWidth(), image->getHeight(), pixels);
1527 mDirtyImages = true;
1528 }
1529}
1530
1531bool Texture::subImage(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, Image *image)
1532{
1533 if (pixels != NULL)
1534 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001535 image->loadData(xoffset, yoffset, width, height, unpackAlignment, pixels);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001536 mDirtyImages = true;
1537 }
1538
1539 return true;
1540}
1541
1542bool Texture::subImageCompressed(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels, Image *image)
1543{
1544 if (pixels != NULL)
1545 {
1546 image->loadCompressedData(xoffset, yoffset, width, height, pixels);
1547 mDirtyImages = true;
1548 }
1549
1550 return true;
1551}
1552
1553IDirect3DBaseTexture9 *Texture::getTexture()
1554{
1555 if (!isSamplerComplete())
1556 {
1557 return NULL;
1558 }
1559
1560 // ensure the underlying texture is created
1561 if (getStorage(false) == NULL)
1562 {
1563 return NULL;
1564 }
1565
1566 updateTexture();
1567
1568 return getBaseTexture();
1569}
1570
1571bool Texture::hasDirtyParameters() const
1572{
1573 return mDirtyParameters;
1574}
1575
1576bool Texture::hasDirtyImages() const
1577{
1578 return mDirtyImages;
1579}
1580
1581void Texture::resetDirty()
1582{
1583 mDirtyParameters = false;
1584 mDirtyImages = false;
1585}
1586
1587unsigned int Texture::getTextureSerial()
1588{
1589 TextureStorage *texture = getStorage(false);
1590 return texture ? texture->getTextureSerial() : 0;
1591}
1592
1593unsigned int Texture::getRenderTargetSerial(GLenum target)
1594{
1595 TextureStorage *texture = getStorage(true);
1596 return texture ? texture->getRenderTargetSerial(target) : 0;
1597}
1598
1599bool Texture::isImmutable() const
1600{
1601 return mImmutable;
1602}
1603
1604GLint Texture::creationLevels(GLsizei width, GLsizei height) const
1605{
1606 if ((isPow2(width) && isPow2(height)) || getContext()->supportsNonPower2Texture())
1607 {
1608 return 0; // Maximum number of levels
1609 }
1610 else
1611 {
1612 // OpenGL ES 2.0 without GL_OES_texture_npot does not permit NPOT mipmaps.
1613 return 1;
1614 }
1615}
1616
1617GLint Texture::creationLevels(GLsizei size) const
1618{
1619 return creationLevels(size, size);
1620}
1621
jbauman@chromium.org6bc4a142012-09-06 21:28:30 +00001622int Texture::levelCount()
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001623{
jbauman@chromium.org6bc4a142012-09-06 21:28:30 +00001624 return getBaseTexture() ? getBaseTexture()->GetLevelCount() - getLodOffset() : 0;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001625}
1626
1627Blit *Texture::getBlitter()
1628{
1629 Context *context = getContext();
1630 return context->getBlitter();
1631}
1632
1633bool Texture::copyToRenderTarget(IDirect3DSurface9 *dest, IDirect3DSurface9 *source, bool fromManaged)
1634{
1635 if (source && dest)
1636 {
daniel@transgaming.comdabf0022012-10-17 18:29:59 +00001637 HRESULT result = D3DERR_OUTOFVIDEOMEMORY;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001638
1639 if (fromManaged)
1640 {
daniel@transgaming.comdabf0022012-10-17 18:29:59 +00001641 D3DSURFACE_DESC desc;
1642 source->GetDesc(&desc);
1643
1644 IDirect3DSurface9 *surf = 0;
1645 result = getDevice()->CreateOffscreenPlainSurface(desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &surf, NULL);
1646
1647 if (SUCCEEDED(result))
1648 {
1649 CopyLockableSurfaces(surf, source);
1650 result = getDevice()->UpdateSurface(surf, NULL, dest, NULL);
1651 surf->Release();
1652 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001653 }
1654 else
1655 {
daniel@transgaming.comf1122172012-10-31 18:07:20 +00001656 renderer::Renderer *renderer = getDisplay()->getRenderer();
daniel@transgaming.com621ce052012-10-31 17:52:29 +00001657 IDirect3DDevice9 *device = renderer->getDevice(); // D3D9_REPLACE
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001658
daniel@transgaming.com621ce052012-10-31 17:52:29 +00001659 renderer->endScene();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001660 result = device->StretchRect(source, NULL, dest, NULL, D3DTEXF_NONE);
1661 }
1662
1663 if (FAILED(result))
1664 {
1665 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1666 return false;
1667 }
1668 }
1669
1670 return true;
1671}
1672
1673TextureStorage2D::TextureStorage2D(IDirect3DTexture9 *surfaceTexture) : TextureStorage(D3DUSAGE_RENDERTARGET), mRenderTargetSerial(RenderbufferStorage::issueSerial())
1674{
1675 mTexture = surfaceTexture;
1676}
1677
1678TextureStorage2D::TextureStorage2D(int levels, D3DFORMAT format, DWORD usage, int width, int height)
1679 : TextureStorage(usage), mRenderTargetSerial(RenderbufferStorage::issueSerial())
1680{
1681 mTexture = NULL;
1682 // if the width or height is not positive this should be treated as an incomplete texture
1683 // we handle that here by skipping the d3d texture creation
1684 if (width > 0 && height > 0)
1685 {
daniel@transgaming.comf1122172012-10-31 18:07:20 +00001686 IDirect3DDevice9 *device = getDisplay()->getRenderer()->getDevice(); // D3D9_REPLACE
daniel@transgaming.com4bb04be2012-10-17 18:29:55 +00001687 MakeValidSize(false, dx::IsCompressedFormat(format), &width, &height, &mLodOffset);
sminns@adobe.comce1189b2012-09-18 20:06:35 +00001688 HRESULT result = device->CreateTexture(width, height, levels ? levels + mLodOffset : 0, getUsage(), format, getPool(), &mTexture, NULL);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001689
1690 if (FAILED(result))
1691 {
1692 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1693 error(GL_OUT_OF_MEMORY);
1694 }
1695 }
1696}
1697
1698TextureStorage2D::~TextureStorage2D()
1699{
1700 if (mTexture)
1701 {
1702 mTexture->Release();
1703 }
1704}
1705
1706// Increments refcount on surface.
1707// caller must Release() the returned surface
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00001708IDirect3DSurface9 *TextureStorage2D::getSurfaceLevel(int level, bool dirty)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001709{
1710 IDirect3DSurface9 *surface = NULL;
1711
1712 if (mTexture)
1713 {
jbauman@chromium.org68715282012-07-12 23:28:41 +00001714 HRESULT result = mTexture->GetSurfaceLevel(level + mLodOffset, &surface);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001715 ASSERT(SUCCEEDED(result));
daniel@transgaming.com1ee986b2012-09-27 17:46:12 +00001716
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00001717 // With managed textures the driver needs to be informed of updates to the lower mipmap levels
1718 if (level != 0 && isManaged() && dirty)
daniel@transgaming.com1ee986b2012-09-27 17:46:12 +00001719 {
1720 mTexture->AddDirtyRect(NULL);
1721 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001722 }
1723
1724 return surface;
1725}
1726
1727IDirect3DBaseTexture9 *TextureStorage2D::getBaseTexture() const
1728{
1729 return mTexture;
1730}
1731
1732unsigned int TextureStorage2D::getRenderTargetSerial(GLenum target) const
1733{
1734 return mRenderTargetSerial;
1735}
1736
1737Texture2D::Texture2D(GLuint id) : Texture(id)
1738{
1739 mTexStorage = NULL;
1740 mSurface = NULL;
1741 mColorbufferProxy = NULL;
1742 mProxyRefs = 0;
1743}
1744
1745Texture2D::~Texture2D()
1746{
1747 mColorbufferProxy = NULL;
1748
1749 delete mTexStorage;
1750 mTexStorage = NULL;
1751
1752 if (mSurface)
1753 {
1754 mSurface->setBoundTexture(NULL);
1755 mSurface = NULL;
1756 }
1757}
1758
1759// We need to maintain a count of references to renderbuffers acting as
1760// proxies for this texture, so that we do not attempt to use a pointer
1761// to a renderbuffer proxy which has been deleted.
1762void Texture2D::addProxyRef(const Renderbuffer *proxy)
1763{
1764 mProxyRefs++;
1765}
1766
1767void Texture2D::releaseProxy(const Renderbuffer *proxy)
1768{
1769 if (mProxyRefs > 0)
1770 mProxyRefs--;
1771
1772 if (mProxyRefs == 0)
1773 mColorbufferProxy = NULL;
1774}
1775
1776GLenum Texture2D::getTarget() const
1777{
1778 return GL_TEXTURE_2D;
1779}
1780
1781GLsizei Texture2D::getWidth(GLint level) const
1782{
1783 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
1784 return mImageArray[level].getWidth();
1785 else
1786 return 0;
1787}
1788
1789GLsizei Texture2D::getHeight(GLint level) const
1790{
1791 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
1792 return mImageArray[level].getHeight();
1793 else
1794 return 0;
1795}
1796
1797GLenum Texture2D::getInternalFormat(GLint level) const
1798{
1799 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001800 return mImageArray[level].getInternalFormat();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001801 else
1802 return GL_NONE;
1803}
1804
1805D3DFORMAT Texture2D::getD3DFormat(GLint level) const
1806{
1807 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
1808 return mImageArray[level].getD3DFormat();
1809 else
1810 return D3DFMT_UNKNOWN;
1811}
1812
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001813void Texture2D::redefineImage(GLint level, GLint internalformat, GLsizei width, GLsizei height)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001814{
1815 releaseTexImage();
1816
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001817 bool redefined = mImageArray[level].redefine(internalformat, width, height, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001818
1819 if (mTexStorage && redefined)
1820 {
1821 for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
1822 {
1823 mImageArray[i].markDirty();
1824 }
1825
1826 delete mTexStorage;
1827 mTexStorage = NULL;
1828 mDirtyImages = true;
1829 }
1830}
1831
1832void Texture2D::setImage(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1833{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001834 GLint internalformat = ConvertSizedInternalFormat(format, type);
1835 redefineImage(level, internalformat, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001836
1837 Texture::setImage(unpackAlignment, pixels, &mImageArray[level]);
1838}
1839
1840void Texture2D::bindTexImage(egl::Surface *surface)
1841{
1842 releaseTexImage();
1843
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001844 GLint internalformat;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001845
1846 switch(surface->getFormat())
1847 {
1848 case D3DFMT_A8R8G8B8:
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001849 internalformat = GL_RGBA8_OES;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001850 break;
1851 case D3DFMT_X8R8G8B8:
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001852 internalformat = GL_RGB8_OES;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001853 break;
1854 default:
1855 UNIMPLEMENTED();
1856 return;
1857 }
1858
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001859 mImageArray[0].redefine(internalformat, surface->getWidth(), surface->getHeight(), true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001860
1861 delete mTexStorage;
1862 mTexStorage = new TextureStorage2D(surface->getOffscreenTexture());
1863
1864 mDirtyImages = true;
1865 mSurface = surface;
1866 mSurface->setBoundTexture(this);
1867}
1868
1869void Texture2D::releaseTexImage()
1870{
1871 if (mSurface)
1872 {
1873 mSurface->setBoundTexture(NULL);
1874 mSurface = NULL;
1875
1876 if (mTexStorage)
1877 {
1878 delete mTexStorage;
1879 mTexStorage = NULL;
1880 }
1881
1882 for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
1883 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001884 mImageArray[i].redefine(GL_RGBA8_OES, 0, 0, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001885 }
1886 }
1887}
1888
1889void Texture2D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
1890{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001891 // compressed formats don't have separate sized internal formats-- we can just use the compressed format directly
1892 redefineImage(level, format, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001893
1894 Texture::setCompressedImage(imageSize, pixels, &mImageArray[level]);
1895}
1896
1897void Texture2D::commitRect(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
1898{
1899 ASSERT(mImageArray[level].getSurface() != NULL);
1900
1901 if (level < levelCount())
1902 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00001903 IDirect3DSurface9 *destLevel = mTexStorage->getSurfaceLevel(level, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001904
1905 if (destLevel)
1906 {
1907 Image *image = &mImageArray[level];
1908 image->updateSurface(destLevel, xoffset, yoffset, width, height);
1909
1910 destLevel->Release();
1911 image->markClean();
1912 }
1913 }
1914}
1915
1916void Texture2D::subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1917{
1918 if (Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[level]))
1919 {
1920 commitRect(level, xoffset, yoffset, width, height);
1921 }
1922}
1923
1924void Texture2D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
1925{
1926 if (Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, &mImageArray[level]))
1927 {
1928 commitRect(level, xoffset, yoffset, width, height);
1929 }
1930}
1931
1932void Texture2D::copyImage(GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
1933{
1934 IDirect3DSurface9 *renderTarget = source->getRenderTarget();
1935
1936 if (!renderTarget)
1937 {
1938 ERR("Failed to retrieve the render target.");
1939 return error(GL_OUT_OF_MEMORY);
1940 }
1941
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001942 GLint internalformat = ConvertSizedInternalFormat(format, GL_UNSIGNED_BYTE);
1943 redefineImage(level, internalformat, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001944
1945 if (!mImageArray[level].isRenderableFormat())
1946 {
1947 mImageArray[level].copy(0, 0, x, y, width, height, renderTarget);
1948 mDirtyImages = true;
1949 }
1950 else
1951 {
1952 if (!mTexStorage || !mTexStorage->isRenderTarget())
1953 {
1954 convertToRenderTarget();
1955 }
1956
1957 mImageArray[level].markClean();
1958
1959 if (width != 0 && height != 0 && level < levelCount())
1960 {
1961 RECT sourceRect;
1962 sourceRect.left = x;
1963 sourceRect.right = x + width;
1964 sourceRect.top = y;
1965 sourceRect.bottom = y + height;
1966
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00001967 IDirect3DSurface9 *dest = mTexStorage->getSurfaceLevel(level, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001968
1969 if (dest)
1970 {
1971 getBlitter()->copy(renderTarget, sourceRect, format, 0, 0, dest);
1972 dest->Release();
1973 }
1974 }
1975 }
1976
1977 renderTarget->Release();
1978}
1979
1980void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
1981{
1982 if (xoffset + width > mImageArray[level].getWidth() || yoffset + height > mImageArray[level].getHeight())
1983 {
1984 return error(GL_INVALID_VALUE);
1985 }
1986
1987 IDirect3DSurface9 *renderTarget = source->getRenderTarget();
1988
1989 if (!renderTarget)
1990 {
1991 ERR("Failed to retrieve the render target.");
1992 return error(GL_OUT_OF_MEMORY);
1993 }
1994
1995 if (!mImageArray[level].isRenderableFormat() || (!mTexStorage && !isSamplerComplete()))
1996 {
1997 mImageArray[level].copy(xoffset, yoffset, x, y, width, height, renderTarget);
1998 mDirtyImages = true;
1999 }
2000 else
2001 {
2002 if (!mTexStorage || !mTexStorage->isRenderTarget())
2003 {
2004 convertToRenderTarget();
2005 }
2006
2007 updateTexture();
2008
2009 if (level < levelCount())
2010 {
2011 RECT sourceRect;
2012 sourceRect.left = x;
2013 sourceRect.right = x + width;
2014 sourceRect.top = y;
2015 sourceRect.bottom = y + height;
2016
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002017 IDirect3DSurface9 *dest = mTexStorage->getSurfaceLevel(level, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002018
2019 if (dest)
2020 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002021 getBlitter()->copy(renderTarget, sourceRect,
2022 gl::ExtractFormat(mImageArray[0].getInternalFormat()),
2023 xoffset, yoffset, dest);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002024 dest->Release();
2025 }
2026 }
2027 }
2028
2029 renderTarget->Release();
2030}
2031
2032void Texture2D::storage(GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height)
2033{
daniel@transgaming.com6b1a0a02012-10-17 18:22:47 +00002034 D3DFORMAT d3dfmt = ConvertTextureInternalFormat(internalformat);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002035 DWORD d3dusage = GetTextureUsage(d3dfmt, mUsage, false);
2036
2037 delete mTexStorage;
2038 mTexStorage = new TextureStorage2D(levels, d3dfmt, d3dusage, width, height);
2039 mImmutable = true;
2040
2041 for (int level = 0; level < levels; level++)
2042 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002043 mImageArray[level].redefine(internalformat, width, height, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002044 width = std::max(1, width >> 1);
2045 height = std::max(1, height >> 1);
2046 }
2047
2048 for (int level = levels; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
2049 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002050 mImageArray[level].redefine(GL_NONE, 0, 0, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002051 }
2052
2053 if (mTexStorage->isManaged())
2054 {
2055 int levels = levelCount();
2056
2057 for (int level = 0; level < levels; level++)
2058 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002059 IDirect3DSurface9 *surface = mTexStorage->getSurfaceLevel(level, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002060 mImageArray[level].setManagedSurface(surface);
2061 }
2062 }
2063}
2064
2065// Tests for 2D texture sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 85.
2066bool Texture2D::isSamplerComplete() const
2067{
2068 GLsizei width = mImageArray[0].getWidth();
2069 GLsizei height = mImageArray[0].getHeight();
2070
2071 if (width <= 0 || height <= 0)
2072 {
2073 return false;
2074 }
2075
daniel@transgaming.comca9a3c82012-10-26 18:55:07 +00002076 bool mipmapping = isMipmapFiltered();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002077
daniel@transgaming.com6b1a0a02012-10-17 18:22:47 +00002078 if ((IsFloat32Format(getInternalFormat(0)) && !getContext()->supportsFloat32LinearFilter()) ||
2079 (IsFloat16Format(getInternalFormat(0)) && !getContext()->supportsFloat16LinearFilter()))
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002080 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +00002081 if (mSamplerState.magFilter != GL_NEAREST ||
2082 (mSamplerState.minFilter != GL_NEAREST && mSamplerState.minFilter != GL_NEAREST_MIPMAP_NEAREST))
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002083 {
2084 return false;
2085 }
2086 }
2087
2088 bool npotSupport = getContext()->supportsNonPower2Texture();
2089
2090 if (!npotSupport)
2091 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +00002092 if ((mSamplerState.wrapS != GL_CLAMP_TO_EDGE && !isPow2(width)) ||
2093 (mSamplerState.wrapT != GL_CLAMP_TO_EDGE && !isPow2(height)))
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002094 {
2095 return false;
2096 }
2097 }
2098
2099 if (mipmapping)
2100 {
2101 if (!npotSupport)
2102 {
2103 if (!isPow2(width) || !isPow2(height))
2104 {
2105 return false;
2106 }
2107 }
2108
2109 if (!isMipmapComplete())
2110 {
2111 return false;
2112 }
2113 }
2114
2115 return true;
2116}
2117
2118// Tests for 2D texture (mipmap) completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
2119bool Texture2D::isMipmapComplete() const
2120{
2121 if (isImmutable())
2122 {
2123 return true;
2124 }
2125
2126 GLsizei width = mImageArray[0].getWidth();
2127 GLsizei height = mImageArray[0].getHeight();
2128
2129 if (width <= 0 || height <= 0)
2130 {
2131 return false;
2132 }
2133
2134 int q = log2(std::max(width, height));
2135
2136 for (int level = 1; level <= q; level++)
2137 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002138 if (mImageArray[level].getInternalFormat() != mImageArray[0].getInternalFormat())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002139 {
2140 return false;
2141 }
2142
2143 if (mImageArray[level].getWidth() != std::max(1, width >> level))
2144 {
2145 return false;
2146 }
2147
2148 if (mImageArray[level].getHeight() != std::max(1, height >> level))
2149 {
2150 return false;
2151 }
2152 }
2153
2154 return true;
2155}
2156
2157bool Texture2D::isCompressed(GLint level) const
2158{
2159 return IsCompressed(getInternalFormat(level));
2160}
2161
2162bool Texture2D::isDepth(GLint level) const
2163{
2164 return IsDepthTexture(getInternalFormat(level));
2165}
2166
2167IDirect3DBaseTexture9 *Texture2D::getBaseTexture() const
2168{
2169 return mTexStorage ? mTexStorage->getBaseTexture() : NULL;
2170}
2171
2172// Constructs a Direct3D 9 texture resource from the texture images
2173void Texture2D::createTexture()
2174{
2175 GLsizei width = mImageArray[0].getWidth();
2176 GLsizei height = mImageArray[0].getHeight();
daniel@transgaming.come6a09842012-09-17 21:28:55 +00002177
2178 if (!(width > 0 && height > 0))
2179 return; // do not attempt to create d3d textures for nonexistant data
2180
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002181 GLint levels = creationLevels(width, height);
2182 D3DFORMAT d3dfmt = mImageArray[0].getD3DFormat();
2183 DWORD d3dusage = GetTextureUsage(d3dfmt, mUsage, false);
2184
2185 delete mTexStorage;
2186 mTexStorage = new TextureStorage2D(levels, d3dfmt, d3dusage, width, height);
2187
2188 if (mTexStorage->isManaged())
2189 {
2190 int levels = levelCount();
2191
2192 for (int level = 0; level < levels; level++)
2193 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002194 IDirect3DSurface9 *surface = mTexStorage->getSurfaceLevel(level, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002195 mImageArray[level].setManagedSurface(surface);
2196 }
2197 }
2198
2199 mDirtyImages = true;
2200}
2201
2202void Texture2D::updateTexture()
2203{
daniel@transgaming.comca9a3c82012-10-26 18:55:07 +00002204 bool mipmapping = (isMipmapFiltered() && isMipmapComplete());
2205
2206 int levels = (mipmapping ? levelCount() : 1);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002207
2208 for (int level = 0; level < levels; level++)
2209 {
2210 Image *image = &mImageArray[level];
2211
2212 if (image->isDirty())
2213 {
2214 commitRect(level, 0, 0, mImageArray[level].getWidth(), mImageArray[level].getHeight());
2215 }
2216 }
2217}
2218
2219void Texture2D::convertToRenderTarget()
2220{
2221 TextureStorage2D *newTexStorage = NULL;
2222
2223 if (mImageArray[0].getWidth() != 0 && mImageArray[0].getHeight() != 0)
2224 {
2225 GLsizei width = mImageArray[0].getWidth();
2226 GLsizei height = mImageArray[0].getHeight();
2227 GLint levels = creationLevels(width, height);
2228 D3DFORMAT d3dfmt = mImageArray[0].getD3DFormat();
2229 DWORD d3dusage = GetTextureUsage(d3dfmt, GL_FRAMEBUFFER_ATTACHMENT_ANGLE, true);
2230
2231 newTexStorage = new TextureStorage2D(levels, d3dfmt, d3dusage, width, height);
2232
2233 if (mTexStorage != NULL)
2234 {
2235 int levels = levelCount();
2236 for (int i = 0; i < levels; i++)
2237 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002238 IDirect3DSurface9 *source = mTexStorage->getSurfaceLevel(i, false);
2239 IDirect3DSurface9 *dest = newTexStorage->getSurfaceLevel(i, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002240
2241 if (!copyToRenderTarget(dest, source, mTexStorage->isManaged()))
2242 {
2243 delete newTexStorage;
2244 if (source) source->Release();
2245 if (dest) dest->Release();
2246 return error(GL_OUT_OF_MEMORY);
2247 }
2248
2249 if (source) source->Release();
2250 if (dest) dest->Release();
2251 }
2252 }
2253 }
2254
2255 delete mTexStorage;
2256 mTexStorage = newTexStorage;
2257
2258 mDirtyImages = true;
2259}
2260
2261void Texture2D::generateMipmaps()
2262{
2263 if (!getContext()->supportsNonPower2Texture())
2264 {
2265 if (!isPow2(mImageArray[0].getWidth()) || !isPow2(mImageArray[0].getHeight()))
2266 {
2267 return error(GL_INVALID_OPERATION);
2268 }
2269 }
2270
2271 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
2272 unsigned int q = log2(std::max(mImageArray[0].getWidth(), mImageArray[0].getHeight()));
2273 for (unsigned int i = 1; i <= q; i++)
2274 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002275 redefineImage(i, mImageArray[0].getInternalFormat(),
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002276 std::max(mImageArray[0].getWidth() >> i, 1),
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002277 std::max(mImageArray[0].getHeight() >> i, 1));
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002278 }
2279
2280 if (mTexStorage && mTexStorage->isRenderTarget())
2281 {
2282 for (unsigned int i = 1; i <= q; i++)
2283 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002284 IDirect3DSurface9 *upper = mTexStorage->getSurfaceLevel(i - 1, false);
2285 IDirect3DSurface9 *lower = mTexStorage->getSurfaceLevel(i, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002286
2287 if (upper != NULL && lower != NULL)
2288 {
2289 getBlitter()->boxFilter(upper, lower);
2290 }
2291
2292 if (upper != NULL) upper->Release();
2293 if (lower != NULL) lower->Release();
2294
2295 mImageArray[i].markClean();
2296 }
2297 }
2298 else
2299 {
2300 for (unsigned int i = 1; i <= q; i++)
2301 {
2302 if (mImageArray[i].getSurface() == NULL)
2303 {
2304 return error(GL_OUT_OF_MEMORY);
2305 }
2306
daniel@transgaming.com2e38b802012-10-17 18:30:06 +00002307 GenerateMip(mImageArray[i].getSurface(), mImageArray[i - 1].getSurface());
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002308
2309 mImageArray[i].markDirty();
2310 }
2311 }
2312}
2313
2314Renderbuffer *Texture2D::getRenderbuffer(GLenum target)
2315{
2316 if (target != GL_TEXTURE_2D)
2317 {
2318 return error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);
2319 }
2320
2321 if (mColorbufferProxy == NULL)
2322 {
2323 mColorbufferProxy = new Renderbuffer(id(), new RenderbufferTexture2D(this, target));
2324 }
2325
2326 return mColorbufferProxy;
2327}
2328
2329// Increments refcount on surface.
2330// caller must Release() the returned surface
2331IDirect3DSurface9 *Texture2D::getRenderTarget(GLenum target)
2332{
2333 ASSERT(target == GL_TEXTURE_2D);
2334
2335 // ensure the underlying texture is created
2336 if (getStorage(true) == NULL)
2337 {
2338 return NULL;
2339 }
2340
2341 updateTexture();
2342
2343 // ensure this is NOT a depth texture
2344 if (isDepth(0))
2345 {
2346 return NULL;
2347 }
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002348 return mTexStorage->getSurfaceLevel(0, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002349}
2350
2351// Increments refcount on surface.
2352// caller must Release() the returned surface
2353IDirect3DSurface9 *Texture2D::getDepthStencil(GLenum target)
2354{
2355 ASSERT(target == GL_TEXTURE_2D);
2356
2357 // ensure the underlying texture is created
2358 if (getStorage(true) == NULL)
2359 {
2360 return NULL;
2361 }
2362
2363 updateTexture();
2364
2365 // ensure this is actually a depth texture
2366 if (!isDepth(0))
2367 {
2368 return NULL;
2369 }
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002370 return mTexStorage->getSurfaceLevel(0, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002371}
2372
2373TextureStorage *Texture2D::getStorage(bool renderTarget)
2374{
2375 if (!mTexStorage || (renderTarget && !mTexStorage->isRenderTarget()))
2376 {
2377 if (renderTarget)
2378 {
2379 convertToRenderTarget();
2380 }
2381 else
2382 {
2383 createTexture();
2384 }
2385 }
2386
2387 return mTexStorage;
2388}
2389
2390TextureStorageCubeMap::TextureStorageCubeMap(int levels, D3DFORMAT format, DWORD usage, int size)
2391 : TextureStorage(usage), mFirstRenderTargetSerial(RenderbufferStorage::issueCubeSerials())
2392{
2393 mTexture = NULL;
2394 // if the size is not positive this should be treated as an incomplete texture
2395 // we handle that here by skipping the d3d texture creation
2396 if (size > 0)
2397 {
daniel@transgaming.comf1122172012-10-31 18:07:20 +00002398 IDirect3DDevice9 *device = getDisplay()->getRenderer()->getDevice(); // D3D9_REPLACE
jbauman@chromium.org68715282012-07-12 23:28:41 +00002399 int height = size;
daniel@transgaming.com4bb04be2012-10-17 18:29:55 +00002400 MakeValidSize(false, dx::IsCompressedFormat(format), &size, &height, &mLodOffset);
sminns@adobe.comce1189b2012-09-18 20:06:35 +00002401 HRESULT result = device->CreateCubeTexture(size, levels ? levels + mLodOffset : 0, getUsage(), format, getPool(), &mTexture, NULL);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002402
2403 if (FAILED(result))
2404 {
2405 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
2406 error(GL_OUT_OF_MEMORY);
2407 }
2408 }
2409}
2410
2411TextureStorageCubeMap::~TextureStorageCubeMap()
2412{
2413 if (mTexture)
2414 {
2415 mTexture->Release();
2416 }
2417}
2418
2419// Increments refcount on surface.
2420// caller must Release() the returned surface
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002421IDirect3DSurface9 *TextureStorageCubeMap::getCubeMapSurface(GLenum faceTarget, int level, bool dirty)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002422{
2423 IDirect3DSurface9 *surface = NULL;
2424
2425 if (mTexture)
2426 {
daniel@transgaming.com1ee986b2012-09-27 17:46:12 +00002427 D3DCUBEMAP_FACES face = es2dx::ConvertCubeFace(faceTarget);
2428 HRESULT result = mTexture->GetCubeMapSurface(face, level + mLodOffset, &surface);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002429 ASSERT(SUCCEEDED(result));
daniel@transgaming.com1ee986b2012-09-27 17:46:12 +00002430
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002431 // With managed textures the driver needs to be informed of updates to the lower mipmap levels
2432 if (level != 0 && isManaged() && dirty)
daniel@transgaming.com1ee986b2012-09-27 17:46:12 +00002433 {
2434 mTexture->AddDirtyRect(face, NULL);
2435 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002436 }
2437
2438 return surface;
2439}
2440
2441IDirect3DBaseTexture9 *TextureStorageCubeMap::getBaseTexture() const
2442{
2443 return mTexture;
2444}
2445
2446unsigned int TextureStorageCubeMap::getRenderTargetSerial(GLenum target) const
2447{
2448 return mFirstRenderTargetSerial + TextureCubeMap::faceIndex(target);
2449}
2450
2451TextureCubeMap::TextureCubeMap(GLuint id) : Texture(id)
2452{
2453 mTexStorage = NULL;
2454 for (int i = 0; i < 6; i++)
2455 {
2456 mFaceProxies[i] = NULL;
2457 mFaceProxyRefs[i] = 0;
2458 }
2459}
2460
2461TextureCubeMap::~TextureCubeMap()
2462{
2463 for (int i = 0; i < 6; i++)
2464 {
2465 mFaceProxies[i] = NULL;
2466 }
2467
2468 delete mTexStorage;
2469 mTexStorage = NULL;
2470}
2471
2472// We need to maintain a count of references to renderbuffers acting as
2473// proxies for this texture, so that the texture is not deleted while
2474// proxy references still exist. If the reference count drops to zero,
2475// we set our proxy pointer NULL, so that a new attempt at referencing
2476// will cause recreation.
2477void TextureCubeMap::addProxyRef(const Renderbuffer *proxy)
2478{
2479 for (int i = 0; i < 6; i++)
2480 {
2481 if (mFaceProxies[i] == proxy)
2482 mFaceProxyRefs[i]++;
2483 }
2484}
2485
2486void TextureCubeMap::releaseProxy(const Renderbuffer *proxy)
2487{
2488 for (int i = 0; i < 6; i++)
2489 {
2490 if (mFaceProxies[i] == proxy)
2491 {
2492 if (mFaceProxyRefs[i] > 0)
2493 mFaceProxyRefs[i]--;
2494
2495 if (mFaceProxyRefs[i] == 0)
2496 mFaceProxies[i] = NULL;
2497 }
2498 }
2499}
2500
2501GLenum TextureCubeMap::getTarget() const
2502{
2503 return GL_TEXTURE_CUBE_MAP;
2504}
2505
2506GLsizei TextureCubeMap::getWidth(GLenum target, GLint level) const
2507{
2508 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
2509 return mImageArray[faceIndex(target)][level].getWidth();
2510 else
2511 return 0;
2512}
2513
2514GLsizei TextureCubeMap::getHeight(GLenum target, GLint level) const
2515{
2516 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
2517 return mImageArray[faceIndex(target)][level].getHeight();
2518 else
2519 return 0;
2520}
2521
2522GLenum TextureCubeMap::getInternalFormat(GLenum target, GLint level) const
2523{
2524 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002525 return mImageArray[faceIndex(target)][level].getInternalFormat();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002526 else
2527 return GL_NONE;
2528}
2529
2530D3DFORMAT TextureCubeMap::getD3DFormat(GLenum target, GLint level) const
2531{
2532 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
2533 return mImageArray[faceIndex(target)][level].getD3DFormat();
2534 else
2535 return D3DFMT_UNKNOWN;
2536}
2537
2538void TextureCubeMap::setImagePosX(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
2539{
2540 setImage(0, level, width, height, format, type, unpackAlignment, pixels);
2541}
2542
2543void TextureCubeMap::setImageNegX(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
2544{
2545 setImage(1, level, width, height, format, type, unpackAlignment, pixels);
2546}
2547
2548void TextureCubeMap::setImagePosY(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
2549{
2550 setImage(2, level, width, height, format, type, unpackAlignment, pixels);
2551}
2552
2553void TextureCubeMap::setImageNegY(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
2554{
2555 setImage(3, level, width, height, format, type, unpackAlignment, pixels);
2556}
2557
2558void TextureCubeMap::setImagePosZ(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
2559{
2560 setImage(4, level, width, height, format, type, unpackAlignment, pixels);
2561}
2562
2563void TextureCubeMap::setImageNegZ(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
2564{
2565 setImage(5, level, width, height, format, type, unpackAlignment, pixels);
2566}
2567
2568void TextureCubeMap::setCompressedImage(GLenum face, GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
2569{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002570 // compressed formats don't have separate sized internal formats-- we can just use the compressed format directly
2571 redefineImage(faceIndex(face), level, format, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002572
2573 Texture::setCompressedImage(imageSize, pixels, &mImageArray[faceIndex(face)][level]);
2574}
2575
2576void TextureCubeMap::commitRect(int face, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
2577{
2578 ASSERT(mImageArray[face][level].getSurface() != NULL);
2579
2580 if (level < levelCount())
2581 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002582 IDirect3DSurface9 *destLevel = mTexStorage->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, level, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002583 ASSERT(destLevel != NULL);
2584
2585 if (destLevel != NULL)
2586 {
2587 Image *image = &mImageArray[face][level];
2588 image->updateSurface(destLevel, xoffset, yoffset, width, height);
2589
2590 destLevel->Release();
2591 image->markClean();
2592 }
2593 }
2594}
2595
2596void TextureCubeMap::subImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
2597{
2598 if (Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[faceIndex(target)][level]))
2599 {
2600 commitRect(faceIndex(target), level, xoffset, yoffset, width, height);
2601 }
2602}
2603
2604void TextureCubeMap::subImageCompressed(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
2605{
2606 if (Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, &mImageArray[faceIndex(target)][level]))
2607 {
2608 commitRect(faceIndex(target), level, xoffset, yoffset, width, height);
2609 }
2610}
2611
2612// Tests for cube map sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 86.
2613bool TextureCubeMap::isSamplerComplete() const
2614{
2615 int size = mImageArray[0][0].getWidth();
2616
daniel@transgaming.comca9a3c82012-10-26 18:55:07 +00002617 bool mipmapping = isMipmapFiltered();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002618
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002619 if ((gl::ExtractType(getInternalFormat(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0)) == GL_FLOAT && !getContext()->supportsFloat32LinearFilter()) ||
2620 (gl::ExtractType(getInternalFormat(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0) == GL_HALF_FLOAT_OES) && !getContext()->supportsFloat16LinearFilter()))
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002621 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +00002622 if (mSamplerState.magFilter != GL_NEAREST ||
2623 (mSamplerState.minFilter != GL_NEAREST && mSamplerState.minFilter != GL_NEAREST_MIPMAP_NEAREST))
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002624 {
2625 return false;
2626 }
2627 }
2628
2629 if (!isPow2(size) && !getContext()->supportsNonPower2Texture())
2630 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +00002631 if (mSamplerState.wrapS != GL_CLAMP_TO_EDGE || mSamplerState.wrapT != GL_CLAMP_TO_EDGE || mipmapping)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002632 {
2633 return false;
2634 }
2635 }
2636
2637 if (!mipmapping)
2638 {
2639 if (!isCubeComplete())
2640 {
2641 return false;
2642 }
2643 }
2644 else
2645 {
2646 if (!isMipmapCubeComplete()) // Also tests for isCubeComplete()
2647 {
2648 return false;
2649 }
2650 }
2651
2652 return true;
2653}
2654
2655// Tests for cube texture completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
2656bool TextureCubeMap::isCubeComplete() const
2657{
2658 if (mImageArray[0][0].getWidth() <= 0 || mImageArray[0][0].getHeight() != mImageArray[0][0].getWidth())
2659 {
2660 return false;
2661 }
2662
2663 for (unsigned int face = 1; face < 6; face++)
2664 {
2665 if (mImageArray[face][0].getWidth() != mImageArray[0][0].getWidth() ||
2666 mImageArray[face][0].getWidth() != mImageArray[0][0].getHeight() ||
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002667 mImageArray[face][0].getInternalFormat() != mImageArray[0][0].getInternalFormat())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002668 {
2669 return false;
2670 }
2671 }
2672
2673 return true;
2674}
2675
2676bool TextureCubeMap::isMipmapCubeComplete() const
2677{
2678 if (isImmutable())
2679 {
2680 return true;
2681 }
2682
2683 if (!isCubeComplete())
2684 {
2685 return false;
2686 }
2687
2688 GLsizei size = mImageArray[0][0].getWidth();
2689
2690 int q = log2(size);
2691
2692 for (int face = 0; face < 6; face++)
2693 {
2694 for (int level = 1; level <= q; level++)
2695 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002696 if (mImageArray[face][level].getInternalFormat() != mImageArray[0][0].getInternalFormat())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002697 {
2698 return false;
2699 }
2700
2701 if (mImageArray[face][level].getWidth() != std::max(1, size >> level))
2702 {
2703 return false;
2704 }
2705 }
2706 }
2707
2708 return true;
2709}
2710
2711bool TextureCubeMap::isCompressed(GLenum target, GLint level) const
2712{
2713 return IsCompressed(getInternalFormat(target, level));
2714}
2715
2716IDirect3DBaseTexture9 *TextureCubeMap::getBaseTexture() const
2717{
2718 return mTexStorage ? mTexStorage->getBaseTexture() : NULL;
2719}
2720
2721// Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one
2722void TextureCubeMap::createTexture()
2723{
2724 GLsizei size = mImageArray[0][0].getWidth();
daniel@transgaming.come6a09842012-09-17 21:28:55 +00002725
2726 if (!(size > 0))
2727 return; // do not attempt to create d3d textures for nonexistant data
2728
sminns@adobe.comce1189b2012-09-18 20:06:35 +00002729 GLint levels = creationLevels(size);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002730 D3DFORMAT d3dfmt = mImageArray[0][0].getD3DFormat();
2731 DWORD d3dusage = GetTextureUsage(d3dfmt, mUsage, false);
2732
2733 delete mTexStorage;
2734 mTexStorage = new TextureStorageCubeMap(levels, d3dfmt, d3dusage, size);
2735
2736 if (mTexStorage->isManaged())
2737 {
2738 int levels = levelCount();
2739
2740 for (int face = 0; face < 6; face++)
2741 {
2742 for (int level = 0; level < levels; level++)
2743 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002744 IDirect3DSurface9 *surface = mTexStorage->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, level, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002745 mImageArray[face][level].setManagedSurface(surface);
2746 }
2747 }
2748 }
2749
2750 mDirtyImages = true;
2751}
2752
2753void TextureCubeMap::updateTexture()
2754{
daniel@transgaming.comca9a3c82012-10-26 18:55:07 +00002755 bool mipmapping = isMipmapFiltered() && isMipmapCubeComplete();
2756
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002757 for (int face = 0; face < 6; face++)
2758 {
daniel@transgaming.comca9a3c82012-10-26 18:55:07 +00002759 int levels = (mipmapping ? levelCount() : 1);
2760
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002761 for (int level = 0; level < levels; level++)
2762 {
2763 Image *image = &mImageArray[face][level];
2764
2765 if (image->isDirty())
2766 {
2767 commitRect(face, level, 0, 0, image->getWidth(), image->getHeight());
2768 }
2769 }
2770 }
2771}
2772
2773void TextureCubeMap::convertToRenderTarget()
2774{
2775 TextureStorageCubeMap *newTexStorage = NULL;
2776
2777 if (mImageArray[0][0].getWidth() != 0)
2778 {
2779 GLsizei size = mImageArray[0][0].getWidth();
sminns@adobe.comce1189b2012-09-18 20:06:35 +00002780 GLint levels = creationLevels(size);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002781 D3DFORMAT d3dfmt = mImageArray[0][0].getD3DFormat();
2782 DWORD d3dusage = GetTextureUsage(d3dfmt, GL_FRAMEBUFFER_ATTACHMENT_ANGLE, true);
2783
2784 newTexStorage = new TextureStorageCubeMap(levels, d3dfmt, d3dusage, size);
2785
2786 if (mTexStorage != NULL)
2787 {
2788 int levels = levelCount();
2789 for (int f = 0; f < 6; f++)
2790 {
2791 for (int i = 0; i < levels; i++)
2792 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002793 IDirect3DSurface9 *source = mTexStorage->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + f, i, false);
2794 IDirect3DSurface9 *dest = newTexStorage->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + f, i, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002795
2796 if (!copyToRenderTarget(dest, source, mTexStorage->isManaged()))
2797 {
2798 delete newTexStorage;
2799 if (source) source->Release();
2800 if (dest) dest->Release();
2801 return error(GL_OUT_OF_MEMORY);
2802 }
2803
2804 if (source) source->Release();
2805 if (dest) dest->Release();
2806 }
2807 }
2808 }
2809 }
2810
2811 delete mTexStorage;
2812 mTexStorage = newTexStorage;
2813
2814 mDirtyImages = true;
2815}
2816
2817void TextureCubeMap::setImage(int faceIndex, GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
2818{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002819 GLint internalformat = ConvertSizedInternalFormat(format, type);
2820 redefineImage(faceIndex, level, internalformat, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002821
2822 Texture::setImage(unpackAlignment, pixels, &mImageArray[faceIndex][level]);
2823}
2824
2825unsigned int TextureCubeMap::faceIndex(GLenum face)
2826{
2827 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_X - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 1);
2828 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 2);
2829 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 3);
2830 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 4);
2831 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 5);
2832
2833 return face - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
2834}
2835
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002836void TextureCubeMap::redefineImage(int face, GLint level, GLint internalformat, GLsizei width, GLsizei height)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002837{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002838 bool redefined = mImageArray[face][level].redefine(internalformat, width, height, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002839
2840 if (mTexStorage && redefined)
2841 {
2842 for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
2843 {
2844 for (int f = 0; f < 6; f++)
2845 {
2846 mImageArray[f][i].markDirty();
2847 }
2848 }
2849
2850 delete mTexStorage;
2851 mTexStorage = NULL;
2852
2853 mDirtyImages = true;
2854 }
2855}
2856
2857void TextureCubeMap::copyImage(GLenum target, GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
2858{
2859 IDirect3DSurface9 *renderTarget = source->getRenderTarget();
2860
2861 if (!renderTarget)
2862 {
2863 ERR("Failed to retrieve the render target.");
2864 return error(GL_OUT_OF_MEMORY);
2865 }
2866
2867 unsigned int faceindex = faceIndex(target);
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002868 GLint internalformat = gl::ConvertSizedInternalFormat(format, GL_UNSIGNED_BYTE);
2869 redefineImage(faceindex, level, internalformat, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002870
2871 if (!mImageArray[faceindex][level].isRenderableFormat())
2872 {
2873 mImageArray[faceindex][level].copy(0, 0, x, y, width, height, renderTarget);
2874 mDirtyImages = true;
2875 }
2876 else
2877 {
2878 if (!mTexStorage || !mTexStorage->isRenderTarget())
2879 {
2880 convertToRenderTarget();
2881 }
2882
2883 mImageArray[faceindex][level].markClean();
2884
2885 ASSERT(width == height);
2886
2887 if (width > 0 && level < levelCount())
2888 {
2889 RECT sourceRect;
2890 sourceRect.left = x;
2891 sourceRect.right = x + width;
2892 sourceRect.top = y;
2893 sourceRect.bottom = y + height;
2894
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002895 IDirect3DSurface9 *dest = mTexStorage->getCubeMapSurface(target, level, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002896
2897 if (dest)
2898 {
2899 getBlitter()->copy(renderTarget, sourceRect, format, 0, 0, dest);
2900 dest->Release();
2901 }
2902 }
2903 }
2904
2905 renderTarget->Release();
2906}
2907
2908void TextureCubeMap::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
2909{
2910 GLsizei size = mImageArray[faceIndex(target)][level].getWidth();
2911
2912 if (xoffset + width > size || yoffset + height > size)
2913 {
2914 return error(GL_INVALID_VALUE);
2915 }
2916
2917 IDirect3DSurface9 *renderTarget = source->getRenderTarget();
2918
2919 if (!renderTarget)
2920 {
2921 ERR("Failed to retrieve the render target.");
2922 return error(GL_OUT_OF_MEMORY);
2923 }
2924
2925 unsigned int faceindex = faceIndex(target);
2926
2927 if (!mImageArray[faceindex][level].isRenderableFormat() || (!mTexStorage && !isSamplerComplete()))
2928 {
2929 mImageArray[faceindex][level].copy(0, 0, x, y, width, height, renderTarget);
2930 mDirtyImages = true;
2931 }
2932 else
2933 {
2934 if (!mTexStorage || !mTexStorage->isRenderTarget())
2935 {
2936 convertToRenderTarget();
2937 }
2938
2939 updateTexture();
2940
2941 if (level < levelCount())
2942 {
2943 RECT sourceRect;
2944 sourceRect.left = x;
2945 sourceRect.right = x + width;
2946 sourceRect.top = y;
2947 sourceRect.bottom = y + height;
2948
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002949 IDirect3DSurface9 *dest = mTexStorage->getCubeMapSurface(target, level, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002950
2951 if (dest)
2952 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002953 getBlitter()->copy(renderTarget, sourceRect, gl::ExtractFormat(mImageArray[0][0].getInternalFormat()), xoffset, yoffset, dest);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002954 dest->Release();
2955 }
2956 }
2957 }
2958
2959 renderTarget->Release();
2960}
2961
2962void TextureCubeMap::storage(GLsizei levels, GLenum internalformat, GLsizei size)
2963{
daniel@transgaming.com6b1a0a02012-10-17 18:22:47 +00002964 D3DFORMAT d3dfmt = ConvertTextureInternalFormat(internalformat);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002965 DWORD d3dusage = GetTextureUsage(d3dfmt, mUsage, false);
2966
2967 delete mTexStorage;
2968 mTexStorage = new TextureStorageCubeMap(levels, d3dfmt, d3dusage, size);
2969 mImmutable = true;
2970
2971 for (int level = 0; level < levels; level++)
2972 {
2973 for (int face = 0; face < 6; face++)
2974 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002975 mImageArray[face][level].redefine(internalformat, size, size, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002976 size = std::max(1, size >> 1);
2977 }
2978 }
2979
2980 for (int level = levels; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
2981 {
2982 for (int face = 0; face < 6; face++)
2983 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002984 mImageArray[face][level].redefine(GL_NONE, 0, 0, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002985 }
2986 }
2987
2988 if (mTexStorage->isManaged())
2989 {
2990 int levels = levelCount();
2991
2992 for (int face = 0; face < 6; face++)
2993 {
2994 for (int level = 0; level < levels; level++)
2995 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002996 IDirect3DSurface9 *surface = mTexStorage->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, level, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002997 mImageArray[face][level].setManagedSurface(surface);
2998 }
2999 }
3000 }
3001}
3002
3003void TextureCubeMap::generateMipmaps()
3004{
3005 if (!isCubeComplete())
3006 {
3007 return error(GL_INVALID_OPERATION);
3008 }
3009
3010 if (!getContext()->supportsNonPower2Texture())
3011 {
3012 if (!isPow2(mImageArray[0][0].getWidth()))
3013 {
3014 return error(GL_INVALID_OPERATION);
3015 }
3016 }
3017
3018 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
3019 unsigned int q = log2(mImageArray[0][0].getWidth());
3020 for (unsigned int f = 0; f < 6; f++)
3021 {
3022 for (unsigned int i = 1; i <= q; i++)
3023 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00003024 redefineImage(f, i, mImageArray[f][0].getInternalFormat(),
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00003025 std::max(mImageArray[f][0].getWidth() >> i, 1),
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00003026 std::max(mImageArray[f][0].getWidth() >> i, 1));
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00003027 }
3028 }
3029
3030 if (mTexStorage && mTexStorage->isRenderTarget())
3031 {
3032 for (unsigned int f = 0; f < 6; f++)
3033 {
3034 for (unsigned int i = 1; i <= q; i++)
3035 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00003036 IDirect3DSurface9 *upper = mTexStorage->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + f, i - 1, false);
3037 IDirect3DSurface9 *lower = mTexStorage->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + f, i, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00003038
3039 if (upper != NULL && lower != NULL)
3040 {
3041 getBlitter()->boxFilter(upper, lower);
3042 }
3043
3044 if (upper != NULL) upper->Release();
3045 if (lower != NULL) lower->Release();
3046
3047 mImageArray[f][i].markClean();
3048 }
3049 }
3050 }
3051 else
3052 {
3053 for (unsigned int f = 0; f < 6; f++)
3054 {
3055 for (unsigned int i = 1; i <= q; i++)
3056 {
3057 if (mImageArray[f][i].getSurface() == NULL)
3058 {
3059 return error(GL_OUT_OF_MEMORY);
3060 }
3061
daniel@transgaming.com2e38b802012-10-17 18:30:06 +00003062 GenerateMip(mImageArray[f][i].getSurface(), mImageArray[f][i - 1].getSurface());
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00003063
3064 mImageArray[f][i].markDirty();
3065 }
3066 }
3067 }
3068}
3069
3070Renderbuffer *TextureCubeMap::getRenderbuffer(GLenum target)
3071{
3072 if (!IsCubemapTextureTarget(target))
3073 {
3074 return error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);
3075 }
3076
3077 unsigned int face = faceIndex(target);
3078
3079 if (mFaceProxies[face] == NULL)
3080 {
3081 mFaceProxies[face] = new Renderbuffer(id(), new RenderbufferTextureCubeMap(this, target));
3082 }
3083
3084 return mFaceProxies[face];
3085}
3086
3087// Increments refcount on surface.
3088// caller must Release() the returned surface
3089IDirect3DSurface9 *TextureCubeMap::getRenderTarget(GLenum target)
3090{
3091 ASSERT(IsCubemapTextureTarget(target));
3092
3093 // ensure the underlying texture is created
3094 if (getStorage(true) == NULL)
3095 {
3096 return NULL;
3097 }
3098
3099 updateTexture();
3100
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00003101 return mTexStorage->getCubeMapSurface(target, 0, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00003102}
3103
3104TextureStorage *TextureCubeMap::getStorage(bool renderTarget)
3105{
3106 if (!mTexStorage || (renderTarget && !mTexStorage->isRenderTarget()))
3107 {
3108 if (renderTarget)
3109 {
3110 convertToRenderTarget();
3111 }
3112 else
3113 {
3114 createTexture();
3115 }
3116 }
3117
3118 return mTexStorage;
3119}
3120
3121}