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