blob: 0ba8b5c87e4899c882732503e6279f3316413014 [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
13#include <d3dx9tex.h>
14
15#include <algorithm>
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000016
17#include "common/debug.h"
18
19#include "libEGL/Display.h"
20
21#include "libGLESv2/main.h"
22#include "libGLESv2/mathutil.h"
23#include "libGLESv2/utilities.h"
24#include "libGLESv2/Blit.h"
25#include "libGLESv2/Framebuffer.h"
26
27namespace gl
28{
29unsigned int TextureStorage::mCurrentTextureSerial = 1;
30
daniel@transgaming.com6452adf2012-10-17 18:22:35 +000031static D3DFORMAT ConvertTextureFormatType(GLint internalformat)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000032{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +000033 switch (internalformat)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000034 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +000035 case GL_DEPTH_COMPONENT16:
36 case GL_DEPTH_COMPONENT32_OES:
37 case GL_DEPTH24_STENCIL8_OES:
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000038 return D3DFMT_INTZ;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +000039 case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
40 case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000041 return D3DFMT_DXT1;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +000042 case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000043 return D3DFMT_DXT3;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +000044 case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000045 return D3DFMT_DXT5;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +000046 case GL_RGBA32F_EXT:
47 case GL_RGB32F_EXT:
48 case GL_ALPHA32F_EXT:
49 case GL_LUMINANCE32F_EXT:
50 case GL_LUMINANCE_ALPHA32F_EXT:
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000051 return D3DFMT_A32B32G32R32F;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +000052 case GL_RGBA16F_EXT:
53 case GL_RGB16F_EXT:
54 case GL_ALPHA16F_EXT:
55 case GL_LUMINANCE16F_EXT:
56 case GL_LUMINANCE_ALPHA16F_EXT:
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000057 return D3DFMT_A16B16G16R16F;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +000058 case GL_LUMINANCE8_EXT:
59 if (getContext()->supportsLuminanceTextures())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000060 {
61 return D3DFMT_L8;
62 }
daniel@transgaming.com6452adf2012-10-17 18:22:35 +000063 break;
64 case GL_LUMINANCE8_ALPHA8_EXT:
65 if (getContext()->supportsLuminanceAlphaTextures())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000066 {
67 return D3DFMT_A8L8;
68 }
daniel@transgaming.com6452adf2012-10-17 18:22:35 +000069 break;
70 case GL_RGB8_OES:
71 return D3DFMT_X8R8G8B8;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000072 }
73
74 return D3DFMT_A8R8G8B8;
75}
76
77static bool IsTextureFormatRenderable(D3DFORMAT format)
78{
79 if (format == D3DFMT_INTZ)
80 {
81 return true;
82 }
83 switch(format)
84 {
85 case D3DFMT_L8:
86 case D3DFMT_A8L8:
87 case D3DFMT_DXT1:
88 case D3DFMT_DXT3:
89 case D3DFMT_DXT5:
90 return false;
91 case D3DFMT_A8R8G8B8:
92 case D3DFMT_X8R8G8B8:
93 case D3DFMT_A16B16G16R16F:
94 case D3DFMT_A32B32G32R32F:
95 return true;
96 default:
97 UNREACHABLE();
98 }
99
100 return false;
101}
102
103static inline DWORD GetTextureUsage(D3DFORMAT d3dfmt, GLenum glusage, bool forceRenderable)
104{
105 DWORD d3dusage = 0;
106
107 if (d3dfmt == D3DFMT_INTZ)
108 {
109 d3dusage |= D3DUSAGE_DEPTHSTENCIL;
110 }
111 else if(forceRenderable || (IsTextureFormatRenderable(d3dfmt) && (glusage == GL_FRAMEBUFFER_ATTACHMENT_ANGLE)))
112 {
113 d3dusage |= D3DUSAGE_RENDERTARGET;
114 }
115 return d3dusage;
116}
117
jbauman@chromium.org68715282012-07-12 23:28:41 +0000118static void MakeValidSize(bool isImage, bool isCompressed, GLsizei *requestWidth, GLsizei *requestHeight, int *levelOffset) {
119 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.com95a758f2012-07-12 15:17:06 +0000138Image::Image()
139{
140 mWidth = 0;
141 mHeight = 0;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000142 mInternalFormat = GL_NONE;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000143
144 mSurface = NULL;
145
146 mDirty = false;
147
148 mD3DPool = D3DPOOL_SYSTEMMEM;
149 mD3DFormat = D3DFMT_UNKNOWN;
150}
151
152Image::~Image()
153{
154 if (mSurface)
155 {
156 mSurface->Release();
157 }
158}
159
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000160bool Image::redefine(GLint internalformat, GLsizei width, GLsizei height, bool forceRelease)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000161{
162 if (mWidth != width ||
163 mHeight != height ||
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000164 mInternalFormat != internalformat ||
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000165 forceRelease)
166 {
167 mWidth = width;
168 mHeight = height;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000169 mInternalFormat = internalformat;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000170 // compute the d3d format that will be used
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000171 mD3DFormat = ConvertTextureFormatType(internalformat);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000172
173 if (mSurface)
174 {
175 mSurface->Release();
176 mSurface = NULL;
177 }
178
179 return true;
180 }
181
182 return false;
183}
184
185void Image::createSurface()
186{
187 if(mSurface)
188 {
189 return;
190 }
191
192 IDirect3DTexture9 *newTexture = NULL;
193 IDirect3DSurface9 *newSurface = NULL;
194 const D3DPOOL poolToUse = D3DPOOL_SYSTEMMEM;
195 const D3DFORMAT d3dFormat = getD3DFormat();
196 ASSERT(d3dFormat != D3DFMT_INTZ); // We should never get here for depth textures
197
198 if (mWidth != 0 && mHeight != 0)
199 {
200 int levelToFetch = 0;
201 GLsizei requestWidth = mWidth;
202 GLsizei requestHeight = mHeight;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000203 MakeValidSize(true, IsCompressed(mInternalFormat), &requestWidth, &requestHeight, &levelToFetch);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000204
205 HRESULT result = getDevice()->CreateTexture(requestWidth, requestHeight, levelToFetch + 1, NULL, d3dFormat,
206 poolToUse, &newTexture, NULL);
207
208 if (FAILED(result))
209 {
210 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
211 ERR("Creating image surface failed.");
212 return error(GL_OUT_OF_MEMORY);
213 }
214
215 newTexture->GetSurfaceLevel(levelToFetch, &newSurface);
216 newTexture->Release();
217 }
218
219 mSurface = newSurface;
220 mDirty = false;
221 mD3DPool = poolToUse;
222}
223
224HRESULT Image::lock(D3DLOCKED_RECT *lockedRect, const RECT *rect)
225{
226 createSurface();
227
228 HRESULT result = D3DERR_INVALIDCALL;
229
230 if (mSurface)
231 {
232 result = mSurface->LockRect(lockedRect, rect, 0);
233 ASSERT(SUCCEEDED(result));
234
235 mDirty = true;
236 }
237
238 return result;
239}
240
241void Image::unlock()
242{
243 if (mSurface)
244 {
245 HRESULT result = mSurface->UnlockRect();
246 ASSERT(SUCCEEDED(result));
247 }
248}
249
250bool Image::isRenderableFormat() const
251{
252 return IsTextureFormatRenderable(getD3DFormat());
253}
254
255D3DFORMAT Image::getD3DFormat() const
256{
257 // this should only happen if the image hasn't been redefined first
258 // which would be a bug by the caller
259 ASSERT(mD3DFormat != D3DFMT_UNKNOWN);
260
261 return mD3DFormat;
262}
263
264IDirect3DSurface9 *Image::getSurface()
265{
266 createSurface();
267
268 return mSurface;
269}
270
271void Image::setManagedSurface(IDirect3DSurface9 *surface)
272{
273 if (mSurface)
274 {
275 D3DXLoadSurfaceFromSurface(surface, NULL, NULL, mSurface, NULL, NULL, D3DX_FILTER_BOX, 0);
276 mSurface->Release();
277 }
278
279 D3DSURFACE_DESC desc;
280 surface->GetDesc(&desc);
281 ASSERT(desc.Pool == D3DPOOL_MANAGED);
282
283 mSurface = surface;
284 mD3DPool = desc.Pool;
285}
286
287void Image::updateSurface(IDirect3DSurface9 *destSurface, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
288{
289 IDirect3DSurface9 *sourceSurface = getSurface();
290
291 if (sourceSurface && sourceSurface != destSurface)
292 {
293 RECT rect;
294 rect.left = xoffset;
295 rect.top = yoffset;
296 rect.right = xoffset + width;
297 rect.bottom = yoffset + height;
298
299 if (mD3DPool == D3DPOOL_MANAGED)
300 {
301 HRESULT result = D3DXLoadSurfaceFromSurface(destSurface, NULL, &rect, sourceSurface, NULL, &rect, D3DX_FILTER_BOX, 0);
302 ASSERT(SUCCEEDED(result));
303 }
304 else
305 {
306 // UpdateSurface: source must be SYSTEMMEM, dest must be DEFAULT pools
307 POINT point = {rect.left, rect.top};
308 HRESULT result = getDevice()->UpdateSurface(sourceSurface, &rect, destSurface, &point);
309 ASSERT(SUCCEEDED(result));
310 }
311 }
312}
313
314// Store the pixel rectangle designated by xoffset,yoffset,width,height with pixels stored as format/type at input
315// into the target pixel rectangle.
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000316void Image::loadData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000317 GLint unpackAlignment, const void *input)
318{
319 RECT lockRect =
320 {
321 xoffset, yoffset,
322 xoffset + width, yoffset + height
323 };
324
325 D3DLOCKED_RECT locked;
326 HRESULT result = lock(&locked, &lockRect);
327 if (FAILED(result))
328 {
329 return;
330 }
331
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000332
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000333 GLsizei inputPitch = ComputePitch(width, mInternalFormat, unpackAlignment);
334
335 switch (mInternalFormat)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000336 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000337 case GL_ALPHA8_EXT:
338 if (supportsSSE2())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000339 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000340 loadAlphaDataSSE2(width, height, inputPitch, input, locked.Pitch, locked.pBits);
341 }
342 else
343 {
344 loadAlphaData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000345 }
346 break;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000347 case GL_LUMINANCE8_EXT:
348 loadLuminanceData(width, height, inputPitch, input, locked.Pitch, locked.pBits, getD3DFormat() == D3DFMT_L8);
349 break;
350 case GL_ALPHA32F_EXT:
351 loadAlphaFloatData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
352 break;
353 case GL_LUMINANCE32F_EXT:
354 loadLuminanceFloatData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
355 break;
356 case GL_ALPHA16F_EXT:
357 loadAlphaHalfFloatData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
358 break;
359 case GL_LUMINANCE16F_EXT:
360 loadLuminanceHalfFloatData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
361 break;
362 case GL_LUMINANCE8_ALPHA8_EXT:
363 loadLuminanceAlphaData(width, height, inputPitch, input, locked.Pitch, locked.pBits, getD3DFormat() == D3DFMT_A8L8);
364 break;
365 case GL_LUMINANCE_ALPHA32F_EXT:
366 loadLuminanceAlphaFloatData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
367 break;
368 case GL_LUMINANCE_ALPHA16F_EXT:
369 loadLuminanceAlphaHalfFloatData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
370 break;
371 case GL_RGB8_OES:
372 loadRGBUByteData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
373 break;
374 case GL_RGB565:
375 loadRGB565Data(width, height, inputPitch, input, locked.Pitch, locked.pBits);
376 break;
377 case GL_RGBA8_OES:
378 if (supportsSSE2())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000379 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000380 loadRGBAUByteDataSSE2(width, height, inputPitch, input, locked.Pitch, locked.pBits);
381 }
382 else
383 {
384 loadRGBAUByteData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000385 }
386 break;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000387 case GL_RGBA4:
388 loadRGBA4444Data(width, height, inputPitch, input, locked.Pitch, locked.pBits);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000389 break;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000390 case GL_RGB5_A1:
391 loadRGBA5551Data(width, height, inputPitch, input, locked.Pitch, locked.pBits);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000392 break;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000393 case GL_BGRA8_EXT:
394 loadBGRAData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000395 break;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000396 // float textures are converted to RGBA, not BGRA, as they're stored that way in D3D
397 case GL_RGB32F_EXT:
398 loadRGBFloatData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000399 break;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000400 case GL_RGB16F_EXT:
401 loadRGBHalfFloatData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
402 break;
403 case GL_RGBA32F_EXT:
404 loadRGBAFloatData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
405 break;
406 case GL_RGBA16F_EXT:
407 loadRGBAHalfFloatData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
408 break;
409 default: UNREACHABLE();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000410 }
411
412 unlock();
413}
414
415void Image::loadAlphaData(GLsizei width, GLsizei height,
416 int inputPitch, const void *input, size_t outputPitch, void *output) const
417{
418 const unsigned char *source = NULL;
419 unsigned char *dest = NULL;
420
421 for (int y = 0; y < height; y++)
422 {
423 source = static_cast<const unsigned char*>(input) + y * inputPitch;
424 dest = static_cast<unsigned char*>(output) + y * outputPitch;
425 for (int x = 0; x < width; x++)
426 {
427 dest[4 * x + 0] = 0;
428 dest[4 * x + 1] = 0;
429 dest[4 * x + 2] = 0;
430 dest[4 * x + 3] = source[x];
431 }
432 }
433}
434
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000435void Image::loadAlphaFloatData(GLsizei width, GLsizei height,
436 int inputPitch, const void *input, size_t outputPitch, void *output) const
437{
438 const float *source = NULL;
439 float *dest = NULL;
440
441 for (int y = 0; y < height; y++)
442 {
443 source = reinterpret_cast<const float*>(static_cast<const unsigned char*>(input) + y * inputPitch);
444 dest = reinterpret_cast<float*>(static_cast<unsigned char*>(output) + y * outputPitch);
445 for (int x = 0; x < width; x++)
446 {
447 dest[4 * x + 0] = 0;
448 dest[4 * x + 1] = 0;
449 dest[4 * x + 2] = 0;
450 dest[4 * x + 3] = source[x];
451 }
452 }
453}
454
455void Image::loadAlphaHalfFloatData(GLsizei width, GLsizei height,
456 int inputPitch, const void *input, size_t outputPitch, void *output) const
457{
458 const unsigned short *source = NULL;
459 unsigned short *dest = NULL;
460
461 for (int y = 0; y < height; y++)
462 {
463 source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
464 dest = reinterpret_cast<unsigned short*>(static_cast<unsigned char*>(output) + y * outputPitch);
465 for (int x = 0; x < width; x++)
466 {
467 dest[4 * x + 0] = 0;
468 dest[4 * x + 1] = 0;
469 dest[4 * x + 2] = 0;
470 dest[4 * x + 3] = source[x];
471 }
472 }
473}
474
475void Image::loadLuminanceData(GLsizei width, GLsizei height,
476 int inputPitch, const void *input, size_t outputPitch, void *output, bool native) const
477{
478 const unsigned char *source = NULL;
479 unsigned char *dest = NULL;
480
481 for (int y = 0; y < height; y++)
482 {
483 source = static_cast<const unsigned char*>(input) + y * inputPitch;
484 dest = static_cast<unsigned char*>(output) + y * outputPitch;
485
486 if (!native) // BGRA8 destination format
487 {
488 for (int x = 0; x < width; x++)
489 {
490 dest[4 * x + 0] = source[x];
491 dest[4 * x + 1] = source[x];
492 dest[4 * x + 2] = source[x];
493 dest[4 * x + 3] = 0xFF;
494 }
495 }
496 else // L8 destination format
497 {
498 memcpy(dest, source, width);
499 }
500 }
501}
502
503void Image::loadLuminanceFloatData(GLsizei width, GLsizei height,
504 int inputPitch, const void *input, size_t outputPitch, void *output) const
505{
506 const float *source = NULL;
507 float *dest = NULL;
508
509 for (int y = 0; y < height; y++)
510 {
511 source = reinterpret_cast<const float*>(static_cast<const unsigned char*>(input) + y * inputPitch);
512 dest = reinterpret_cast<float*>(static_cast<unsigned char*>(output) + y * outputPitch);
513 for (int x = 0; x < width; x++)
514 {
515 dest[4 * x + 0] = source[x];
516 dest[4 * x + 1] = source[x];
517 dest[4 * x + 2] = source[x];
518 dest[4 * x + 3] = 1.0f;
519 }
520 }
521}
522
523void Image::loadLuminanceHalfFloatData(GLsizei width, GLsizei height,
524 int inputPitch, const void *input, size_t outputPitch, void *output) const
525{
526 const unsigned short *source = NULL;
527 unsigned short *dest = NULL;
528
529 for (int y = 0; y < height; y++)
530 {
531 source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
532 dest = reinterpret_cast<unsigned short*>(static_cast<unsigned char*>(output) + y * outputPitch);
533 for (int x = 0; x < width; x++)
534 {
535 dest[4 * x + 0] = source[x];
536 dest[4 * x + 1] = source[x];
537 dest[4 * x + 2] = source[x];
538 dest[4 * x + 3] = 0x3C00; // SEEEEEMMMMMMMMMM, S = 0, E = 15, M = 0: 16bit flpt representation of 1
539 }
540 }
541}
542
543void Image::loadLuminanceAlphaData(GLsizei width, GLsizei height,
544 int inputPitch, const void *input, size_t outputPitch, void *output, bool native) const
545{
546 const unsigned char *source = NULL;
547 unsigned char *dest = NULL;
548
549 for (int y = 0; y < height; y++)
550 {
551 source = static_cast<const unsigned char*>(input) + y * inputPitch;
552 dest = static_cast<unsigned char*>(output) + y * outputPitch;
553
554 if (!native) // BGRA8 destination format
555 {
556 for (int x = 0; x < width; x++)
557 {
558 dest[4 * x + 0] = source[2*x+0];
559 dest[4 * x + 1] = source[2*x+0];
560 dest[4 * x + 2] = source[2*x+0];
561 dest[4 * x + 3] = source[2*x+1];
562 }
563 }
564 else
565 {
566 memcpy(dest, source, width * 2);
567 }
568 }
569}
570
571void Image::loadLuminanceAlphaFloatData(GLsizei width, GLsizei height,
572 int inputPitch, const void *input, size_t outputPitch, void *output) const
573{
574 const float *source = NULL;
575 float *dest = NULL;
576
577 for (int y = 0; y < height; y++)
578 {
579 source = reinterpret_cast<const float*>(static_cast<const unsigned char*>(input) + y * inputPitch);
580 dest = reinterpret_cast<float*>(static_cast<unsigned char*>(output) + y * outputPitch);
581 for (int x = 0; x < width; x++)
582 {
583 dest[4 * x + 0] = source[2*x+0];
584 dest[4 * x + 1] = source[2*x+0];
585 dest[4 * x + 2] = source[2*x+0];
586 dest[4 * x + 3] = source[2*x+1];
587 }
588 }
589}
590
591void Image::loadLuminanceAlphaHalfFloatData(GLsizei width, GLsizei height,
592 int inputPitch, const void *input, size_t outputPitch, void *output) const
593{
594 const unsigned short *source = NULL;
595 unsigned short *dest = NULL;
596
597 for (int y = 0; y < height; y++)
598 {
599 source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
600 dest = reinterpret_cast<unsigned short*>(static_cast<unsigned char*>(output) + y * outputPitch);
601 for (int x = 0; x < width; x++)
602 {
603 dest[4 * x + 0] = source[2*x+0];
604 dest[4 * x + 1] = source[2*x+0];
605 dest[4 * x + 2] = source[2*x+0];
606 dest[4 * x + 3] = source[2*x+1];
607 }
608 }
609}
610
611void Image::loadRGBUByteData(GLsizei width, GLsizei height,
612 int inputPitch, const void *input, size_t outputPitch, void *output) const
613{
614 const unsigned char *source = NULL;
615 unsigned char *dest = NULL;
616
617 for (int y = 0; y < height; y++)
618 {
619 source = static_cast<const unsigned char*>(input) + y * inputPitch;
620 dest = static_cast<unsigned char*>(output) + y * outputPitch;
621 for (int x = 0; x < width; x++)
622 {
623 dest[4 * x + 0] = source[x * 3 + 2];
624 dest[4 * x + 1] = source[x * 3 + 1];
625 dest[4 * x + 2] = source[x * 3 + 0];
626 dest[4 * x + 3] = 0xFF;
627 }
628 }
629}
630
631void Image::loadRGB565Data(GLsizei width, GLsizei height,
632 int inputPitch, const void *input, size_t outputPitch, void *output) const
633{
634 const unsigned short *source = NULL;
635 unsigned char *dest = NULL;
636
637 for (int y = 0; y < height; y++)
638 {
639 source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
640 dest = static_cast<unsigned char*>(output) + y * outputPitch;
641 for (int x = 0; x < width; x++)
642 {
643 unsigned short rgba = source[x];
644 dest[4 * x + 0] = ((rgba & 0x001F) << 3) | ((rgba & 0x001F) >> 2);
645 dest[4 * x + 1] = ((rgba & 0x07E0) >> 3) | ((rgba & 0x07E0) >> 9);
646 dest[4 * x + 2] = ((rgba & 0xF800) >> 8) | ((rgba & 0xF800) >> 13);
647 dest[4 * x + 3] = 0xFF;
648 }
649 }
650}
651
652void Image::loadRGBFloatData(GLsizei width, GLsizei height,
653 int inputPitch, const void *input, size_t outputPitch, void *output) const
654{
655 const float *source = NULL;
656 float *dest = NULL;
657
658 for (int y = 0; y < height; y++)
659 {
660 source = reinterpret_cast<const float*>(static_cast<const unsigned char*>(input) + y * inputPitch);
661 dest = reinterpret_cast<float*>(static_cast<unsigned char*>(output) + y * outputPitch);
662 for (int x = 0; x < width; x++)
663 {
664 dest[4 * x + 0] = source[x * 3 + 0];
665 dest[4 * x + 1] = source[x * 3 + 1];
666 dest[4 * x + 2] = source[x * 3 + 2];
667 dest[4 * x + 3] = 1.0f;
668 }
669 }
670}
671
672void Image::loadRGBHalfFloatData(GLsizei width, GLsizei height,
673 int inputPitch, const void *input, size_t outputPitch, void *output) const
674{
675 const unsigned short *source = NULL;
676 unsigned short *dest = NULL;
677
678 for (int y = 0; y < height; y++)
679 {
680 source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
681 dest = reinterpret_cast<unsigned short*>(static_cast<unsigned char*>(output) + y * outputPitch);
682 for (int x = 0; x < width; x++)
683 {
684 dest[4 * x + 0] = source[x * 3 + 0];
685 dest[4 * x + 1] = source[x * 3 + 1];
686 dest[4 * x + 2] = source[x * 3 + 2];
687 dest[4 * x + 3] = 0x3C00; // SEEEEEMMMMMMMMMM, S = 0, E = 15, M = 0: 16bit flpt representation of 1
688 }
689 }
690}
691
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000692void Image::loadRGBAUByteData(GLsizei width, GLsizei height,
693 int inputPitch, const void *input, size_t outputPitch, void *output) const
694{
695 const unsigned int *source = NULL;
696 unsigned int *dest = NULL;
697 for (int y = 0; y < height; y++)
698 {
699 source = reinterpret_cast<const unsigned int*>(static_cast<const unsigned char*>(input) + y * inputPitch);
700 dest = reinterpret_cast<unsigned int*>(static_cast<unsigned char*>(output) + y * outputPitch);
701
702 for (int x = 0; x < width; x++)
703 {
704 unsigned int rgba = source[x];
705 dest[x] = (_rotl(rgba, 16) & 0x00ff00ff) | (rgba & 0xff00ff00);
706 }
707 }
708}
709
710void Image::loadRGBA4444Data(GLsizei width, GLsizei height,
711 int inputPitch, const void *input, size_t outputPitch, void *output) const
712{
713 const unsigned short *source = NULL;
714 unsigned char *dest = NULL;
715
716 for (int y = 0; y < height; y++)
717 {
718 source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
719 dest = static_cast<unsigned char*>(output) + y * outputPitch;
720 for (int x = 0; x < width; x++)
721 {
722 unsigned short rgba = source[x];
723 dest[4 * x + 0] = ((rgba & 0x00F0) << 0) | ((rgba & 0x00F0) >> 4);
724 dest[4 * x + 1] = ((rgba & 0x0F00) >> 4) | ((rgba & 0x0F00) >> 8);
725 dest[4 * x + 2] = ((rgba & 0xF000) >> 8) | ((rgba & 0xF000) >> 12);
726 dest[4 * x + 3] = ((rgba & 0x000F) << 4) | ((rgba & 0x000F) >> 0);
727 }
728 }
729}
730
731void Image::loadRGBA5551Data(GLsizei width, GLsizei height,
732 int inputPitch, const void *input, size_t outputPitch, void *output) const
733{
734 const unsigned short *source = NULL;
735 unsigned char *dest = NULL;
736
737 for (int y = 0; y < height; y++)
738 {
739 source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
740 dest = static_cast<unsigned char*>(output) + y * outputPitch;
741 for (int x = 0; x < width; x++)
742 {
743 unsigned short rgba = source[x];
744 dest[4 * x + 0] = ((rgba & 0x003E) << 2) | ((rgba & 0x003E) >> 3);
745 dest[4 * x + 1] = ((rgba & 0x07C0) >> 3) | ((rgba & 0x07C0) >> 8);
746 dest[4 * x + 2] = ((rgba & 0xF800) >> 8) | ((rgba & 0xF800) >> 13);
747 dest[4 * x + 3] = (rgba & 0x0001) ? 0xFF : 0;
748 }
749 }
750}
751
752void Image::loadRGBAFloatData(GLsizei width, GLsizei height,
753 int inputPitch, const void *input, size_t outputPitch, void *output) const
754{
755 const float *source = NULL;
756 float *dest = NULL;
757
758 for (int y = 0; y < height; y++)
759 {
760 source = reinterpret_cast<const float*>(static_cast<const unsigned char*>(input) + y * inputPitch);
761 dest = reinterpret_cast<float*>(static_cast<unsigned char*>(output) + y * outputPitch);
762 memcpy(dest, source, width * 16);
763 }
764}
765
766void Image::loadRGBAHalfFloatData(GLsizei width, GLsizei height,
767 int inputPitch, const void *input, size_t outputPitch, void *output) const
768{
769 const unsigned char *source = NULL;
770 unsigned char *dest = NULL;
771
772 for (int y = 0; y < height; y++)
773 {
774 source = static_cast<const unsigned char*>(input) + y * inputPitch;
775 dest = static_cast<unsigned char*>(output) + y * outputPitch;
776 memcpy(dest, source, width * 8);
777 }
778}
779
780void Image::loadBGRAData(GLsizei width, GLsizei height,
781 int inputPitch, const void *input, size_t outputPitch, void *output) const
782{
783 const unsigned char *source = NULL;
784 unsigned char *dest = NULL;
785
786 for (int y = 0; y < height; y++)
787 {
788 source = static_cast<const unsigned char*>(input) + y * inputPitch;
789 dest = static_cast<unsigned char*>(output) + y * outputPitch;
790 memcpy(dest, source, width*4);
791 }
792}
793
794void Image::loadCompressedData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
795 const void *input) {
796 ASSERT(xoffset % 4 == 0);
797 ASSERT(yoffset % 4 == 0);
798
799 RECT lockRect = {
800 xoffset, yoffset,
801 xoffset + width, yoffset + height
802 };
803
804 D3DLOCKED_RECT locked;
805 HRESULT result = lock(&locked, &lockRect);
806 if (FAILED(result))
807 {
808 return;
809 }
810
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000811 GLsizei inputSize = ComputeCompressedSize(width, height, mInternalFormat);
812 GLsizei inputPitch = ComputeCompressedPitch(width, mInternalFormat);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000813 int rows = inputSize / inputPitch;
814 for (int i = 0; i < rows; ++i)
815 {
816 memcpy((void*)((BYTE*)locked.pBits + i * locked.Pitch), (void*)((BYTE*)input + i * inputPitch), inputPitch);
817 }
818
819 unlock();
820}
821
822// This implements glCopyTex[Sub]Image2D for non-renderable internal texture formats and incomplete textures
823void Image::copy(GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, IDirect3DSurface9 *renderTarget)
824{
825 IDirect3DDevice9 *device = getDevice();
826 IDirect3DSurface9 *renderTargetData = NULL;
827 D3DSURFACE_DESC description;
828 renderTarget->GetDesc(&description);
829
830 HRESULT result = device->CreateOffscreenPlainSurface(description.Width, description.Height, description.Format, D3DPOOL_SYSTEMMEM, &renderTargetData, NULL);
831
832 if (FAILED(result))
833 {
834 ERR("Could not create matching destination surface.");
835 return error(GL_OUT_OF_MEMORY);
836 }
837
838 result = device->GetRenderTargetData(renderTarget, renderTargetData);
839
840 if (FAILED(result))
841 {
842 ERR("GetRenderTargetData unexpectedly failed.");
843 renderTargetData->Release();
844 return error(GL_OUT_OF_MEMORY);
845 }
846
847 RECT sourceRect = {x, y, x + width, y + height};
848 RECT destRect = {xoffset, yoffset, xoffset + width, yoffset + height};
849
850 if (isRenderableFormat())
851 {
852 result = D3DXLoadSurfaceFromSurface(getSurface(), NULL, &destRect, renderTargetData, NULL, &sourceRect, D3DX_FILTER_BOX, 0);
853
854 if (FAILED(result))
855 {
856 ERR("Copying surfaces unexpectedly failed.");
857 renderTargetData->Release();
858 return error(GL_OUT_OF_MEMORY);
859 }
860 }
861 else
862 {
863 D3DLOCKED_RECT sourceLock = {0};
864 result = renderTargetData->LockRect(&sourceLock, &sourceRect, 0);
865
866 if (FAILED(result))
867 {
868 ERR("Failed to lock the source surface (rectangle might be invalid).");
869 renderTargetData->Release();
870 return error(GL_OUT_OF_MEMORY);
871 }
872
873 D3DLOCKED_RECT destLock = {0};
874 result = lock(&destLock, &destRect);
875
876 if (FAILED(result))
877 {
878 ERR("Failed to lock the destination surface (rectangle might be invalid).");
879 renderTargetData->UnlockRect();
880 renderTargetData->Release();
881 return error(GL_OUT_OF_MEMORY);
882 }
883
884 if (destLock.pBits && sourceLock.pBits)
885 {
886 unsigned char *source = (unsigned char*)sourceLock.pBits;
887 unsigned char *dest = (unsigned char*)destLock.pBits;
888
889 switch (description.Format)
890 {
891 case D3DFMT_X8R8G8B8:
892 case D3DFMT_A8R8G8B8:
893 switch(getD3DFormat())
894 {
895 case D3DFMT_L8:
896 for(int y = 0; y < height; y++)
897 {
898 for(int x = 0; x < width; x++)
899 {
900 dest[x] = source[x * 4 + 2];
901 }
902
903 source += sourceLock.Pitch;
904 dest += destLock.Pitch;
905 }
906 break;
907 case D3DFMT_A8L8:
908 for(int y = 0; y < height; y++)
909 {
910 for(int x = 0; x < width; x++)
911 {
912 dest[x * 2 + 0] = source[x * 4 + 2];
913 dest[x * 2 + 1] = source[x * 4 + 3];
914 }
915
916 source += sourceLock.Pitch;
917 dest += destLock.Pitch;
918 }
919 break;
920 default:
921 UNREACHABLE();
922 }
923 break;
924 case D3DFMT_R5G6B5:
925 switch(getD3DFormat())
926 {
927 case D3DFMT_L8:
928 for(int y = 0; y < height; y++)
929 {
930 for(int x = 0; x < width; x++)
931 {
932 unsigned char red = source[x * 2 + 1] & 0xF8;
933 dest[x] = red | (red >> 5);
934 }
935
936 source += sourceLock.Pitch;
937 dest += destLock.Pitch;
938 }
939 break;
940 default:
941 UNREACHABLE();
942 }
943 break;
944 case D3DFMT_A1R5G5B5:
945 switch(getD3DFormat())
946 {
947 case D3DFMT_L8:
948 for(int y = 0; y < height; y++)
949 {
950 for(int x = 0; x < width; x++)
951 {
952 unsigned char red = source[x * 2 + 1] & 0x7C;
953 dest[x] = (red << 1) | (red >> 4);
954 }
955
956 source += sourceLock.Pitch;
957 dest += destLock.Pitch;
958 }
959 break;
960 case D3DFMT_A8L8:
961 for(int y = 0; y < height; y++)
962 {
963 for(int x = 0; x < width; x++)
964 {
965 unsigned char red = source[x * 2 + 1] & 0x7C;
966 dest[x * 2 + 0] = (red << 1) | (red >> 4);
967 dest[x * 2 + 1] = (signed char)source[x * 2 + 1] >> 7;
968 }
969
970 source += sourceLock.Pitch;
971 dest += destLock.Pitch;
972 }
973 break;
974 default:
975 UNREACHABLE();
976 }
977 break;
978 default:
979 UNREACHABLE();
980 }
981 }
982
983 unlock();
984 renderTargetData->UnlockRect();
985 }
986
987 renderTargetData->Release();
988
989 mDirty = true;
990}
991
992TextureStorage::TextureStorage(DWORD usage)
993 : mD3DUsage(usage),
994 mD3DPool(getDisplay()->getTexturePool(usage)),
jbauman@chromium.org68715282012-07-12 23:28:41 +0000995 mTextureSerial(issueTextureSerial()),
996 mLodOffset(0)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000997{
998}
999
1000TextureStorage::~TextureStorage()
1001{
1002}
1003
1004bool TextureStorage::isRenderTarget() const
1005{
1006 return (mD3DUsage & (D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL)) != 0;
1007}
1008
1009bool TextureStorage::isManaged() const
1010{
1011 return (mD3DPool == D3DPOOL_MANAGED);
1012}
1013
1014D3DPOOL TextureStorage::getPool() const
1015{
1016 return mD3DPool;
1017}
1018
1019DWORD TextureStorage::getUsage() const
1020{
1021 return mD3DUsage;
1022}
1023
1024unsigned int TextureStorage::getTextureSerial() const
1025{
1026 return mTextureSerial;
1027}
1028
1029unsigned int TextureStorage::issueTextureSerial()
1030{
1031 return mCurrentTextureSerial++;
1032}
1033
jbauman@chromium.org68715282012-07-12 23:28:41 +00001034int TextureStorage::getLodOffset() const
1035{
1036 return mLodOffset;
1037}
1038
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001039Texture::Texture(GLuint id) : RefCountObject(id)
1040{
1041 mMinFilter = GL_NEAREST_MIPMAP_LINEAR;
1042 mMagFilter = GL_LINEAR;
1043 mWrapS = GL_REPEAT;
1044 mWrapT = GL_REPEAT;
1045 mDirtyParameters = true;
1046 mUsage = GL_NONE;
daniel@transgaming.com07ab8412012-07-12 15:17:09 +00001047 mMaxAnisotropy = 1.0f;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001048
1049 mDirtyImages = true;
1050
1051 mImmutable = false;
1052}
1053
1054Texture::~Texture()
1055{
1056}
1057
1058// Returns true on successful filter state update (valid enum parameter)
1059bool Texture::setMinFilter(GLenum filter)
1060{
1061 switch (filter)
1062 {
1063 case GL_NEAREST:
1064 case GL_LINEAR:
1065 case GL_NEAREST_MIPMAP_NEAREST:
1066 case GL_LINEAR_MIPMAP_NEAREST:
1067 case GL_NEAREST_MIPMAP_LINEAR:
1068 case GL_LINEAR_MIPMAP_LINEAR:
1069 {
1070 if (mMinFilter != filter)
1071 {
1072 mMinFilter = filter;
1073 mDirtyParameters = true;
1074 }
1075 return true;
1076 }
1077 default:
1078 return false;
1079 }
1080}
1081
1082// Returns true on successful filter state update (valid enum parameter)
1083bool Texture::setMagFilter(GLenum filter)
1084{
1085 switch (filter)
1086 {
1087 case GL_NEAREST:
1088 case GL_LINEAR:
1089 {
1090 if (mMagFilter != filter)
1091 {
1092 mMagFilter = filter;
1093 mDirtyParameters = true;
1094 }
1095 return true;
1096 }
1097 default:
1098 return false;
1099 }
1100}
1101
1102// Returns true on successful wrap state update (valid enum parameter)
1103bool Texture::setWrapS(GLenum wrap)
1104{
1105 switch (wrap)
1106 {
1107 case GL_REPEAT:
1108 case GL_CLAMP_TO_EDGE:
1109 case GL_MIRRORED_REPEAT:
1110 {
1111 if (mWrapS != wrap)
1112 {
1113 mWrapS = wrap;
1114 mDirtyParameters = true;
1115 }
1116 return true;
1117 }
1118 default:
1119 return false;
1120 }
1121}
1122
1123// Returns true on successful wrap state update (valid enum parameter)
1124bool Texture::setWrapT(GLenum wrap)
1125{
1126 switch (wrap)
1127 {
1128 case GL_REPEAT:
1129 case GL_CLAMP_TO_EDGE:
1130 case GL_MIRRORED_REPEAT:
1131 {
1132 if (mWrapT != wrap)
1133 {
1134 mWrapT = wrap;
1135 mDirtyParameters = true;
1136 }
1137 return true;
1138 }
1139 default:
1140 return false;
1141 }
1142}
1143
daniel@transgaming.com07ab8412012-07-12 15:17:09 +00001144// Returns true on successful max anisotropy update (valid anisotropy value)
1145bool Texture::setMaxAnisotropy(float textureMaxAnisotropy, float contextMaxAnisotropy)
1146{
1147 textureMaxAnisotropy = std::min(textureMaxAnisotropy, contextMaxAnisotropy);
1148 if (textureMaxAnisotropy < 1.0f)
1149 {
1150 return false;
1151 }
1152 if (mMaxAnisotropy != textureMaxAnisotropy)
1153 {
1154 mMaxAnisotropy = textureMaxAnisotropy;
1155 mDirtyParameters = true;
1156 }
1157 return true;
1158}
1159
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001160// Returns true on successful usage state update (valid enum parameter)
1161bool Texture::setUsage(GLenum usage)
1162{
1163 switch (usage)
1164 {
1165 case GL_NONE:
1166 case GL_FRAMEBUFFER_ATTACHMENT_ANGLE:
1167 mUsage = usage;
1168 return true;
1169 default:
1170 return false;
1171 }
1172}
1173
1174GLenum Texture::getMinFilter() const
1175{
1176 return mMinFilter;
1177}
1178
1179GLenum Texture::getMagFilter() const
1180{
1181 return mMagFilter;
1182}
1183
1184GLenum Texture::getWrapS() const
1185{
1186 return mWrapS;
1187}
1188
1189GLenum Texture::getWrapT() const
1190{
1191 return mWrapT;
1192}
1193
daniel@transgaming.com07ab8412012-07-12 15:17:09 +00001194float Texture::getMaxAnisotropy() const
1195{
1196 return mMaxAnisotropy;
1197}
1198
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001199GLenum Texture::getUsage() const
1200{
1201 return mUsage;
1202}
1203
1204void Texture::setImage(GLint unpackAlignment, const void *pixels, Image *image)
1205{
1206 if (pixels != NULL)
1207 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001208 image->loadData(0, 0, image->getWidth(), image->getHeight(), unpackAlignment, pixels);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001209 mDirtyImages = true;
1210 }
1211}
1212
1213void Texture::setCompressedImage(GLsizei imageSize, const void *pixels, Image *image)
1214{
1215 if (pixels != NULL)
1216 {
1217 image->loadCompressedData(0, 0, image->getWidth(), image->getHeight(), pixels);
1218 mDirtyImages = true;
1219 }
1220}
1221
1222bool Texture::subImage(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, Image *image)
1223{
1224 if (pixels != NULL)
1225 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001226 image->loadData(xoffset, yoffset, width, height, unpackAlignment, pixels);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001227 mDirtyImages = true;
1228 }
1229
1230 return true;
1231}
1232
1233bool Texture::subImageCompressed(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels, Image *image)
1234{
1235 if (pixels != NULL)
1236 {
1237 image->loadCompressedData(xoffset, yoffset, width, height, pixels);
1238 mDirtyImages = true;
1239 }
1240
1241 return true;
1242}
1243
1244IDirect3DBaseTexture9 *Texture::getTexture()
1245{
1246 if (!isSamplerComplete())
1247 {
1248 return NULL;
1249 }
1250
1251 // ensure the underlying texture is created
1252 if (getStorage(false) == NULL)
1253 {
1254 return NULL;
1255 }
1256
1257 updateTexture();
1258
1259 return getBaseTexture();
1260}
1261
1262bool Texture::hasDirtyParameters() const
1263{
1264 return mDirtyParameters;
1265}
1266
1267bool Texture::hasDirtyImages() const
1268{
1269 return mDirtyImages;
1270}
1271
1272void Texture::resetDirty()
1273{
1274 mDirtyParameters = false;
1275 mDirtyImages = false;
1276}
1277
1278unsigned int Texture::getTextureSerial()
1279{
1280 TextureStorage *texture = getStorage(false);
1281 return texture ? texture->getTextureSerial() : 0;
1282}
1283
1284unsigned int Texture::getRenderTargetSerial(GLenum target)
1285{
1286 TextureStorage *texture = getStorage(true);
1287 return texture ? texture->getRenderTargetSerial(target) : 0;
1288}
1289
1290bool Texture::isImmutable() const
1291{
1292 return mImmutable;
1293}
1294
jbauman@chromium.org68715282012-07-12 23:28:41 +00001295int Texture::getLodOffset()
1296{
1297 TextureStorage *texture = getStorage(false);
1298 return texture ? texture->getLodOffset() : 0;
1299}
1300
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001301GLint Texture::creationLevels(GLsizei width, GLsizei height) const
1302{
1303 if ((isPow2(width) && isPow2(height)) || getContext()->supportsNonPower2Texture())
1304 {
1305 return 0; // Maximum number of levels
1306 }
1307 else
1308 {
1309 // OpenGL ES 2.0 without GL_OES_texture_npot does not permit NPOT mipmaps.
1310 return 1;
1311 }
1312}
1313
1314GLint Texture::creationLevels(GLsizei size) const
1315{
1316 return creationLevels(size, size);
1317}
1318
jbauman@chromium.org6bc4a142012-09-06 21:28:30 +00001319int Texture::levelCount()
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001320{
jbauman@chromium.org6bc4a142012-09-06 21:28:30 +00001321 return getBaseTexture() ? getBaseTexture()->GetLevelCount() - getLodOffset() : 0;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001322}
1323
1324Blit *Texture::getBlitter()
1325{
1326 Context *context = getContext();
1327 return context->getBlitter();
1328}
1329
1330bool Texture::copyToRenderTarget(IDirect3DSurface9 *dest, IDirect3DSurface9 *source, bool fromManaged)
1331{
1332 if (source && dest)
1333 {
1334 HRESULT result;
1335
1336 if (fromManaged)
1337 {
1338 result = D3DXLoadSurfaceFromSurface(dest, NULL, NULL, source, NULL, NULL, D3DX_FILTER_BOX, 0);
1339 }
1340 else
1341 {
1342 egl::Display *display = getDisplay();
1343 IDirect3DDevice9 *device = display->getDevice();
1344
1345 display->endScene();
1346 result = device->StretchRect(source, NULL, dest, NULL, D3DTEXF_NONE);
1347 }
1348
1349 if (FAILED(result))
1350 {
1351 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1352 return false;
1353 }
1354 }
1355
1356 return true;
1357}
1358
1359TextureStorage2D::TextureStorage2D(IDirect3DTexture9 *surfaceTexture) : TextureStorage(D3DUSAGE_RENDERTARGET), mRenderTargetSerial(RenderbufferStorage::issueSerial())
1360{
1361 mTexture = surfaceTexture;
1362}
1363
1364TextureStorage2D::TextureStorage2D(int levels, D3DFORMAT format, DWORD usage, int width, int height)
1365 : TextureStorage(usage), mRenderTargetSerial(RenderbufferStorage::issueSerial())
1366{
1367 mTexture = NULL;
1368 // if the width or height is not positive this should be treated as an incomplete texture
1369 // we handle that here by skipping the d3d texture creation
1370 if (width > 0 && height > 0)
1371 {
1372 IDirect3DDevice9 *device = getDevice();
jbauman@chromium.org68715282012-07-12 23:28:41 +00001373 MakeValidSize(false, dx2es::IsCompressedD3DFormat(format), &width, &height, &mLodOffset);
sminns@adobe.comce1189b2012-09-18 20:06:35 +00001374 HRESULT result = device->CreateTexture(width, height, levels ? levels + mLodOffset : 0, getUsage(), format, getPool(), &mTexture, NULL);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001375
1376 if (FAILED(result))
1377 {
1378 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1379 error(GL_OUT_OF_MEMORY);
1380 }
1381 }
1382}
1383
1384TextureStorage2D::~TextureStorage2D()
1385{
1386 if (mTexture)
1387 {
1388 mTexture->Release();
1389 }
1390}
1391
1392// Increments refcount on surface.
1393// caller must Release() the returned surface
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00001394IDirect3DSurface9 *TextureStorage2D::getSurfaceLevel(int level, bool dirty)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001395{
1396 IDirect3DSurface9 *surface = NULL;
1397
1398 if (mTexture)
1399 {
jbauman@chromium.org68715282012-07-12 23:28:41 +00001400 HRESULT result = mTexture->GetSurfaceLevel(level + mLodOffset, &surface);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001401 ASSERT(SUCCEEDED(result));
daniel@transgaming.com1ee986b2012-09-27 17:46:12 +00001402
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00001403 // With managed textures the driver needs to be informed of updates to the lower mipmap levels
1404 if (level != 0 && isManaged() && dirty)
daniel@transgaming.com1ee986b2012-09-27 17:46:12 +00001405 {
1406 mTexture->AddDirtyRect(NULL);
1407 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001408 }
1409
1410 return surface;
1411}
1412
1413IDirect3DBaseTexture9 *TextureStorage2D::getBaseTexture() const
1414{
1415 return mTexture;
1416}
1417
1418unsigned int TextureStorage2D::getRenderTargetSerial(GLenum target) const
1419{
1420 return mRenderTargetSerial;
1421}
1422
1423Texture2D::Texture2D(GLuint id) : Texture(id)
1424{
1425 mTexStorage = NULL;
1426 mSurface = NULL;
1427 mColorbufferProxy = NULL;
1428 mProxyRefs = 0;
1429}
1430
1431Texture2D::~Texture2D()
1432{
1433 mColorbufferProxy = NULL;
1434
1435 delete mTexStorage;
1436 mTexStorage = NULL;
1437
1438 if (mSurface)
1439 {
1440 mSurface->setBoundTexture(NULL);
1441 mSurface = NULL;
1442 }
1443}
1444
1445// We need to maintain a count of references to renderbuffers acting as
1446// proxies for this texture, so that we do not attempt to use a pointer
1447// to a renderbuffer proxy which has been deleted.
1448void Texture2D::addProxyRef(const Renderbuffer *proxy)
1449{
1450 mProxyRefs++;
1451}
1452
1453void Texture2D::releaseProxy(const Renderbuffer *proxy)
1454{
1455 if (mProxyRefs > 0)
1456 mProxyRefs--;
1457
1458 if (mProxyRefs == 0)
1459 mColorbufferProxy = NULL;
1460}
1461
1462GLenum Texture2D::getTarget() const
1463{
1464 return GL_TEXTURE_2D;
1465}
1466
1467GLsizei Texture2D::getWidth(GLint level) const
1468{
1469 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
1470 return mImageArray[level].getWidth();
1471 else
1472 return 0;
1473}
1474
1475GLsizei Texture2D::getHeight(GLint level) const
1476{
1477 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
1478 return mImageArray[level].getHeight();
1479 else
1480 return 0;
1481}
1482
1483GLenum Texture2D::getInternalFormat(GLint level) const
1484{
1485 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001486 return mImageArray[level].getInternalFormat();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001487 else
1488 return GL_NONE;
1489}
1490
1491D3DFORMAT Texture2D::getD3DFormat(GLint level) const
1492{
1493 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
1494 return mImageArray[level].getD3DFormat();
1495 else
1496 return D3DFMT_UNKNOWN;
1497}
1498
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001499void Texture2D::redefineImage(GLint level, GLint internalformat, GLsizei width, GLsizei height)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001500{
1501 releaseTexImage();
1502
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001503 bool redefined = mImageArray[level].redefine(internalformat, width, height, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001504
1505 if (mTexStorage && redefined)
1506 {
1507 for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
1508 {
1509 mImageArray[i].markDirty();
1510 }
1511
1512 delete mTexStorage;
1513 mTexStorage = NULL;
1514 mDirtyImages = true;
1515 }
1516}
1517
1518void Texture2D::setImage(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1519{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001520 GLint internalformat = ConvertSizedInternalFormat(format, type);
1521 redefineImage(level, internalformat, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001522
1523 Texture::setImage(unpackAlignment, pixels, &mImageArray[level]);
1524}
1525
1526void Texture2D::bindTexImage(egl::Surface *surface)
1527{
1528 releaseTexImage();
1529
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001530 GLint internalformat;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001531
1532 switch(surface->getFormat())
1533 {
1534 case D3DFMT_A8R8G8B8:
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001535 internalformat = GL_RGBA8_OES;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001536 break;
1537 case D3DFMT_X8R8G8B8:
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001538 internalformat = GL_RGB8_OES;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001539 break;
1540 default:
1541 UNIMPLEMENTED();
1542 return;
1543 }
1544
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001545 mImageArray[0].redefine(internalformat, surface->getWidth(), surface->getHeight(), true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001546
1547 delete mTexStorage;
1548 mTexStorage = new TextureStorage2D(surface->getOffscreenTexture());
1549
1550 mDirtyImages = true;
1551 mSurface = surface;
1552 mSurface->setBoundTexture(this);
1553}
1554
1555void Texture2D::releaseTexImage()
1556{
1557 if (mSurface)
1558 {
1559 mSurface->setBoundTexture(NULL);
1560 mSurface = NULL;
1561
1562 if (mTexStorage)
1563 {
1564 delete mTexStorage;
1565 mTexStorage = NULL;
1566 }
1567
1568 for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
1569 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001570 mImageArray[i].redefine(GL_RGBA8_OES, 0, 0, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001571 }
1572 }
1573}
1574
1575void Texture2D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
1576{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001577 // compressed formats don't have separate sized internal formats-- we can just use the compressed format directly
1578 redefineImage(level, format, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001579
1580 Texture::setCompressedImage(imageSize, pixels, &mImageArray[level]);
1581}
1582
1583void Texture2D::commitRect(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
1584{
1585 ASSERT(mImageArray[level].getSurface() != NULL);
1586
1587 if (level < levelCount())
1588 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00001589 IDirect3DSurface9 *destLevel = mTexStorage->getSurfaceLevel(level, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001590
1591 if (destLevel)
1592 {
1593 Image *image = &mImageArray[level];
1594 image->updateSurface(destLevel, xoffset, yoffset, width, height);
1595
1596 destLevel->Release();
1597 image->markClean();
1598 }
1599 }
1600}
1601
1602void Texture2D::subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1603{
1604 if (Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[level]))
1605 {
1606 commitRect(level, xoffset, yoffset, width, height);
1607 }
1608}
1609
1610void Texture2D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
1611{
1612 if (Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, &mImageArray[level]))
1613 {
1614 commitRect(level, xoffset, yoffset, width, height);
1615 }
1616}
1617
1618void Texture2D::copyImage(GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
1619{
1620 IDirect3DSurface9 *renderTarget = source->getRenderTarget();
1621
1622 if (!renderTarget)
1623 {
1624 ERR("Failed to retrieve the render target.");
1625 return error(GL_OUT_OF_MEMORY);
1626 }
1627
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001628 GLint internalformat = ConvertSizedInternalFormat(format, GL_UNSIGNED_BYTE);
1629 redefineImage(level, internalformat, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001630
1631 if (!mImageArray[level].isRenderableFormat())
1632 {
1633 mImageArray[level].copy(0, 0, x, y, width, height, renderTarget);
1634 mDirtyImages = true;
1635 }
1636 else
1637 {
1638 if (!mTexStorage || !mTexStorage->isRenderTarget())
1639 {
1640 convertToRenderTarget();
1641 }
1642
1643 mImageArray[level].markClean();
1644
1645 if (width != 0 && height != 0 && level < levelCount())
1646 {
1647 RECT sourceRect;
1648 sourceRect.left = x;
1649 sourceRect.right = x + width;
1650 sourceRect.top = y;
1651 sourceRect.bottom = y + height;
1652
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00001653 IDirect3DSurface9 *dest = mTexStorage->getSurfaceLevel(level, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001654
1655 if (dest)
1656 {
1657 getBlitter()->copy(renderTarget, sourceRect, format, 0, 0, dest);
1658 dest->Release();
1659 }
1660 }
1661 }
1662
1663 renderTarget->Release();
1664}
1665
1666void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
1667{
1668 if (xoffset + width > mImageArray[level].getWidth() || yoffset + height > mImageArray[level].getHeight())
1669 {
1670 return error(GL_INVALID_VALUE);
1671 }
1672
1673 IDirect3DSurface9 *renderTarget = source->getRenderTarget();
1674
1675 if (!renderTarget)
1676 {
1677 ERR("Failed to retrieve the render target.");
1678 return error(GL_OUT_OF_MEMORY);
1679 }
1680
1681 if (!mImageArray[level].isRenderableFormat() || (!mTexStorage && !isSamplerComplete()))
1682 {
1683 mImageArray[level].copy(xoffset, yoffset, x, y, width, height, renderTarget);
1684 mDirtyImages = true;
1685 }
1686 else
1687 {
1688 if (!mTexStorage || !mTexStorage->isRenderTarget())
1689 {
1690 convertToRenderTarget();
1691 }
1692
1693 updateTexture();
1694
1695 if (level < levelCount())
1696 {
1697 RECT sourceRect;
1698 sourceRect.left = x;
1699 sourceRect.right = x + width;
1700 sourceRect.top = y;
1701 sourceRect.bottom = y + height;
1702
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00001703 IDirect3DSurface9 *dest = mTexStorage->getSurfaceLevel(level, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001704
1705 if (dest)
1706 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001707 getBlitter()->copy(renderTarget, sourceRect,
1708 gl::ExtractFormat(mImageArray[0].getInternalFormat()),
1709 xoffset, yoffset, dest);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001710 dest->Release();
1711 }
1712 }
1713 }
1714
1715 renderTarget->Release();
1716}
1717
1718void Texture2D::storage(GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height)
1719{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001720 D3DFORMAT d3dfmt = ConvertTextureFormatType(internalformat);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001721 DWORD d3dusage = GetTextureUsage(d3dfmt, mUsage, false);
1722
1723 delete mTexStorage;
1724 mTexStorage = new TextureStorage2D(levels, d3dfmt, d3dusage, width, height);
1725 mImmutable = true;
1726
1727 for (int level = 0; level < levels; level++)
1728 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001729 mImageArray[level].redefine(internalformat, width, height, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001730 width = std::max(1, width >> 1);
1731 height = std::max(1, height >> 1);
1732 }
1733
1734 for (int level = levels; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
1735 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001736 mImageArray[level].redefine(GL_NONE, 0, 0, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001737 }
1738
1739 if (mTexStorage->isManaged())
1740 {
1741 int levels = levelCount();
1742
1743 for (int level = 0; level < levels; level++)
1744 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00001745 IDirect3DSurface9 *surface = mTexStorage->getSurfaceLevel(level, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001746 mImageArray[level].setManagedSurface(surface);
1747 }
1748 }
1749}
1750
1751// Tests for 2D texture sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 85.
1752bool Texture2D::isSamplerComplete() const
1753{
1754 GLsizei width = mImageArray[0].getWidth();
1755 GLsizei height = mImageArray[0].getHeight();
1756
1757 if (width <= 0 || height <= 0)
1758 {
1759 return false;
1760 }
1761
1762 bool mipmapping = false;
1763
1764 switch (mMinFilter)
1765 {
1766 case GL_NEAREST:
1767 case GL_LINEAR:
1768 mipmapping = false;
1769 break;
1770 case GL_NEAREST_MIPMAP_NEAREST:
1771 case GL_LINEAR_MIPMAP_NEAREST:
1772 case GL_NEAREST_MIPMAP_LINEAR:
1773 case GL_LINEAR_MIPMAP_LINEAR:
1774 mipmapping = true;
1775 break;
1776 default: UNREACHABLE();
1777 }
1778
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001779 if ((gl::ExtractType(getInternalFormat(0)) == GL_FLOAT && !getContext()->supportsFloat32LinearFilter()) ||
1780 (gl::ExtractType(getInternalFormat(0)) == GL_HALF_FLOAT_OES && !getContext()->supportsFloat16LinearFilter()))
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001781 {
1782 if (mMagFilter != GL_NEAREST || (mMinFilter != GL_NEAREST && mMinFilter != GL_NEAREST_MIPMAP_NEAREST))
1783 {
1784 return false;
1785 }
1786 }
1787
1788 bool npotSupport = getContext()->supportsNonPower2Texture();
1789
1790 if (!npotSupport)
1791 {
1792 if ((getWrapS() != GL_CLAMP_TO_EDGE && !isPow2(width)) ||
1793 (getWrapT() != GL_CLAMP_TO_EDGE && !isPow2(height)))
1794 {
1795 return false;
1796 }
1797 }
1798
1799 if (mipmapping)
1800 {
1801 if (!npotSupport)
1802 {
1803 if (!isPow2(width) || !isPow2(height))
1804 {
1805 return false;
1806 }
1807 }
1808
1809 if (!isMipmapComplete())
1810 {
1811 return false;
1812 }
1813 }
1814
1815 return true;
1816}
1817
1818// Tests for 2D texture (mipmap) completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
1819bool Texture2D::isMipmapComplete() const
1820{
1821 if (isImmutable())
1822 {
1823 return true;
1824 }
1825
1826 GLsizei width = mImageArray[0].getWidth();
1827 GLsizei height = mImageArray[0].getHeight();
1828
1829 if (width <= 0 || height <= 0)
1830 {
1831 return false;
1832 }
1833
1834 int q = log2(std::max(width, height));
1835
1836 for (int level = 1; level <= q; level++)
1837 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001838 if (mImageArray[level].getInternalFormat() != mImageArray[0].getInternalFormat())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001839 {
1840 return false;
1841 }
1842
1843 if (mImageArray[level].getWidth() != std::max(1, width >> level))
1844 {
1845 return false;
1846 }
1847
1848 if (mImageArray[level].getHeight() != std::max(1, height >> level))
1849 {
1850 return false;
1851 }
1852 }
1853
1854 return true;
1855}
1856
1857bool Texture2D::isCompressed(GLint level) const
1858{
1859 return IsCompressed(getInternalFormat(level));
1860}
1861
1862bool Texture2D::isDepth(GLint level) const
1863{
1864 return IsDepthTexture(getInternalFormat(level));
1865}
1866
1867IDirect3DBaseTexture9 *Texture2D::getBaseTexture() const
1868{
1869 return mTexStorage ? mTexStorage->getBaseTexture() : NULL;
1870}
1871
1872// Constructs a Direct3D 9 texture resource from the texture images
1873void Texture2D::createTexture()
1874{
1875 GLsizei width = mImageArray[0].getWidth();
1876 GLsizei height = mImageArray[0].getHeight();
daniel@transgaming.come6a09842012-09-17 21:28:55 +00001877
1878 if (!(width > 0 && height > 0))
1879 return; // do not attempt to create d3d textures for nonexistant data
1880
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001881 GLint levels = creationLevels(width, height);
1882 D3DFORMAT d3dfmt = mImageArray[0].getD3DFormat();
1883 DWORD d3dusage = GetTextureUsage(d3dfmt, mUsage, false);
1884
1885 delete mTexStorage;
1886 mTexStorage = new TextureStorage2D(levels, d3dfmt, d3dusage, width, height);
1887
1888 if (mTexStorage->isManaged())
1889 {
1890 int levels = levelCount();
1891
1892 for (int level = 0; level < levels; level++)
1893 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00001894 IDirect3DSurface9 *surface = mTexStorage->getSurfaceLevel(level, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001895 mImageArray[level].setManagedSurface(surface);
1896 }
1897 }
1898
1899 mDirtyImages = true;
1900}
1901
1902void Texture2D::updateTexture()
1903{
1904 int levels = levelCount();
1905
1906 for (int level = 0; level < levels; level++)
1907 {
1908 Image *image = &mImageArray[level];
1909
1910 if (image->isDirty())
1911 {
1912 commitRect(level, 0, 0, mImageArray[level].getWidth(), mImageArray[level].getHeight());
1913 }
1914 }
1915}
1916
1917void Texture2D::convertToRenderTarget()
1918{
1919 TextureStorage2D *newTexStorage = NULL;
1920
1921 if (mImageArray[0].getWidth() != 0 && mImageArray[0].getHeight() != 0)
1922 {
1923 GLsizei width = mImageArray[0].getWidth();
1924 GLsizei height = mImageArray[0].getHeight();
1925 GLint levels = creationLevels(width, height);
1926 D3DFORMAT d3dfmt = mImageArray[0].getD3DFormat();
1927 DWORD d3dusage = GetTextureUsage(d3dfmt, GL_FRAMEBUFFER_ATTACHMENT_ANGLE, true);
1928
1929 newTexStorage = new TextureStorage2D(levels, d3dfmt, d3dusage, width, height);
1930
1931 if (mTexStorage != NULL)
1932 {
1933 int levels = levelCount();
1934 for (int i = 0; i < levels; i++)
1935 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00001936 IDirect3DSurface9 *source = mTexStorage->getSurfaceLevel(i, false);
1937 IDirect3DSurface9 *dest = newTexStorage->getSurfaceLevel(i, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001938
1939 if (!copyToRenderTarget(dest, source, mTexStorage->isManaged()))
1940 {
1941 delete newTexStorage;
1942 if (source) source->Release();
1943 if (dest) dest->Release();
1944 return error(GL_OUT_OF_MEMORY);
1945 }
1946
1947 if (source) source->Release();
1948 if (dest) dest->Release();
1949 }
1950 }
1951 }
1952
1953 delete mTexStorage;
1954 mTexStorage = newTexStorage;
1955
1956 mDirtyImages = true;
1957}
1958
1959void Texture2D::generateMipmaps()
1960{
1961 if (!getContext()->supportsNonPower2Texture())
1962 {
1963 if (!isPow2(mImageArray[0].getWidth()) || !isPow2(mImageArray[0].getHeight()))
1964 {
1965 return error(GL_INVALID_OPERATION);
1966 }
1967 }
1968
1969 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
1970 unsigned int q = log2(std::max(mImageArray[0].getWidth(), mImageArray[0].getHeight()));
1971 for (unsigned int i = 1; i <= q; i++)
1972 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001973 redefineImage(i, mImageArray[0].getInternalFormat(),
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001974 std::max(mImageArray[0].getWidth() >> i, 1),
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001975 std::max(mImageArray[0].getHeight() >> i, 1));
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001976 }
1977
1978 if (mTexStorage && mTexStorage->isRenderTarget())
1979 {
1980 for (unsigned int i = 1; i <= q; i++)
1981 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00001982 IDirect3DSurface9 *upper = mTexStorage->getSurfaceLevel(i - 1, false);
1983 IDirect3DSurface9 *lower = mTexStorage->getSurfaceLevel(i, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001984
1985 if (upper != NULL && lower != NULL)
1986 {
1987 getBlitter()->boxFilter(upper, lower);
1988 }
1989
1990 if (upper != NULL) upper->Release();
1991 if (lower != NULL) lower->Release();
1992
1993 mImageArray[i].markClean();
1994 }
1995 }
1996 else
1997 {
1998 for (unsigned int i = 1; i <= q; i++)
1999 {
2000 if (mImageArray[i].getSurface() == NULL)
2001 {
2002 return error(GL_OUT_OF_MEMORY);
2003 }
2004
2005 if (FAILED(D3DXLoadSurfaceFromSurface(mImageArray[i].getSurface(), NULL, NULL, mImageArray[i - 1].getSurface(), NULL, NULL, D3DX_FILTER_BOX, 0)))
2006 {
2007 ERR(" failed to load filter %d to %d.", i - 1, i);
2008 }
2009
2010 mImageArray[i].markDirty();
2011 }
2012 }
2013}
2014
2015Renderbuffer *Texture2D::getRenderbuffer(GLenum target)
2016{
2017 if (target != GL_TEXTURE_2D)
2018 {
2019 return error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);
2020 }
2021
2022 if (mColorbufferProxy == NULL)
2023 {
2024 mColorbufferProxy = new Renderbuffer(id(), new RenderbufferTexture2D(this, target));
2025 }
2026
2027 return mColorbufferProxy;
2028}
2029
2030// Increments refcount on surface.
2031// caller must Release() the returned surface
2032IDirect3DSurface9 *Texture2D::getRenderTarget(GLenum target)
2033{
2034 ASSERT(target == GL_TEXTURE_2D);
2035
2036 // ensure the underlying texture is created
2037 if (getStorage(true) == NULL)
2038 {
2039 return NULL;
2040 }
2041
2042 updateTexture();
2043
2044 // ensure this is NOT a depth texture
2045 if (isDepth(0))
2046 {
2047 return NULL;
2048 }
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002049 return mTexStorage->getSurfaceLevel(0, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002050}
2051
2052// Increments refcount on surface.
2053// caller must Release() the returned surface
2054IDirect3DSurface9 *Texture2D::getDepthStencil(GLenum target)
2055{
2056 ASSERT(target == GL_TEXTURE_2D);
2057
2058 // ensure the underlying texture is created
2059 if (getStorage(true) == NULL)
2060 {
2061 return NULL;
2062 }
2063
2064 updateTexture();
2065
2066 // ensure this is actually a depth texture
2067 if (!isDepth(0))
2068 {
2069 return NULL;
2070 }
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002071 return mTexStorage->getSurfaceLevel(0, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002072}
2073
2074TextureStorage *Texture2D::getStorage(bool renderTarget)
2075{
2076 if (!mTexStorage || (renderTarget && !mTexStorage->isRenderTarget()))
2077 {
2078 if (renderTarget)
2079 {
2080 convertToRenderTarget();
2081 }
2082 else
2083 {
2084 createTexture();
2085 }
2086 }
2087
2088 return mTexStorage;
2089}
2090
2091TextureStorageCubeMap::TextureStorageCubeMap(int levels, D3DFORMAT format, DWORD usage, int size)
2092 : TextureStorage(usage), mFirstRenderTargetSerial(RenderbufferStorage::issueCubeSerials())
2093{
2094 mTexture = NULL;
2095 // if the size is not positive this should be treated as an incomplete texture
2096 // we handle that here by skipping the d3d texture creation
2097 if (size > 0)
2098 {
2099 IDirect3DDevice9 *device = getDevice();
jbauman@chromium.org68715282012-07-12 23:28:41 +00002100 int height = size;
2101 MakeValidSize(false, dx2es::IsCompressedD3DFormat(format), &size, &height, &mLodOffset);
sminns@adobe.comce1189b2012-09-18 20:06:35 +00002102 HRESULT result = device->CreateCubeTexture(size, levels ? levels + mLodOffset : 0, getUsage(), format, getPool(), &mTexture, NULL);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002103
2104 if (FAILED(result))
2105 {
2106 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
2107 error(GL_OUT_OF_MEMORY);
2108 }
2109 }
2110}
2111
2112TextureStorageCubeMap::~TextureStorageCubeMap()
2113{
2114 if (mTexture)
2115 {
2116 mTexture->Release();
2117 }
2118}
2119
2120// Increments refcount on surface.
2121// caller must Release() the returned surface
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002122IDirect3DSurface9 *TextureStorageCubeMap::getCubeMapSurface(GLenum faceTarget, int level, bool dirty)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002123{
2124 IDirect3DSurface9 *surface = NULL;
2125
2126 if (mTexture)
2127 {
daniel@transgaming.com1ee986b2012-09-27 17:46:12 +00002128 D3DCUBEMAP_FACES face = es2dx::ConvertCubeFace(faceTarget);
2129 HRESULT result = mTexture->GetCubeMapSurface(face, level + mLodOffset, &surface);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002130 ASSERT(SUCCEEDED(result));
daniel@transgaming.com1ee986b2012-09-27 17:46:12 +00002131
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002132 // With managed textures the driver needs to be informed of updates to the lower mipmap levels
2133 if (level != 0 && isManaged() && dirty)
daniel@transgaming.com1ee986b2012-09-27 17:46:12 +00002134 {
2135 mTexture->AddDirtyRect(face, NULL);
2136 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002137 }
2138
2139 return surface;
2140}
2141
2142IDirect3DBaseTexture9 *TextureStorageCubeMap::getBaseTexture() const
2143{
2144 return mTexture;
2145}
2146
2147unsigned int TextureStorageCubeMap::getRenderTargetSerial(GLenum target) const
2148{
2149 return mFirstRenderTargetSerial + TextureCubeMap::faceIndex(target);
2150}
2151
2152TextureCubeMap::TextureCubeMap(GLuint id) : Texture(id)
2153{
2154 mTexStorage = NULL;
2155 for (int i = 0; i < 6; i++)
2156 {
2157 mFaceProxies[i] = NULL;
2158 mFaceProxyRefs[i] = 0;
2159 }
2160}
2161
2162TextureCubeMap::~TextureCubeMap()
2163{
2164 for (int i = 0; i < 6; i++)
2165 {
2166 mFaceProxies[i] = NULL;
2167 }
2168
2169 delete mTexStorage;
2170 mTexStorage = NULL;
2171}
2172
2173// We need to maintain a count of references to renderbuffers acting as
2174// proxies for this texture, so that the texture is not deleted while
2175// proxy references still exist. If the reference count drops to zero,
2176// we set our proxy pointer NULL, so that a new attempt at referencing
2177// will cause recreation.
2178void TextureCubeMap::addProxyRef(const Renderbuffer *proxy)
2179{
2180 for (int i = 0; i < 6; i++)
2181 {
2182 if (mFaceProxies[i] == proxy)
2183 mFaceProxyRefs[i]++;
2184 }
2185}
2186
2187void TextureCubeMap::releaseProxy(const Renderbuffer *proxy)
2188{
2189 for (int i = 0; i < 6; i++)
2190 {
2191 if (mFaceProxies[i] == proxy)
2192 {
2193 if (mFaceProxyRefs[i] > 0)
2194 mFaceProxyRefs[i]--;
2195
2196 if (mFaceProxyRefs[i] == 0)
2197 mFaceProxies[i] = NULL;
2198 }
2199 }
2200}
2201
2202GLenum TextureCubeMap::getTarget() const
2203{
2204 return GL_TEXTURE_CUBE_MAP;
2205}
2206
2207GLsizei TextureCubeMap::getWidth(GLenum target, GLint level) const
2208{
2209 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
2210 return mImageArray[faceIndex(target)][level].getWidth();
2211 else
2212 return 0;
2213}
2214
2215GLsizei TextureCubeMap::getHeight(GLenum target, GLint level) const
2216{
2217 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
2218 return mImageArray[faceIndex(target)][level].getHeight();
2219 else
2220 return 0;
2221}
2222
2223GLenum TextureCubeMap::getInternalFormat(GLenum target, GLint level) const
2224{
2225 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002226 return mImageArray[faceIndex(target)][level].getInternalFormat();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002227 else
2228 return GL_NONE;
2229}
2230
2231D3DFORMAT TextureCubeMap::getD3DFormat(GLenum target, GLint level) const
2232{
2233 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
2234 return mImageArray[faceIndex(target)][level].getD3DFormat();
2235 else
2236 return D3DFMT_UNKNOWN;
2237}
2238
2239void TextureCubeMap::setImagePosX(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
2240{
2241 setImage(0, level, width, height, format, type, unpackAlignment, pixels);
2242}
2243
2244void TextureCubeMap::setImageNegX(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
2245{
2246 setImage(1, level, width, height, format, type, unpackAlignment, pixels);
2247}
2248
2249void TextureCubeMap::setImagePosY(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
2250{
2251 setImage(2, level, width, height, format, type, unpackAlignment, pixels);
2252}
2253
2254void TextureCubeMap::setImageNegY(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
2255{
2256 setImage(3, level, width, height, format, type, unpackAlignment, pixels);
2257}
2258
2259void TextureCubeMap::setImagePosZ(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
2260{
2261 setImage(4, level, width, height, format, type, unpackAlignment, pixels);
2262}
2263
2264void TextureCubeMap::setImageNegZ(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
2265{
2266 setImage(5, level, width, height, format, type, unpackAlignment, pixels);
2267}
2268
2269void TextureCubeMap::setCompressedImage(GLenum face, GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
2270{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002271 // compressed formats don't have separate sized internal formats-- we can just use the compressed format directly
2272 redefineImage(faceIndex(face), level, format, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002273
2274 Texture::setCompressedImage(imageSize, pixels, &mImageArray[faceIndex(face)][level]);
2275}
2276
2277void TextureCubeMap::commitRect(int face, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
2278{
2279 ASSERT(mImageArray[face][level].getSurface() != NULL);
2280
2281 if (level < levelCount())
2282 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002283 IDirect3DSurface9 *destLevel = mTexStorage->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, level, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002284 ASSERT(destLevel != NULL);
2285
2286 if (destLevel != NULL)
2287 {
2288 Image *image = &mImageArray[face][level];
2289 image->updateSurface(destLevel, xoffset, yoffset, width, height);
2290
2291 destLevel->Release();
2292 image->markClean();
2293 }
2294 }
2295}
2296
2297void TextureCubeMap::subImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
2298{
2299 if (Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[faceIndex(target)][level]))
2300 {
2301 commitRect(faceIndex(target), level, xoffset, yoffset, width, height);
2302 }
2303}
2304
2305void TextureCubeMap::subImageCompressed(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
2306{
2307 if (Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, &mImageArray[faceIndex(target)][level]))
2308 {
2309 commitRect(faceIndex(target), level, xoffset, yoffset, width, height);
2310 }
2311}
2312
2313// Tests for cube map sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 86.
2314bool TextureCubeMap::isSamplerComplete() const
2315{
2316 int size = mImageArray[0][0].getWidth();
2317
2318 bool mipmapping;
2319
2320 switch (mMinFilter)
2321 {
2322 case GL_NEAREST:
2323 case GL_LINEAR:
2324 mipmapping = false;
2325 break;
2326 case GL_NEAREST_MIPMAP_NEAREST:
2327 case GL_LINEAR_MIPMAP_NEAREST:
2328 case GL_NEAREST_MIPMAP_LINEAR:
2329 case GL_LINEAR_MIPMAP_LINEAR:
2330 mipmapping = true;
2331 break;
2332 default:
2333 UNREACHABLE();
2334 return false;
2335 }
2336
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002337 if ((gl::ExtractType(getInternalFormat(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0)) == GL_FLOAT && !getContext()->supportsFloat32LinearFilter()) ||
2338 (gl::ExtractType(getInternalFormat(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0) == GL_HALF_FLOAT_OES) && !getContext()->supportsFloat16LinearFilter()))
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002339 {
2340 if (mMagFilter != GL_NEAREST || (mMinFilter != GL_NEAREST && mMinFilter != GL_NEAREST_MIPMAP_NEAREST))
2341 {
2342 return false;
2343 }
2344 }
2345
2346 if (!isPow2(size) && !getContext()->supportsNonPower2Texture())
2347 {
2348 if (getWrapS() != GL_CLAMP_TO_EDGE || getWrapT() != GL_CLAMP_TO_EDGE || mipmapping)
2349 {
2350 return false;
2351 }
2352 }
2353
2354 if (!mipmapping)
2355 {
2356 if (!isCubeComplete())
2357 {
2358 return false;
2359 }
2360 }
2361 else
2362 {
2363 if (!isMipmapCubeComplete()) // Also tests for isCubeComplete()
2364 {
2365 return false;
2366 }
2367 }
2368
2369 return true;
2370}
2371
2372// Tests for cube texture completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
2373bool TextureCubeMap::isCubeComplete() const
2374{
2375 if (mImageArray[0][0].getWidth() <= 0 || mImageArray[0][0].getHeight() != mImageArray[0][0].getWidth())
2376 {
2377 return false;
2378 }
2379
2380 for (unsigned int face = 1; face < 6; face++)
2381 {
2382 if (mImageArray[face][0].getWidth() != mImageArray[0][0].getWidth() ||
2383 mImageArray[face][0].getWidth() != mImageArray[0][0].getHeight() ||
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002384 mImageArray[face][0].getInternalFormat() != mImageArray[0][0].getInternalFormat())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002385 {
2386 return false;
2387 }
2388 }
2389
2390 return true;
2391}
2392
2393bool TextureCubeMap::isMipmapCubeComplete() const
2394{
2395 if (isImmutable())
2396 {
2397 return true;
2398 }
2399
2400 if (!isCubeComplete())
2401 {
2402 return false;
2403 }
2404
2405 GLsizei size = mImageArray[0][0].getWidth();
2406
2407 int q = log2(size);
2408
2409 for (int face = 0; face < 6; face++)
2410 {
2411 for (int level = 1; level <= q; level++)
2412 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002413 if (mImageArray[face][level].getInternalFormat() != mImageArray[0][0].getInternalFormat())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002414 {
2415 return false;
2416 }
2417
2418 if (mImageArray[face][level].getWidth() != std::max(1, size >> level))
2419 {
2420 return false;
2421 }
2422 }
2423 }
2424
2425 return true;
2426}
2427
2428bool TextureCubeMap::isCompressed(GLenum target, GLint level) const
2429{
2430 return IsCompressed(getInternalFormat(target, level));
2431}
2432
2433IDirect3DBaseTexture9 *TextureCubeMap::getBaseTexture() const
2434{
2435 return mTexStorage ? mTexStorage->getBaseTexture() : NULL;
2436}
2437
2438// Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one
2439void TextureCubeMap::createTexture()
2440{
2441 GLsizei size = mImageArray[0][0].getWidth();
daniel@transgaming.come6a09842012-09-17 21:28:55 +00002442
2443 if (!(size > 0))
2444 return; // do not attempt to create d3d textures for nonexistant data
2445
sminns@adobe.comce1189b2012-09-18 20:06:35 +00002446 GLint levels = creationLevels(size);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002447 D3DFORMAT d3dfmt = mImageArray[0][0].getD3DFormat();
2448 DWORD d3dusage = GetTextureUsage(d3dfmt, mUsage, false);
2449
2450 delete mTexStorage;
2451 mTexStorage = new TextureStorageCubeMap(levels, d3dfmt, d3dusage, size);
2452
2453 if (mTexStorage->isManaged())
2454 {
2455 int levels = levelCount();
2456
2457 for (int face = 0; face < 6; face++)
2458 {
2459 for (int level = 0; level < levels; level++)
2460 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002461 IDirect3DSurface9 *surface = mTexStorage->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, level, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002462 mImageArray[face][level].setManagedSurface(surface);
2463 }
2464 }
2465 }
2466
2467 mDirtyImages = true;
2468}
2469
2470void TextureCubeMap::updateTexture()
2471{
2472 for (int face = 0; face < 6; face++)
2473 {
2474 int levels = levelCount();
2475 for (int level = 0; level < levels; level++)
2476 {
2477 Image *image = &mImageArray[face][level];
2478
2479 if (image->isDirty())
2480 {
2481 commitRect(face, level, 0, 0, image->getWidth(), image->getHeight());
2482 }
2483 }
2484 }
2485}
2486
2487void TextureCubeMap::convertToRenderTarget()
2488{
2489 TextureStorageCubeMap *newTexStorage = NULL;
2490
2491 if (mImageArray[0][0].getWidth() != 0)
2492 {
2493 GLsizei size = mImageArray[0][0].getWidth();
sminns@adobe.comce1189b2012-09-18 20:06:35 +00002494 GLint levels = creationLevels(size);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002495 D3DFORMAT d3dfmt = mImageArray[0][0].getD3DFormat();
2496 DWORD d3dusage = GetTextureUsage(d3dfmt, GL_FRAMEBUFFER_ATTACHMENT_ANGLE, true);
2497
2498 newTexStorage = new TextureStorageCubeMap(levels, d3dfmt, d3dusage, size);
2499
2500 if (mTexStorage != NULL)
2501 {
2502 int levels = levelCount();
2503 for (int f = 0; f < 6; f++)
2504 {
2505 for (int i = 0; i < levels; i++)
2506 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002507 IDirect3DSurface9 *source = mTexStorage->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + f, i, false);
2508 IDirect3DSurface9 *dest = newTexStorage->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + f, i, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002509
2510 if (!copyToRenderTarget(dest, source, mTexStorage->isManaged()))
2511 {
2512 delete newTexStorage;
2513 if (source) source->Release();
2514 if (dest) dest->Release();
2515 return error(GL_OUT_OF_MEMORY);
2516 }
2517
2518 if (source) source->Release();
2519 if (dest) dest->Release();
2520 }
2521 }
2522 }
2523 }
2524
2525 delete mTexStorage;
2526 mTexStorage = newTexStorage;
2527
2528 mDirtyImages = true;
2529}
2530
2531void TextureCubeMap::setImage(int faceIndex, GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
2532{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002533 GLint internalformat = ConvertSizedInternalFormat(format, type);
2534 redefineImage(faceIndex, level, internalformat, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002535
2536 Texture::setImage(unpackAlignment, pixels, &mImageArray[faceIndex][level]);
2537}
2538
2539unsigned int TextureCubeMap::faceIndex(GLenum face)
2540{
2541 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_X - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 1);
2542 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 2);
2543 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 3);
2544 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 4);
2545 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 5);
2546
2547 return face - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
2548}
2549
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002550void TextureCubeMap::redefineImage(int face, GLint level, GLint internalformat, GLsizei width, GLsizei height)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002551{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002552 bool redefined = mImageArray[face][level].redefine(internalformat, width, height, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002553
2554 if (mTexStorage && redefined)
2555 {
2556 for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
2557 {
2558 for (int f = 0; f < 6; f++)
2559 {
2560 mImageArray[f][i].markDirty();
2561 }
2562 }
2563
2564 delete mTexStorage;
2565 mTexStorage = NULL;
2566
2567 mDirtyImages = true;
2568 }
2569}
2570
2571void TextureCubeMap::copyImage(GLenum target, GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
2572{
2573 IDirect3DSurface9 *renderTarget = source->getRenderTarget();
2574
2575 if (!renderTarget)
2576 {
2577 ERR("Failed to retrieve the render target.");
2578 return error(GL_OUT_OF_MEMORY);
2579 }
2580
2581 unsigned int faceindex = faceIndex(target);
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002582 GLint internalformat = gl::ConvertSizedInternalFormat(format, GL_UNSIGNED_BYTE);
2583 redefineImage(faceindex, level, internalformat, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002584
2585 if (!mImageArray[faceindex][level].isRenderableFormat())
2586 {
2587 mImageArray[faceindex][level].copy(0, 0, x, y, width, height, renderTarget);
2588 mDirtyImages = true;
2589 }
2590 else
2591 {
2592 if (!mTexStorage || !mTexStorage->isRenderTarget())
2593 {
2594 convertToRenderTarget();
2595 }
2596
2597 mImageArray[faceindex][level].markClean();
2598
2599 ASSERT(width == height);
2600
2601 if (width > 0 && level < levelCount())
2602 {
2603 RECT sourceRect;
2604 sourceRect.left = x;
2605 sourceRect.right = x + width;
2606 sourceRect.top = y;
2607 sourceRect.bottom = y + height;
2608
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002609 IDirect3DSurface9 *dest = mTexStorage->getCubeMapSurface(target, level, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002610
2611 if (dest)
2612 {
2613 getBlitter()->copy(renderTarget, sourceRect, format, 0, 0, dest);
2614 dest->Release();
2615 }
2616 }
2617 }
2618
2619 renderTarget->Release();
2620}
2621
2622void TextureCubeMap::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
2623{
2624 GLsizei size = mImageArray[faceIndex(target)][level].getWidth();
2625
2626 if (xoffset + width > size || yoffset + height > size)
2627 {
2628 return error(GL_INVALID_VALUE);
2629 }
2630
2631 IDirect3DSurface9 *renderTarget = source->getRenderTarget();
2632
2633 if (!renderTarget)
2634 {
2635 ERR("Failed to retrieve the render target.");
2636 return error(GL_OUT_OF_MEMORY);
2637 }
2638
2639 unsigned int faceindex = faceIndex(target);
2640
2641 if (!mImageArray[faceindex][level].isRenderableFormat() || (!mTexStorage && !isSamplerComplete()))
2642 {
2643 mImageArray[faceindex][level].copy(0, 0, x, y, width, height, renderTarget);
2644 mDirtyImages = true;
2645 }
2646 else
2647 {
2648 if (!mTexStorage || !mTexStorage->isRenderTarget())
2649 {
2650 convertToRenderTarget();
2651 }
2652
2653 updateTexture();
2654
2655 if (level < levelCount())
2656 {
2657 RECT sourceRect;
2658 sourceRect.left = x;
2659 sourceRect.right = x + width;
2660 sourceRect.top = y;
2661 sourceRect.bottom = y + height;
2662
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002663 IDirect3DSurface9 *dest = mTexStorage->getCubeMapSurface(target, level, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002664
2665 if (dest)
2666 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002667 getBlitter()->copy(renderTarget, sourceRect, gl::ExtractFormat(mImageArray[0][0].getInternalFormat()), xoffset, yoffset, dest);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002668 dest->Release();
2669 }
2670 }
2671 }
2672
2673 renderTarget->Release();
2674}
2675
2676void TextureCubeMap::storage(GLsizei levels, GLenum internalformat, GLsizei size)
2677{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002678 D3DFORMAT d3dfmt = ConvertTextureFormatType(internalformat);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002679 DWORD d3dusage = GetTextureUsage(d3dfmt, mUsage, false);
2680
2681 delete mTexStorage;
2682 mTexStorage = new TextureStorageCubeMap(levels, d3dfmt, d3dusage, size);
2683 mImmutable = true;
2684
2685 for (int level = 0; level < levels; level++)
2686 {
2687 for (int face = 0; face < 6; face++)
2688 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002689 mImageArray[face][level].redefine(internalformat, size, size, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002690 size = std::max(1, size >> 1);
2691 }
2692 }
2693
2694 for (int level = levels; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
2695 {
2696 for (int face = 0; face < 6; face++)
2697 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002698 mImageArray[face][level].redefine(GL_NONE, 0, 0, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002699 }
2700 }
2701
2702 if (mTexStorage->isManaged())
2703 {
2704 int levels = levelCount();
2705
2706 for (int face = 0; face < 6; face++)
2707 {
2708 for (int level = 0; level < levels; level++)
2709 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002710 IDirect3DSurface9 *surface = mTexStorage->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, level, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002711 mImageArray[face][level].setManagedSurface(surface);
2712 }
2713 }
2714 }
2715}
2716
2717void TextureCubeMap::generateMipmaps()
2718{
2719 if (!isCubeComplete())
2720 {
2721 return error(GL_INVALID_OPERATION);
2722 }
2723
2724 if (!getContext()->supportsNonPower2Texture())
2725 {
2726 if (!isPow2(mImageArray[0][0].getWidth()))
2727 {
2728 return error(GL_INVALID_OPERATION);
2729 }
2730 }
2731
2732 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
2733 unsigned int q = log2(mImageArray[0][0].getWidth());
2734 for (unsigned int f = 0; f < 6; f++)
2735 {
2736 for (unsigned int i = 1; i <= q; i++)
2737 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002738 redefineImage(f, i, mImageArray[f][0].getInternalFormat(),
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002739 std::max(mImageArray[f][0].getWidth() >> i, 1),
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002740 std::max(mImageArray[f][0].getWidth() >> i, 1));
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002741 }
2742 }
2743
2744 if (mTexStorage && mTexStorage->isRenderTarget())
2745 {
2746 for (unsigned int f = 0; f < 6; f++)
2747 {
2748 for (unsigned int i = 1; i <= q; i++)
2749 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002750 IDirect3DSurface9 *upper = mTexStorage->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + f, i - 1, false);
2751 IDirect3DSurface9 *lower = mTexStorage->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + f, i, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002752
2753 if (upper != NULL && lower != NULL)
2754 {
2755 getBlitter()->boxFilter(upper, lower);
2756 }
2757
2758 if (upper != NULL) upper->Release();
2759 if (lower != NULL) lower->Release();
2760
2761 mImageArray[f][i].markClean();
2762 }
2763 }
2764 }
2765 else
2766 {
2767 for (unsigned int f = 0; f < 6; f++)
2768 {
2769 for (unsigned int i = 1; i <= q; i++)
2770 {
2771 if (mImageArray[f][i].getSurface() == NULL)
2772 {
2773 return error(GL_OUT_OF_MEMORY);
2774 }
2775
2776 if (FAILED(D3DXLoadSurfaceFromSurface(mImageArray[f][i].getSurface(), NULL, NULL, mImageArray[f][i - 1].getSurface(), NULL, NULL, D3DX_FILTER_BOX, 0)))
2777 {
2778 ERR(" failed to load filter %d to %d.", i - 1, i);
2779 }
2780
2781 mImageArray[f][i].markDirty();
2782 }
2783 }
2784 }
2785}
2786
2787Renderbuffer *TextureCubeMap::getRenderbuffer(GLenum target)
2788{
2789 if (!IsCubemapTextureTarget(target))
2790 {
2791 return error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);
2792 }
2793
2794 unsigned int face = faceIndex(target);
2795
2796 if (mFaceProxies[face] == NULL)
2797 {
2798 mFaceProxies[face] = new Renderbuffer(id(), new RenderbufferTextureCubeMap(this, target));
2799 }
2800
2801 return mFaceProxies[face];
2802}
2803
2804// Increments refcount on surface.
2805// caller must Release() the returned surface
2806IDirect3DSurface9 *TextureCubeMap::getRenderTarget(GLenum target)
2807{
2808 ASSERT(IsCubemapTextureTarget(target));
2809
2810 // ensure the underlying texture is created
2811 if (getStorage(true) == NULL)
2812 {
2813 return NULL;
2814 }
2815
2816 updateTexture();
2817
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002818 return mTexStorage->getCubeMapSurface(target, 0, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002819}
2820
2821TextureStorage *TextureCubeMap::getStorage(bool renderTarget)
2822{
2823 if (!mTexStorage || (renderTarget && !mTexStorage->isRenderTarget()))
2824 {
2825 if (renderTarget)
2826 {
2827 convertToRenderTarget();
2828 }
2829 else
2830 {
2831 createTexture();
2832 }
2833 }
2834
2835 return mTexStorage;
2836}
2837
2838}