blob: f66ef60e7c2a51cbde93128d349a5a70e82298b3 [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
daniel@transgaming.com4bb04be2012-10-17 18:29:55 +0000119static void MakeValidSize(bool isImage, bool isCompressed, GLsizei *requestWidth, GLsizei *requestHeight, int *levelOffset)
120{
jbauman@chromium.org68715282012-07-12 23:28:41 +0000121 int upsampleCount = 0;
122
123 if (isCompressed)
124 {
125 // Don't expand the size of full textures that are at least 4x4
126 // already.
127 if (isImage || *requestWidth < 4 || *requestHeight < 4)
128 {
129 while (*requestWidth % 4 != 0 || *requestHeight % 4 != 0)
130 {
131 *requestWidth <<= 1;
132 *requestHeight <<= 1;
133 upsampleCount++;
134 }
135 }
136 }
137 *levelOffset = upsampleCount;
138}
139
daniel@transgaming.com4bb04be2012-10-17 18:29:55 +0000140static void CopyLockableSurfaces(IDirect3DSurface9 *dest, IDirect3DSurface9 *source)
141{
142 D3DLOCKED_RECT sourceLock = {0};
143 D3DLOCKED_RECT destLock = {0};
144
145 source->LockRect(&sourceLock, NULL, 0);
146 dest->LockRect(&destLock, NULL, 0);
147
148 if (sourceLock.pBits && destLock.pBits)
149 {
150 D3DSURFACE_DESC desc;
151 source->GetDesc(&desc);
152
153 int rows = dx::IsCompressedFormat(desc.Format) ? desc.Height / 4 : desc.Height;
154 int bytes = dx::ComputeRowSize(desc.Format, desc.Width);
155 ASSERT(bytes <= sourceLock.Pitch && bytes <= destLock.Pitch);
156
157 for(int i = 0; i < rows; i++)
158 {
159 memcpy((char*)destLock.pBits + destLock.Pitch * i, (char*)sourceLock.pBits + sourceLock.Pitch * i, bytes);
160 }
161
162 source->UnlockRect();
163 dest->UnlockRect();
164 }
165 else UNREACHABLE();
166}
167
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000168Image::Image()
169{
170 mWidth = 0;
171 mHeight = 0;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000172 mInternalFormat = GL_NONE;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000173
174 mSurface = NULL;
175
176 mDirty = false;
177
178 mD3DPool = D3DPOOL_SYSTEMMEM;
179 mD3DFormat = D3DFMT_UNKNOWN;
180}
181
182Image::~Image()
183{
184 if (mSurface)
185 {
186 mSurface->Release();
187 }
188}
189
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000190bool Image::redefine(GLint internalformat, GLsizei width, GLsizei height, bool forceRelease)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000191{
192 if (mWidth != width ||
193 mHeight != height ||
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000194 mInternalFormat != internalformat ||
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000195 forceRelease)
196 {
197 mWidth = width;
198 mHeight = height;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000199 mInternalFormat = internalformat;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000200 // compute the d3d format that will be used
daniel@transgaming.com6b1a0a02012-10-17 18:22:47 +0000201 mD3DFormat = ConvertTextureInternalFormat(internalformat);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000202
203 if (mSurface)
204 {
205 mSurface->Release();
206 mSurface = NULL;
207 }
208
209 return true;
210 }
211
212 return false;
213}
214
215void Image::createSurface()
216{
217 if(mSurface)
218 {
219 return;
220 }
221
222 IDirect3DTexture9 *newTexture = NULL;
223 IDirect3DSurface9 *newSurface = NULL;
224 const D3DPOOL poolToUse = D3DPOOL_SYSTEMMEM;
225 const D3DFORMAT d3dFormat = getD3DFormat();
226 ASSERT(d3dFormat != D3DFMT_INTZ); // We should never get here for depth textures
227
228 if (mWidth != 0 && mHeight != 0)
229 {
230 int levelToFetch = 0;
231 GLsizei requestWidth = mWidth;
232 GLsizei requestHeight = mHeight;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000233 MakeValidSize(true, IsCompressed(mInternalFormat), &requestWidth, &requestHeight, &levelToFetch);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000234
235 HRESULT result = getDevice()->CreateTexture(requestWidth, requestHeight, levelToFetch + 1, NULL, d3dFormat,
236 poolToUse, &newTexture, NULL);
237
238 if (FAILED(result))
239 {
240 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
241 ERR("Creating image surface failed.");
242 return error(GL_OUT_OF_MEMORY);
243 }
244
245 newTexture->GetSurfaceLevel(levelToFetch, &newSurface);
246 newTexture->Release();
247 }
248
249 mSurface = newSurface;
250 mDirty = false;
251 mD3DPool = poolToUse;
252}
253
254HRESULT Image::lock(D3DLOCKED_RECT *lockedRect, const RECT *rect)
255{
256 createSurface();
257
258 HRESULT result = D3DERR_INVALIDCALL;
259
260 if (mSurface)
261 {
262 result = mSurface->LockRect(lockedRect, rect, 0);
263 ASSERT(SUCCEEDED(result));
264
265 mDirty = true;
266 }
267
268 return result;
269}
270
271void Image::unlock()
272{
273 if (mSurface)
274 {
275 HRESULT result = mSurface->UnlockRect();
276 ASSERT(SUCCEEDED(result));
277 }
278}
279
280bool Image::isRenderableFormat() const
281{
282 return IsTextureFormatRenderable(getD3DFormat());
283}
284
285D3DFORMAT Image::getD3DFormat() const
286{
287 // this should only happen if the image hasn't been redefined first
288 // which would be a bug by the caller
289 ASSERT(mD3DFormat != D3DFMT_UNKNOWN);
290
291 return mD3DFormat;
292}
293
294IDirect3DSurface9 *Image::getSurface()
295{
296 createSurface();
297
298 return mSurface;
299}
300
301void Image::setManagedSurface(IDirect3DSurface9 *surface)
302{
303 if (mSurface)
304 {
daniel@transgaming.com4bb04be2012-10-17 18:29:55 +0000305 CopyLockableSurfaces(surface, mSurface);
306
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000307 mSurface->Release();
308 }
309
310 D3DSURFACE_DESC desc;
311 surface->GetDesc(&desc);
312 ASSERT(desc.Pool == D3DPOOL_MANAGED);
313
314 mSurface = surface;
315 mD3DPool = desc.Pool;
316}
317
318void Image::updateSurface(IDirect3DSurface9 *destSurface, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
319{
320 IDirect3DSurface9 *sourceSurface = getSurface();
321
322 if (sourceSurface && sourceSurface != destSurface)
323 {
324 RECT rect;
325 rect.left = xoffset;
326 rect.top = yoffset;
327 rect.right = xoffset + width;
328 rect.bottom = yoffset + height;
329
daniel@transgaming.come0adbd82012-10-17 18:29:52 +0000330 // UpdateSurface: source must be SYSTEMMEM, dest must be DEFAULT pools
331 POINT point = {rect.left, rect.top};
332 HRESULT result = getDevice()->UpdateSurface(sourceSurface, &rect, destSurface, &point);
333 ASSERT(SUCCEEDED(result));
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000334 }
335}
336
337// Store the pixel rectangle designated by xoffset,yoffset,width,height with pixels stored as format/type at input
338// into the target pixel rectangle.
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000339void Image::loadData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000340 GLint unpackAlignment, const void *input)
341{
342 RECT lockRect =
343 {
344 xoffset, yoffset,
345 xoffset + width, yoffset + height
346 };
347
348 D3DLOCKED_RECT locked;
349 HRESULT result = lock(&locked, &lockRect);
350 if (FAILED(result))
351 {
352 return;
353 }
354
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000355
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000356 GLsizei inputPitch = ComputePitch(width, mInternalFormat, unpackAlignment);
357
358 switch (mInternalFormat)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000359 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000360 case GL_ALPHA8_EXT:
361 if (supportsSSE2())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000362 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000363 loadAlphaDataSSE2(width, height, inputPitch, input, locked.Pitch, locked.pBits);
364 }
365 else
366 {
367 loadAlphaData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000368 }
369 break;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000370 case GL_LUMINANCE8_EXT:
371 loadLuminanceData(width, height, inputPitch, input, locked.Pitch, locked.pBits, getD3DFormat() == D3DFMT_L8);
372 break;
373 case GL_ALPHA32F_EXT:
374 loadAlphaFloatData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
375 break;
376 case GL_LUMINANCE32F_EXT:
377 loadLuminanceFloatData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
378 break;
379 case GL_ALPHA16F_EXT:
380 loadAlphaHalfFloatData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
381 break;
382 case GL_LUMINANCE16F_EXT:
383 loadLuminanceHalfFloatData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
384 break;
385 case GL_LUMINANCE8_ALPHA8_EXT:
386 loadLuminanceAlphaData(width, height, inputPitch, input, locked.Pitch, locked.pBits, getD3DFormat() == D3DFMT_A8L8);
387 break;
388 case GL_LUMINANCE_ALPHA32F_EXT:
389 loadLuminanceAlphaFloatData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
390 break;
391 case GL_LUMINANCE_ALPHA16F_EXT:
392 loadLuminanceAlphaHalfFloatData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
393 break;
394 case GL_RGB8_OES:
395 loadRGBUByteData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
396 break;
397 case GL_RGB565:
398 loadRGB565Data(width, height, inputPitch, input, locked.Pitch, locked.pBits);
399 break;
400 case GL_RGBA8_OES:
401 if (supportsSSE2())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000402 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000403 loadRGBAUByteDataSSE2(width, height, inputPitch, input, locked.Pitch, locked.pBits);
404 }
405 else
406 {
407 loadRGBAUByteData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000408 }
409 break;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000410 case GL_RGBA4:
411 loadRGBA4444Data(width, height, inputPitch, input, locked.Pitch, locked.pBits);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000412 break;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000413 case GL_RGB5_A1:
414 loadRGBA5551Data(width, height, inputPitch, input, locked.Pitch, locked.pBits);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000415 break;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000416 case GL_BGRA8_EXT:
417 loadBGRAData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000418 break;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000419 // float textures are converted to RGBA, not BGRA, as they're stored that way in D3D
420 case GL_RGB32F_EXT:
421 loadRGBFloatData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000422 break;
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000423 case GL_RGB16F_EXT:
424 loadRGBHalfFloatData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
425 break;
426 case GL_RGBA32F_EXT:
427 loadRGBAFloatData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
428 break;
429 case GL_RGBA16F_EXT:
430 loadRGBAHalfFloatData(width, height, inputPitch, input, locked.Pitch, locked.pBits);
431 break;
432 default: UNREACHABLE();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000433 }
434
435 unlock();
436}
437
438void Image::loadAlphaData(GLsizei width, GLsizei height,
439 int inputPitch, const void *input, size_t outputPitch, void *output) const
440{
441 const unsigned char *source = NULL;
442 unsigned char *dest = NULL;
443
444 for (int y = 0; y < height; y++)
445 {
446 source = static_cast<const unsigned char*>(input) + y * inputPitch;
447 dest = static_cast<unsigned char*>(output) + y * outputPitch;
448 for (int x = 0; x < width; x++)
449 {
450 dest[4 * x + 0] = 0;
451 dest[4 * x + 1] = 0;
452 dest[4 * x + 2] = 0;
453 dest[4 * x + 3] = source[x];
454 }
455 }
456}
457
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000458void Image::loadAlphaFloatData(GLsizei width, GLsizei height,
459 int inputPitch, const void *input, size_t outputPitch, void *output) const
460{
461 const float *source = NULL;
462 float *dest = NULL;
463
464 for (int y = 0; y < height; y++)
465 {
466 source = reinterpret_cast<const float*>(static_cast<const unsigned char*>(input) + y * inputPitch);
467 dest = reinterpret_cast<float*>(static_cast<unsigned char*>(output) + y * outputPitch);
468 for (int x = 0; x < width; x++)
469 {
470 dest[4 * x + 0] = 0;
471 dest[4 * x + 1] = 0;
472 dest[4 * x + 2] = 0;
473 dest[4 * x + 3] = source[x];
474 }
475 }
476}
477
478void Image::loadAlphaHalfFloatData(GLsizei width, GLsizei height,
479 int inputPitch, const void *input, size_t outputPitch, void *output) const
480{
481 const unsigned short *source = NULL;
482 unsigned short *dest = NULL;
483
484 for (int y = 0; y < height; y++)
485 {
486 source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
487 dest = reinterpret_cast<unsigned short*>(static_cast<unsigned char*>(output) + y * outputPitch);
488 for (int x = 0; x < width; x++)
489 {
490 dest[4 * x + 0] = 0;
491 dest[4 * x + 1] = 0;
492 dest[4 * x + 2] = 0;
493 dest[4 * x + 3] = source[x];
494 }
495 }
496}
497
498void Image::loadLuminanceData(GLsizei width, GLsizei height,
499 int inputPitch, const void *input, size_t outputPitch, void *output, bool native) const
500{
501 const unsigned char *source = NULL;
502 unsigned char *dest = NULL;
503
504 for (int y = 0; y < height; y++)
505 {
506 source = static_cast<const unsigned char*>(input) + y * inputPitch;
507 dest = static_cast<unsigned char*>(output) + y * outputPitch;
508
509 if (!native) // BGRA8 destination format
510 {
511 for (int x = 0; x < width; x++)
512 {
513 dest[4 * x + 0] = source[x];
514 dest[4 * x + 1] = source[x];
515 dest[4 * x + 2] = source[x];
516 dest[4 * x + 3] = 0xFF;
517 }
518 }
519 else // L8 destination format
520 {
521 memcpy(dest, source, width);
522 }
523 }
524}
525
526void Image::loadLuminanceFloatData(GLsizei width, GLsizei height,
527 int inputPitch, const void *input, size_t outputPitch, void *output) const
528{
529 const float *source = NULL;
530 float *dest = NULL;
531
532 for (int y = 0; y < height; y++)
533 {
534 source = reinterpret_cast<const float*>(static_cast<const unsigned char*>(input) + y * inputPitch);
535 dest = reinterpret_cast<float*>(static_cast<unsigned char*>(output) + y * outputPitch);
536 for (int x = 0; x < width; x++)
537 {
538 dest[4 * x + 0] = source[x];
539 dest[4 * x + 1] = source[x];
540 dest[4 * x + 2] = source[x];
541 dest[4 * x + 3] = 1.0f;
542 }
543 }
544}
545
546void Image::loadLuminanceHalfFloatData(GLsizei width, GLsizei height,
547 int inputPitch, const void *input, size_t outputPitch, void *output) const
548{
549 const unsigned short *source = NULL;
550 unsigned short *dest = NULL;
551
552 for (int y = 0; y < height; y++)
553 {
554 source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
555 dest = reinterpret_cast<unsigned short*>(static_cast<unsigned char*>(output) + y * outputPitch);
556 for (int x = 0; x < width; x++)
557 {
558 dest[4 * x + 0] = source[x];
559 dest[4 * x + 1] = source[x];
560 dest[4 * x + 2] = source[x];
561 dest[4 * x + 3] = 0x3C00; // SEEEEEMMMMMMMMMM, S = 0, E = 15, M = 0: 16bit flpt representation of 1
562 }
563 }
564}
565
566void Image::loadLuminanceAlphaData(GLsizei width, GLsizei height,
567 int inputPitch, const void *input, size_t outputPitch, void *output, bool native) const
568{
569 const unsigned char *source = NULL;
570 unsigned char *dest = NULL;
571
572 for (int y = 0; y < height; y++)
573 {
574 source = static_cast<const unsigned char*>(input) + y * inputPitch;
575 dest = static_cast<unsigned char*>(output) + y * outputPitch;
576
577 if (!native) // BGRA8 destination format
578 {
579 for (int x = 0; x < width; x++)
580 {
581 dest[4 * x + 0] = source[2*x+0];
582 dest[4 * x + 1] = source[2*x+0];
583 dest[4 * x + 2] = source[2*x+0];
584 dest[4 * x + 3] = source[2*x+1];
585 }
586 }
587 else
588 {
589 memcpy(dest, source, width * 2);
590 }
591 }
592}
593
594void Image::loadLuminanceAlphaFloatData(GLsizei width, GLsizei height,
595 int inputPitch, const void *input, size_t outputPitch, void *output) const
596{
597 const float *source = NULL;
598 float *dest = NULL;
599
600 for (int y = 0; y < height; y++)
601 {
602 source = reinterpret_cast<const float*>(static_cast<const unsigned char*>(input) + y * inputPitch);
603 dest = reinterpret_cast<float*>(static_cast<unsigned char*>(output) + y * outputPitch);
604 for (int x = 0; x < width; x++)
605 {
606 dest[4 * x + 0] = source[2*x+0];
607 dest[4 * x + 1] = source[2*x+0];
608 dest[4 * x + 2] = source[2*x+0];
609 dest[4 * x + 3] = source[2*x+1];
610 }
611 }
612}
613
614void Image::loadLuminanceAlphaHalfFloatData(GLsizei width, GLsizei height,
615 int inputPitch, const void *input, size_t outputPitch, void *output) const
616{
617 const unsigned short *source = NULL;
618 unsigned short *dest = NULL;
619
620 for (int y = 0; y < height; y++)
621 {
622 source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
623 dest = reinterpret_cast<unsigned short*>(static_cast<unsigned char*>(output) + y * outputPitch);
624 for (int x = 0; x < width; x++)
625 {
626 dest[4 * x + 0] = source[2*x+0];
627 dest[4 * x + 1] = source[2*x+0];
628 dest[4 * x + 2] = source[2*x+0];
629 dest[4 * x + 3] = source[2*x+1];
630 }
631 }
632}
633
634void Image::loadRGBUByteData(GLsizei width, GLsizei height,
635 int inputPitch, const void *input, size_t outputPitch, void *output) const
636{
637 const unsigned char *source = NULL;
638 unsigned char *dest = NULL;
639
640 for (int y = 0; y < height; y++)
641 {
642 source = static_cast<const unsigned char*>(input) + y * inputPitch;
643 dest = static_cast<unsigned char*>(output) + y * outputPitch;
644 for (int x = 0; x < width; x++)
645 {
646 dest[4 * x + 0] = source[x * 3 + 2];
647 dest[4 * x + 1] = source[x * 3 + 1];
648 dest[4 * x + 2] = source[x * 3 + 0];
649 dest[4 * x + 3] = 0xFF;
650 }
651 }
652}
653
654void Image::loadRGB565Data(GLsizei width, GLsizei height,
655 int inputPitch, const void *input, size_t outputPitch, void *output) const
656{
657 const unsigned short *source = NULL;
658 unsigned char *dest = NULL;
659
660 for (int y = 0; y < height; y++)
661 {
662 source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
663 dest = static_cast<unsigned char*>(output) + y * outputPitch;
664 for (int x = 0; x < width; x++)
665 {
666 unsigned short rgba = source[x];
667 dest[4 * x + 0] = ((rgba & 0x001F) << 3) | ((rgba & 0x001F) >> 2);
668 dest[4 * x + 1] = ((rgba & 0x07E0) >> 3) | ((rgba & 0x07E0) >> 9);
669 dest[4 * x + 2] = ((rgba & 0xF800) >> 8) | ((rgba & 0xF800) >> 13);
670 dest[4 * x + 3] = 0xFF;
671 }
672 }
673}
674
675void Image::loadRGBFloatData(GLsizei width, GLsizei height,
676 int inputPitch, const void *input, size_t outputPitch, void *output) const
677{
678 const float *source = NULL;
679 float *dest = NULL;
680
681 for (int y = 0; y < height; y++)
682 {
683 source = reinterpret_cast<const float*>(static_cast<const unsigned char*>(input) + y * inputPitch);
684 dest = reinterpret_cast<float*>(static_cast<unsigned char*>(output) + y * outputPitch);
685 for (int x = 0; x < width; x++)
686 {
687 dest[4 * x + 0] = source[x * 3 + 0];
688 dest[4 * x + 1] = source[x * 3 + 1];
689 dest[4 * x + 2] = source[x * 3 + 2];
690 dest[4 * x + 3] = 1.0f;
691 }
692 }
693}
694
695void Image::loadRGBHalfFloatData(GLsizei width, GLsizei height,
696 int inputPitch, const void *input, size_t outputPitch, void *output) const
697{
698 const unsigned short *source = NULL;
699 unsigned short *dest = NULL;
700
701 for (int y = 0; y < height; y++)
702 {
703 source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
704 dest = reinterpret_cast<unsigned short*>(static_cast<unsigned char*>(output) + y * outputPitch);
705 for (int x = 0; x < width; x++)
706 {
707 dest[4 * x + 0] = source[x * 3 + 0];
708 dest[4 * x + 1] = source[x * 3 + 1];
709 dest[4 * x + 2] = source[x * 3 + 2];
710 dest[4 * x + 3] = 0x3C00; // SEEEEEMMMMMMMMMM, S = 0, E = 15, M = 0: 16bit flpt representation of 1
711 }
712 }
713}
714
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000715void Image::loadRGBAUByteData(GLsizei width, GLsizei height,
716 int inputPitch, const void *input, size_t outputPitch, void *output) const
717{
718 const unsigned int *source = NULL;
719 unsigned int *dest = NULL;
720 for (int y = 0; y < height; y++)
721 {
722 source = reinterpret_cast<const unsigned int*>(static_cast<const unsigned char*>(input) + y * inputPitch);
723 dest = reinterpret_cast<unsigned int*>(static_cast<unsigned char*>(output) + y * outputPitch);
724
725 for (int x = 0; x < width; x++)
726 {
727 unsigned int rgba = source[x];
728 dest[x] = (_rotl(rgba, 16) & 0x00ff00ff) | (rgba & 0xff00ff00);
729 }
730 }
731}
732
733void Image::loadRGBA4444Data(GLsizei width, GLsizei height,
734 int inputPitch, const void *input, size_t outputPitch, void *output) const
735{
736 const unsigned short *source = NULL;
737 unsigned char *dest = NULL;
738
739 for (int y = 0; y < height; y++)
740 {
741 source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
742 dest = static_cast<unsigned char*>(output) + y * outputPitch;
743 for (int x = 0; x < width; x++)
744 {
745 unsigned short rgba = source[x];
746 dest[4 * x + 0] = ((rgba & 0x00F0) << 0) | ((rgba & 0x00F0) >> 4);
747 dest[4 * x + 1] = ((rgba & 0x0F00) >> 4) | ((rgba & 0x0F00) >> 8);
748 dest[4 * x + 2] = ((rgba & 0xF000) >> 8) | ((rgba & 0xF000) >> 12);
749 dest[4 * x + 3] = ((rgba & 0x000F) << 4) | ((rgba & 0x000F) >> 0);
750 }
751 }
752}
753
754void Image::loadRGBA5551Data(GLsizei width, GLsizei height,
755 int inputPitch, const void *input, size_t outputPitch, void *output) const
756{
757 const unsigned short *source = NULL;
758 unsigned char *dest = NULL;
759
760 for (int y = 0; y < height; y++)
761 {
762 source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
763 dest = static_cast<unsigned char*>(output) + y * outputPitch;
764 for (int x = 0; x < width; x++)
765 {
766 unsigned short rgba = source[x];
767 dest[4 * x + 0] = ((rgba & 0x003E) << 2) | ((rgba & 0x003E) >> 3);
768 dest[4 * x + 1] = ((rgba & 0x07C0) >> 3) | ((rgba & 0x07C0) >> 8);
769 dest[4 * x + 2] = ((rgba & 0xF800) >> 8) | ((rgba & 0xF800) >> 13);
770 dest[4 * x + 3] = (rgba & 0x0001) ? 0xFF : 0;
771 }
772 }
773}
774
775void Image::loadRGBAFloatData(GLsizei width, GLsizei height,
776 int inputPitch, const void *input, size_t outputPitch, void *output) const
777{
778 const float *source = NULL;
779 float *dest = NULL;
780
781 for (int y = 0; y < height; y++)
782 {
783 source = reinterpret_cast<const float*>(static_cast<const unsigned char*>(input) + y * inputPitch);
784 dest = reinterpret_cast<float*>(static_cast<unsigned char*>(output) + y * outputPitch);
785 memcpy(dest, source, width * 16);
786 }
787}
788
789void Image::loadRGBAHalfFloatData(GLsizei width, GLsizei height,
790 int inputPitch, const void *input, size_t outputPitch, void *output) const
791{
792 const unsigned char *source = NULL;
793 unsigned char *dest = NULL;
794
795 for (int y = 0; y < height; y++)
796 {
797 source = static_cast<const unsigned char*>(input) + y * inputPitch;
798 dest = static_cast<unsigned char*>(output) + y * outputPitch;
799 memcpy(dest, source, width * 8);
800 }
801}
802
803void Image::loadBGRAData(GLsizei width, GLsizei height,
804 int inputPitch, const void *input, size_t outputPitch, void *output) const
805{
806 const unsigned char *source = NULL;
807 unsigned char *dest = NULL;
808
809 for (int y = 0; y < height; y++)
810 {
811 source = static_cast<const unsigned char*>(input) + y * inputPitch;
812 dest = static_cast<unsigned char*>(output) + y * outputPitch;
813 memcpy(dest, source, width*4);
814 }
815}
816
817void Image::loadCompressedData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
818 const void *input) {
819 ASSERT(xoffset % 4 == 0);
820 ASSERT(yoffset % 4 == 0);
821
822 RECT lockRect = {
823 xoffset, yoffset,
824 xoffset + width, yoffset + height
825 };
826
827 D3DLOCKED_RECT locked;
828 HRESULT result = lock(&locked, &lockRect);
829 if (FAILED(result))
830 {
831 return;
832 }
833
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000834 GLsizei inputSize = ComputeCompressedSize(width, height, mInternalFormat);
835 GLsizei inputPitch = ComputeCompressedPitch(width, mInternalFormat);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000836 int rows = inputSize / inputPitch;
837 for (int i = 0; i < rows; ++i)
838 {
839 memcpy((void*)((BYTE*)locked.pBits + i * locked.Pitch), (void*)((BYTE*)input + i * inputPitch), inputPitch);
840 }
841
842 unlock();
843}
844
845// This implements glCopyTex[Sub]Image2D for non-renderable internal texture formats and incomplete textures
846void Image::copy(GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, IDirect3DSurface9 *renderTarget)
847{
848 IDirect3DDevice9 *device = getDevice();
849 IDirect3DSurface9 *renderTargetData = NULL;
850 D3DSURFACE_DESC description;
851 renderTarget->GetDesc(&description);
852
853 HRESULT result = device->CreateOffscreenPlainSurface(description.Width, description.Height, description.Format, D3DPOOL_SYSTEMMEM, &renderTargetData, NULL);
854
855 if (FAILED(result))
856 {
857 ERR("Could not create matching destination surface.");
858 return error(GL_OUT_OF_MEMORY);
859 }
860
861 result = device->GetRenderTargetData(renderTarget, renderTargetData);
862
863 if (FAILED(result))
864 {
865 ERR("GetRenderTargetData unexpectedly failed.");
866 renderTargetData->Release();
867 return error(GL_OUT_OF_MEMORY);
868 }
869
870 RECT sourceRect = {x, y, x + width, y + height};
871 RECT destRect = {xoffset, yoffset, xoffset + width, yoffset + height};
872
873 if (isRenderableFormat())
874 {
875 result = D3DXLoadSurfaceFromSurface(getSurface(), NULL, &destRect, renderTargetData, NULL, &sourceRect, D3DX_FILTER_BOX, 0);
876
877 if (FAILED(result))
878 {
879 ERR("Copying surfaces unexpectedly failed.");
880 renderTargetData->Release();
881 return error(GL_OUT_OF_MEMORY);
882 }
883 }
884 else
885 {
886 D3DLOCKED_RECT sourceLock = {0};
887 result = renderTargetData->LockRect(&sourceLock, &sourceRect, 0);
888
889 if (FAILED(result))
890 {
891 ERR("Failed to lock the source surface (rectangle might be invalid).");
892 renderTargetData->Release();
893 return error(GL_OUT_OF_MEMORY);
894 }
895
896 D3DLOCKED_RECT destLock = {0};
897 result = lock(&destLock, &destRect);
898
899 if (FAILED(result))
900 {
901 ERR("Failed to lock the destination surface (rectangle might be invalid).");
902 renderTargetData->UnlockRect();
903 renderTargetData->Release();
904 return error(GL_OUT_OF_MEMORY);
905 }
906
907 if (destLock.pBits && sourceLock.pBits)
908 {
909 unsigned char *source = (unsigned char*)sourceLock.pBits;
910 unsigned char *dest = (unsigned char*)destLock.pBits;
911
912 switch (description.Format)
913 {
914 case D3DFMT_X8R8G8B8:
915 case D3DFMT_A8R8G8B8:
916 switch(getD3DFormat())
917 {
918 case D3DFMT_L8:
919 for(int y = 0; y < height; y++)
920 {
921 for(int x = 0; x < width; x++)
922 {
923 dest[x] = source[x * 4 + 2];
924 }
925
926 source += sourceLock.Pitch;
927 dest += destLock.Pitch;
928 }
929 break;
930 case D3DFMT_A8L8:
931 for(int y = 0; y < height; y++)
932 {
933 for(int x = 0; x < width; x++)
934 {
935 dest[x * 2 + 0] = source[x * 4 + 2];
936 dest[x * 2 + 1] = source[x * 4 + 3];
937 }
938
939 source += sourceLock.Pitch;
940 dest += destLock.Pitch;
941 }
942 break;
943 default:
944 UNREACHABLE();
945 }
946 break;
947 case D3DFMT_R5G6B5:
948 switch(getD3DFormat())
949 {
950 case D3DFMT_L8:
951 for(int y = 0; y < height; y++)
952 {
953 for(int x = 0; x < width; x++)
954 {
955 unsigned char red = source[x * 2 + 1] & 0xF8;
956 dest[x] = red | (red >> 5);
957 }
958
959 source += sourceLock.Pitch;
960 dest += destLock.Pitch;
961 }
962 break;
963 default:
964 UNREACHABLE();
965 }
966 break;
967 case D3DFMT_A1R5G5B5:
968 switch(getD3DFormat())
969 {
970 case D3DFMT_L8:
971 for(int y = 0; y < height; y++)
972 {
973 for(int x = 0; x < width; x++)
974 {
975 unsigned char red = source[x * 2 + 1] & 0x7C;
976 dest[x] = (red << 1) | (red >> 4);
977 }
978
979 source += sourceLock.Pitch;
980 dest += destLock.Pitch;
981 }
982 break;
983 case D3DFMT_A8L8:
984 for(int y = 0; y < height; y++)
985 {
986 for(int x = 0; x < width; x++)
987 {
988 unsigned char red = source[x * 2 + 1] & 0x7C;
989 dest[x * 2 + 0] = (red << 1) | (red >> 4);
990 dest[x * 2 + 1] = (signed char)source[x * 2 + 1] >> 7;
991 }
992
993 source += sourceLock.Pitch;
994 dest += destLock.Pitch;
995 }
996 break;
997 default:
998 UNREACHABLE();
999 }
1000 break;
1001 default:
1002 UNREACHABLE();
1003 }
1004 }
1005
1006 unlock();
1007 renderTargetData->UnlockRect();
1008 }
1009
1010 renderTargetData->Release();
1011
1012 mDirty = true;
1013}
1014
1015TextureStorage::TextureStorage(DWORD usage)
1016 : mD3DUsage(usage),
1017 mD3DPool(getDisplay()->getTexturePool(usage)),
jbauman@chromium.org68715282012-07-12 23:28:41 +00001018 mTextureSerial(issueTextureSerial()),
1019 mLodOffset(0)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001020{
1021}
1022
1023TextureStorage::~TextureStorage()
1024{
1025}
1026
1027bool TextureStorage::isRenderTarget() const
1028{
1029 return (mD3DUsage & (D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL)) != 0;
1030}
1031
1032bool TextureStorage::isManaged() const
1033{
1034 return (mD3DPool == D3DPOOL_MANAGED);
1035}
1036
1037D3DPOOL TextureStorage::getPool() const
1038{
1039 return mD3DPool;
1040}
1041
1042DWORD TextureStorage::getUsage() const
1043{
1044 return mD3DUsage;
1045}
1046
1047unsigned int TextureStorage::getTextureSerial() const
1048{
1049 return mTextureSerial;
1050}
1051
1052unsigned int TextureStorage::issueTextureSerial()
1053{
1054 return mCurrentTextureSerial++;
1055}
1056
jbauman@chromium.org68715282012-07-12 23:28:41 +00001057int TextureStorage::getLodOffset() const
1058{
1059 return mLodOffset;
1060}
1061
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001062Texture::Texture(GLuint id) : RefCountObject(id)
1063{
1064 mMinFilter = GL_NEAREST_MIPMAP_LINEAR;
1065 mMagFilter = GL_LINEAR;
1066 mWrapS = GL_REPEAT;
1067 mWrapT = GL_REPEAT;
1068 mDirtyParameters = true;
1069 mUsage = GL_NONE;
daniel@transgaming.com07ab8412012-07-12 15:17:09 +00001070 mMaxAnisotropy = 1.0f;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001071
1072 mDirtyImages = true;
1073
1074 mImmutable = false;
1075}
1076
1077Texture::~Texture()
1078{
1079}
1080
1081// Returns true on successful filter state update (valid enum parameter)
1082bool Texture::setMinFilter(GLenum filter)
1083{
1084 switch (filter)
1085 {
1086 case GL_NEAREST:
1087 case GL_LINEAR:
1088 case GL_NEAREST_MIPMAP_NEAREST:
1089 case GL_LINEAR_MIPMAP_NEAREST:
1090 case GL_NEAREST_MIPMAP_LINEAR:
1091 case GL_LINEAR_MIPMAP_LINEAR:
1092 {
1093 if (mMinFilter != filter)
1094 {
1095 mMinFilter = filter;
1096 mDirtyParameters = true;
1097 }
1098 return true;
1099 }
1100 default:
1101 return false;
1102 }
1103}
1104
1105// Returns true on successful filter state update (valid enum parameter)
1106bool Texture::setMagFilter(GLenum filter)
1107{
1108 switch (filter)
1109 {
1110 case GL_NEAREST:
1111 case GL_LINEAR:
1112 {
1113 if (mMagFilter != filter)
1114 {
1115 mMagFilter = filter;
1116 mDirtyParameters = true;
1117 }
1118 return true;
1119 }
1120 default:
1121 return false;
1122 }
1123}
1124
1125// Returns true on successful wrap state update (valid enum parameter)
1126bool Texture::setWrapS(GLenum wrap)
1127{
1128 switch (wrap)
1129 {
1130 case GL_REPEAT:
1131 case GL_CLAMP_TO_EDGE:
1132 case GL_MIRRORED_REPEAT:
1133 {
1134 if (mWrapS != wrap)
1135 {
1136 mWrapS = wrap;
1137 mDirtyParameters = true;
1138 }
1139 return true;
1140 }
1141 default:
1142 return false;
1143 }
1144}
1145
1146// Returns true on successful wrap state update (valid enum parameter)
1147bool Texture::setWrapT(GLenum wrap)
1148{
1149 switch (wrap)
1150 {
1151 case GL_REPEAT:
1152 case GL_CLAMP_TO_EDGE:
1153 case GL_MIRRORED_REPEAT:
1154 {
1155 if (mWrapT != wrap)
1156 {
1157 mWrapT = wrap;
1158 mDirtyParameters = true;
1159 }
1160 return true;
1161 }
1162 default:
1163 return false;
1164 }
1165}
1166
daniel@transgaming.com07ab8412012-07-12 15:17:09 +00001167// Returns true on successful max anisotropy update (valid anisotropy value)
1168bool Texture::setMaxAnisotropy(float textureMaxAnisotropy, float contextMaxAnisotropy)
1169{
1170 textureMaxAnisotropy = std::min(textureMaxAnisotropy, contextMaxAnisotropy);
1171 if (textureMaxAnisotropy < 1.0f)
1172 {
1173 return false;
1174 }
1175 if (mMaxAnisotropy != textureMaxAnisotropy)
1176 {
1177 mMaxAnisotropy = textureMaxAnisotropy;
1178 mDirtyParameters = true;
1179 }
1180 return true;
1181}
1182
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001183// Returns true on successful usage state update (valid enum parameter)
1184bool Texture::setUsage(GLenum usage)
1185{
1186 switch (usage)
1187 {
1188 case GL_NONE:
1189 case GL_FRAMEBUFFER_ATTACHMENT_ANGLE:
1190 mUsage = usage;
1191 return true;
1192 default:
1193 return false;
1194 }
1195}
1196
1197GLenum Texture::getMinFilter() const
1198{
1199 return mMinFilter;
1200}
1201
1202GLenum Texture::getMagFilter() const
1203{
1204 return mMagFilter;
1205}
1206
1207GLenum Texture::getWrapS() const
1208{
1209 return mWrapS;
1210}
1211
1212GLenum Texture::getWrapT() const
1213{
1214 return mWrapT;
1215}
1216
daniel@transgaming.com07ab8412012-07-12 15:17:09 +00001217float Texture::getMaxAnisotropy() const
1218{
1219 return mMaxAnisotropy;
1220}
1221
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001222GLenum Texture::getUsage() const
1223{
1224 return mUsage;
1225}
1226
1227void Texture::setImage(GLint unpackAlignment, const void *pixels, Image *image)
1228{
1229 if (pixels != NULL)
1230 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001231 image->loadData(0, 0, image->getWidth(), image->getHeight(), unpackAlignment, pixels);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001232 mDirtyImages = true;
1233 }
1234}
1235
1236void Texture::setCompressedImage(GLsizei imageSize, const void *pixels, Image *image)
1237{
1238 if (pixels != NULL)
1239 {
1240 image->loadCompressedData(0, 0, image->getWidth(), image->getHeight(), pixels);
1241 mDirtyImages = true;
1242 }
1243}
1244
1245bool Texture::subImage(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, Image *image)
1246{
1247 if (pixels != NULL)
1248 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001249 image->loadData(xoffset, yoffset, width, height, unpackAlignment, pixels);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001250 mDirtyImages = true;
1251 }
1252
1253 return true;
1254}
1255
1256bool Texture::subImageCompressed(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels, Image *image)
1257{
1258 if (pixels != NULL)
1259 {
1260 image->loadCompressedData(xoffset, yoffset, width, height, pixels);
1261 mDirtyImages = true;
1262 }
1263
1264 return true;
1265}
1266
1267IDirect3DBaseTexture9 *Texture::getTexture()
1268{
1269 if (!isSamplerComplete())
1270 {
1271 return NULL;
1272 }
1273
1274 // ensure the underlying texture is created
1275 if (getStorage(false) == NULL)
1276 {
1277 return NULL;
1278 }
1279
1280 updateTexture();
1281
1282 return getBaseTexture();
1283}
1284
1285bool Texture::hasDirtyParameters() const
1286{
1287 return mDirtyParameters;
1288}
1289
1290bool Texture::hasDirtyImages() const
1291{
1292 return mDirtyImages;
1293}
1294
1295void Texture::resetDirty()
1296{
1297 mDirtyParameters = false;
1298 mDirtyImages = false;
1299}
1300
1301unsigned int Texture::getTextureSerial()
1302{
1303 TextureStorage *texture = getStorage(false);
1304 return texture ? texture->getTextureSerial() : 0;
1305}
1306
1307unsigned int Texture::getRenderTargetSerial(GLenum target)
1308{
1309 TextureStorage *texture = getStorage(true);
1310 return texture ? texture->getRenderTargetSerial(target) : 0;
1311}
1312
1313bool Texture::isImmutable() const
1314{
1315 return mImmutable;
1316}
1317
jbauman@chromium.org68715282012-07-12 23:28:41 +00001318int Texture::getLodOffset()
1319{
1320 TextureStorage *texture = getStorage(false);
1321 return texture ? texture->getLodOffset() : 0;
1322}
1323
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001324GLint Texture::creationLevels(GLsizei width, GLsizei height) const
1325{
1326 if ((isPow2(width) && isPow2(height)) || getContext()->supportsNonPower2Texture())
1327 {
1328 return 0; // Maximum number of levels
1329 }
1330 else
1331 {
1332 // OpenGL ES 2.0 without GL_OES_texture_npot does not permit NPOT mipmaps.
1333 return 1;
1334 }
1335}
1336
1337GLint Texture::creationLevels(GLsizei size) const
1338{
1339 return creationLevels(size, size);
1340}
1341
jbauman@chromium.org6bc4a142012-09-06 21:28:30 +00001342int Texture::levelCount()
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001343{
jbauman@chromium.org6bc4a142012-09-06 21:28:30 +00001344 return getBaseTexture() ? getBaseTexture()->GetLevelCount() - getLodOffset() : 0;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001345}
1346
1347Blit *Texture::getBlitter()
1348{
1349 Context *context = getContext();
1350 return context->getBlitter();
1351}
1352
1353bool Texture::copyToRenderTarget(IDirect3DSurface9 *dest, IDirect3DSurface9 *source, bool fromManaged)
1354{
1355 if (source && dest)
1356 {
1357 HRESULT result;
1358
1359 if (fromManaged)
1360 {
1361 result = D3DXLoadSurfaceFromSurface(dest, NULL, NULL, source, NULL, NULL, D3DX_FILTER_BOX, 0);
1362 }
1363 else
1364 {
1365 egl::Display *display = getDisplay();
1366 IDirect3DDevice9 *device = display->getDevice();
1367
1368 display->endScene();
1369 result = device->StretchRect(source, NULL, dest, NULL, D3DTEXF_NONE);
1370 }
1371
1372 if (FAILED(result))
1373 {
1374 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1375 return false;
1376 }
1377 }
1378
1379 return true;
1380}
1381
1382TextureStorage2D::TextureStorage2D(IDirect3DTexture9 *surfaceTexture) : TextureStorage(D3DUSAGE_RENDERTARGET), mRenderTargetSerial(RenderbufferStorage::issueSerial())
1383{
1384 mTexture = surfaceTexture;
1385}
1386
1387TextureStorage2D::TextureStorage2D(int levels, D3DFORMAT format, DWORD usage, int width, int height)
1388 : TextureStorage(usage), mRenderTargetSerial(RenderbufferStorage::issueSerial())
1389{
1390 mTexture = NULL;
1391 // if the width or height is not positive this should be treated as an incomplete texture
1392 // we handle that here by skipping the d3d texture creation
1393 if (width > 0 && height > 0)
1394 {
1395 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com4bb04be2012-10-17 18:29:55 +00001396 MakeValidSize(false, dx::IsCompressedFormat(format), &width, &height, &mLodOffset);
sminns@adobe.comce1189b2012-09-18 20:06:35 +00001397 HRESULT result = device->CreateTexture(width, height, levels ? levels + mLodOffset : 0, getUsage(), format, getPool(), &mTexture, NULL);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001398
1399 if (FAILED(result))
1400 {
1401 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1402 error(GL_OUT_OF_MEMORY);
1403 }
1404 }
1405}
1406
1407TextureStorage2D::~TextureStorage2D()
1408{
1409 if (mTexture)
1410 {
1411 mTexture->Release();
1412 }
1413}
1414
1415// Increments refcount on surface.
1416// caller must Release() the returned surface
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00001417IDirect3DSurface9 *TextureStorage2D::getSurfaceLevel(int level, bool dirty)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001418{
1419 IDirect3DSurface9 *surface = NULL;
1420
1421 if (mTexture)
1422 {
jbauman@chromium.org68715282012-07-12 23:28:41 +00001423 HRESULT result = mTexture->GetSurfaceLevel(level + mLodOffset, &surface);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001424 ASSERT(SUCCEEDED(result));
daniel@transgaming.com1ee986b2012-09-27 17:46:12 +00001425
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00001426 // With managed textures the driver needs to be informed of updates to the lower mipmap levels
1427 if (level != 0 && isManaged() && dirty)
daniel@transgaming.com1ee986b2012-09-27 17:46:12 +00001428 {
1429 mTexture->AddDirtyRect(NULL);
1430 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001431 }
1432
1433 return surface;
1434}
1435
1436IDirect3DBaseTexture9 *TextureStorage2D::getBaseTexture() const
1437{
1438 return mTexture;
1439}
1440
1441unsigned int TextureStorage2D::getRenderTargetSerial(GLenum target) const
1442{
1443 return mRenderTargetSerial;
1444}
1445
1446Texture2D::Texture2D(GLuint id) : Texture(id)
1447{
1448 mTexStorage = NULL;
1449 mSurface = NULL;
1450 mColorbufferProxy = NULL;
1451 mProxyRefs = 0;
1452}
1453
1454Texture2D::~Texture2D()
1455{
1456 mColorbufferProxy = NULL;
1457
1458 delete mTexStorage;
1459 mTexStorage = NULL;
1460
1461 if (mSurface)
1462 {
1463 mSurface->setBoundTexture(NULL);
1464 mSurface = NULL;
1465 }
1466}
1467
1468// We need to maintain a count of references to renderbuffers acting as
1469// proxies for this texture, so that we do not attempt to use a pointer
1470// to a renderbuffer proxy which has been deleted.
1471void Texture2D::addProxyRef(const Renderbuffer *proxy)
1472{
1473 mProxyRefs++;
1474}
1475
1476void Texture2D::releaseProxy(const Renderbuffer *proxy)
1477{
1478 if (mProxyRefs > 0)
1479 mProxyRefs--;
1480
1481 if (mProxyRefs == 0)
1482 mColorbufferProxy = NULL;
1483}
1484
1485GLenum Texture2D::getTarget() const
1486{
1487 return GL_TEXTURE_2D;
1488}
1489
1490GLsizei Texture2D::getWidth(GLint level) const
1491{
1492 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
1493 return mImageArray[level].getWidth();
1494 else
1495 return 0;
1496}
1497
1498GLsizei Texture2D::getHeight(GLint level) const
1499{
1500 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
1501 return mImageArray[level].getHeight();
1502 else
1503 return 0;
1504}
1505
1506GLenum Texture2D::getInternalFormat(GLint level) const
1507{
1508 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001509 return mImageArray[level].getInternalFormat();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001510 else
1511 return GL_NONE;
1512}
1513
1514D3DFORMAT Texture2D::getD3DFormat(GLint level) const
1515{
1516 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
1517 return mImageArray[level].getD3DFormat();
1518 else
1519 return D3DFMT_UNKNOWN;
1520}
1521
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001522void Texture2D::redefineImage(GLint level, GLint internalformat, GLsizei width, GLsizei height)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001523{
1524 releaseTexImage();
1525
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001526 bool redefined = mImageArray[level].redefine(internalformat, width, height, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001527
1528 if (mTexStorage && redefined)
1529 {
1530 for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
1531 {
1532 mImageArray[i].markDirty();
1533 }
1534
1535 delete mTexStorage;
1536 mTexStorage = NULL;
1537 mDirtyImages = true;
1538 }
1539}
1540
1541void Texture2D::setImage(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1542{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001543 GLint internalformat = ConvertSizedInternalFormat(format, type);
1544 redefineImage(level, internalformat, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001545
1546 Texture::setImage(unpackAlignment, pixels, &mImageArray[level]);
1547}
1548
1549void Texture2D::bindTexImage(egl::Surface *surface)
1550{
1551 releaseTexImage();
1552
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001553 GLint internalformat;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001554
1555 switch(surface->getFormat())
1556 {
1557 case D3DFMT_A8R8G8B8:
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001558 internalformat = GL_RGBA8_OES;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001559 break;
1560 case D3DFMT_X8R8G8B8:
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001561 internalformat = GL_RGB8_OES;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001562 break;
1563 default:
1564 UNIMPLEMENTED();
1565 return;
1566 }
1567
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001568 mImageArray[0].redefine(internalformat, surface->getWidth(), surface->getHeight(), true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001569
1570 delete mTexStorage;
1571 mTexStorage = new TextureStorage2D(surface->getOffscreenTexture());
1572
1573 mDirtyImages = true;
1574 mSurface = surface;
1575 mSurface->setBoundTexture(this);
1576}
1577
1578void Texture2D::releaseTexImage()
1579{
1580 if (mSurface)
1581 {
1582 mSurface->setBoundTexture(NULL);
1583 mSurface = NULL;
1584
1585 if (mTexStorage)
1586 {
1587 delete mTexStorage;
1588 mTexStorage = NULL;
1589 }
1590
1591 for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
1592 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001593 mImageArray[i].redefine(GL_RGBA8_OES, 0, 0, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001594 }
1595 }
1596}
1597
1598void Texture2D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
1599{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001600 // compressed formats don't have separate sized internal formats-- we can just use the compressed format directly
1601 redefineImage(level, format, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001602
1603 Texture::setCompressedImage(imageSize, pixels, &mImageArray[level]);
1604}
1605
1606void Texture2D::commitRect(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
1607{
1608 ASSERT(mImageArray[level].getSurface() != NULL);
1609
1610 if (level < levelCount())
1611 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00001612 IDirect3DSurface9 *destLevel = mTexStorage->getSurfaceLevel(level, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001613
1614 if (destLevel)
1615 {
1616 Image *image = &mImageArray[level];
1617 image->updateSurface(destLevel, xoffset, yoffset, width, height);
1618
1619 destLevel->Release();
1620 image->markClean();
1621 }
1622 }
1623}
1624
1625void Texture2D::subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1626{
1627 if (Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[level]))
1628 {
1629 commitRect(level, xoffset, yoffset, width, height);
1630 }
1631}
1632
1633void Texture2D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
1634{
1635 if (Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, &mImageArray[level]))
1636 {
1637 commitRect(level, xoffset, yoffset, width, height);
1638 }
1639}
1640
1641void Texture2D::copyImage(GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
1642{
1643 IDirect3DSurface9 *renderTarget = source->getRenderTarget();
1644
1645 if (!renderTarget)
1646 {
1647 ERR("Failed to retrieve the render target.");
1648 return error(GL_OUT_OF_MEMORY);
1649 }
1650
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001651 GLint internalformat = ConvertSizedInternalFormat(format, GL_UNSIGNED_BYTE);
1652 redefineImage(level, internalformat, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001653
1654 if (!mImageArray[level].isRenderableFormat())
1655 {
1656 mImageArray[level].copy(0, 0, x, y, width, height, renderTarget);
1657 mDirtyImages = true;
1658 }
1659 else
1660 {
1661 if (!mTexStorage || !mTexStorage->isRenderTarget())
1662 {
1663 convertToRenderTarget();
1664 }
1665
1666 mImageArray[level].markClean();
1667
1668 if (width != 0 && height != 0 && level < levelCount())
1669 {
1670 RECT sourceRect;
1671 sourceRect.left = x;
1672 sourceRect.right = x + width;
1673 sourceRect.top = y;
1674 sourceRect.bottom = y + height;
1675
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00001676 IDirect3DSurface9 *dest = mTexStorage->getSurfaceLevel(level, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001677
1678 if (dest)
1679 {
1680 getBlitter()->copy(renderTarget, sourceRect, format, 0, 0, dest);
1681 dest->Release();
1682 }
1683 }
1684 }
1685
1686 renderTarget->Release();
1687}
1688
1689void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
1690{
1691 if (xoffset + width > mImageArray[level].getWidth() || yoffset + height > mImageArray[level].getHeight())
1692 {
1693 return error(GL_INVALID_VALUE);
1694 }
1695
1696 IDirect3DSurface9 *renderTarget = source->getRenderTarget();
1697
1698 if (!renderTarget)
1699 {
1700 ERR("Failed to retrieve the render target.");
1701 return error(GL_OUT_OF_MEMORY);
1702 }
1703
1704 if (!mImageArray[level].isRenderableFormat() || (!mTexStorage && !isSamplerComplete()))
1705 {
1706 mImageArray[level].copy(xoffset, yoffset, x, y, width, height, renderTarget);
1707 mDirtyImages = true;
1708 }
1709 else
1710 {
1711 if (!mTexStorage || !mTexStorage->isRenderTarget())
1712 {
1713 convertToRenderTarget();
1714 }
1715
1716 updateTexture();
1717
1718 if (level < levelCount())
1719 {
1720 RECT sourceRect;
1721 sourceRect.left = x;
1722 sourceRect.right = x + width;
1723 sourceRect.top = y;
1724 sourceRect.bottom = y + height;
1725
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00001726 IDirect3DSurface9 *dest = mTexStorage->getSurfaceLevel(level, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001727
1728 if (dest)
1729 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001730 getBlitter()->copy(renderTarget, sourceRect,
1731 gl::ExtractFormat(mImageArray[0].getInternalFormat()),
1732 xoffset, yoffset, dest);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001733 dest->Release();
1734 }
1735 }
1736 }
1737
1738 renderTarget->Release();
1739}
1740
1741void Texture2D::storage(GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height)
1742{
daniel@transgaming.com6b1a0a02012-10-17 18:22:47 +00001743 D3DFORMAT d3dfmt = ConvertTextureInternalFormat(internalformat);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001744 DWORD d3dusage = GetTextureUsage(d3dfmt, mUsage, false);
1745
1746 delete mTexStorage;
1747 mTexStorage = new TextureStorage2D(levels, d3dfmt, d3dusage, width, height);
1748 mImmutable = true;
1749
1750 for (int level = 0; level < levels; level++)
1751 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001752 mImageArray[level].redefine(internalformat, width, height, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001753 width = std::max(1, width >> 1);
1754 height = std::max(1, height >> 1);
1755 }
1756
1757 for (int level = levels; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
1758 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001759 mImageArray[level].redefine(GL_NONE, 0, 0, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001760 }
1761
1762 if (mTexStorage->isManaged())
1763 {
1764 int levels = levelCount();
1765
1766 for (int level = 0; level < levels; level++)
1767 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00001768 IDirect3DSurface9 *surface = mTexStorage->getSurfaceLevel(level, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001769 mImageArray[level].setManagedSurface(surface);
1770 }
1771 }
1772}
1773
1774// Tests for 2D texture sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 85.
1775bool Texture2D::isSamplerComplete() const
1776{
1777 GLsizei width = mImageArray[0].getWidth();
1778 GLsizei height = mImageArray[0].getHeight();
1779
1780 if (width <= 0 || height <= 0)
1781 {
1782 return false;
1783 }
1784
1785 bool mipmapping = false;
1786
1787 switch (mMinFilter)
1788 {
1789 case GL_NEAREST:
1790 case GL_LINEAR:
1791 mipmapping = false;
1792 break;
1793 case GL_NEAREST_MIPMAP_NEAREST:
1794 case GL_LINEAR_MIPMAP_NEAREST:
1795 case GL_NEAREST_MIPMAP_LINEAR:
1796 case GL_LINEAR_MIPMAP_LINEAR:
1797 mipmapping = true;
1798 break;
1799 default: UNREACHABLE();
1800 }
1801
daniel@transgaming.com6b1a0a02012-10-17 18:22:47 +00001802 if ((IsFloat32Format(getInternalFormat(0)) && !getContext()->supportsFloat32LinearFilter()) ||
1803 (IsFloat16Format(getInternalFormat(0)) && !getContext()->supportsFloat16LinearFilter()))
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001804 {
1805 if (mMagFilter != GL_NEAREST || (mMinFilter != GL_NEAREST && mMinFilter != GL_NEAREST_MIPMAP_NEAREST))
1806 {
1807 return false;
1808 }
1809 }
1810
1811 bool npotSupport = getContext()->supportsNonPower2Texture();
1812
1813 if (!npotSupport)
1814 {
1815 if ((getWrapS() != GL_CLAMP_TO_EDGE && !isPow2(width)) ||
1816 (getWrapT() != GL_CLAMP_TO_EDGE && !isPow2(height)))
1817 {
1818 return false;
1819 }
1820 }
1821
1822 if (mipmapping)
1823 {
1824 if (!npotSupport)
1825 {
1826 if (!isPow2(width) || !isPow2(height))
1827 {
1828 return false;
1829 }
1830 }
1831
1832 if (!isMipmapComplete())
1833 {
1834 return false;
1835 }
1836 }
1837
1838 return true;
1839}
1840
1841// Tests for 2D texture (mipmap) completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
1842bool Texture2D::isMipmapComplete() const
1843{
1844 if (isImmutable())
1845 {
1846 return true;
1847 }
1848
1849 GLsizei width = mImageArray[0].getWidth();
1850 GLsizei height = mImageArray[0].getHeight();
1851
1852 if (width <= 0 || height <= 0)
1853 {
1854 return false;
1855 }
1856
1857 int q = log2(std::max(width, height));
1858
1859 for (int level = 1; level <= q; level++)
1860 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001861 if (mImageArray[level].getInternalFormat() != mImageArray[0].getInternalFormat())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001862 {
1863 return false;
1864 }
1865
1866 if (mImageArray[level].getWidth() != std::max(1, width >> level))
1867 {
1868 return false;
1869 }
1870
1871 if (mImageArray[level].getHeight() != std::max(1, height >> level))
1872 {
1873 return false;
1874 }
1875 }
1876
1877 return true;
1878}
1879
1880bool Texture2D::isCompressed(GLint level) const
1881{
1882 return IsCompressed(getInternalFormat(level));
1883}
1884
1885bool Texture2D::isDepth(GLint level) const
1886{
1887 return IsDepthTexture(getInternalFormat(level));
1888}
1889
1890IDirect3DBaseTexture9 *Texture2D::getBaseTexture() const
1891{
1892 return mTexStorage ? mTexStorage->getBaseTexture() : NULL;
1893}
1894
1895// Constructs a Direct3D 9 texture resource from the texture images
1896void Texture2D::createTexture()
1897{
1898 GLsizei width = mImageArray[0].getWidth();
1899 GLsizei height = mImageArray[0].getHeight();
daniel@transgaming.come6a09842012-09-17 21:28:55 +00001900
1901 if (!(width > 0 && height > 0))
1902 return; // do not attempt to create d3d textures for nonexistant data
1903
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001904 GLint levels = creationLevels(width, height);
1905 D3DFORMAT d3dfmt = mImageArray[0].getD3DFormat();
1906 DWORD d3dusage = GetTextureUsage(d3dfmt, mUsage, false);
1907
1908 delete mTexStorage;
1909 mTexStorage = new TextureStorage2D(levels, d3dfmt, d3dusage, width, height);
1910
1911 if (mTexStorage->isManaged())
1912 {
1913 int levels = levelCount();
1914
1915 for (int level = 0; level < levels; level++)
1916 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00001917 IDirect3DSurface9 *surface = mTexStorage->getSurfaceLevel(level, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001918 mImageArray[level].setManagedSurface(surface);
1919 }
1920 }
1921
1922 mDirtyImages = true;
1923}
1924
1925void Texture2D::updateTexture()
1926{
1927 int levels = levelCount();
1928
1929 for (int level = 0; level < levels; level++)
1930 {
1931 Image *image = &mImageArray[level];
1932
1933 if (image->isDirty())
1934 {
1935 commitRect(level, 0, 0, mImageArray[level].getWidth(), mImageArray[level].getHeight());
1936 }
1937 }
1938}
1939
1940void Texture2D::convertToRenderTarget()
1941{
1942 TextureStorage2D *newTexStorage = NULL;
1943
1944 if (mImageArray[0].getWidth() != 0 && mImageArray[0].getHeight() != 0)
1945 {
1946 GLsizei width = mImageArray[0].getWidth();
1947 GLsizei height = mImageArray[0].getHeight();
1948 GLint levels = creationLevels(width, height);
1949 D3DFORMAT d3dfmt = mImageArray[0].getD3DFormat();
1950 DWORD d3dusage = GetTextureUsage(d3dfmt, GL_FRAMEBUFFER_ATTACHMENT_ANGLE, true);
1951
1952 newTexStorage = new TextureStorage2D(levels, d3dfmt, d3dusage, width, height);
1953
1954 if (mTexStorage != NULL)
1955 {
1956 int levels = levelCount();
1957 for (int i = 0; i < levels; i++)
1958 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00001959 IDirect3DSurface9 *source = mTexStorage->getSurfaceLevel(i, false);
1960 IDirect3DSurface9 *dest = newTexStorage->getSurfaceLevel(i, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001961
1962 if (!copyToRenderTarget(dest, source, mTexStorage->isManaged()))
1963 {
1964 delete newTexStorage;
1965 if (source) source->Release();
1966 if (dest) dest->Release();
1967 return error(GL_OUT_OF_MEMORY);
1968 }
1969
1970 if (source) source->Release();
1971 if (dest) dest->Release();
1972 }
1973 }
1974 }
1975
1976 delete mTexStorage;
1977 mTexStorage = newTexStorage;
1978
1979 mDirtyImages = true;
1980}
1981
1982void Texture2D::generateMipmaps()
1983{
1984 if (!getContext()->supportsNonPower2Texture())
1985 {
1986 if (!isPow2(mImageArray[0].getWidth()) || !isPow2(mImageArray[0].getHeight()))
1987 {
1988 return error(GL_INVALID_OPERATION);
1989 }
1990 }
1991
1992 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
1993 unsigned int q = log2(std::max(mImageArray[0].getWidth(), mImageArray[0].getHeight()));
1994 for (unsigned int i = 1; i <= q; i++)
1995 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001996 redefineImage(i, mImageArray[0].getInternalFormat(),
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001997 std::max(mImageArray[0].getWidth() >> i, 1),
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001998 std::max(mImageArray[0].getHeight() >> i, 1));
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001999 }
2000
2001 if (mTexStorage && mTexStorage->isRenderTarget())
2002 {
2003 for (unsigned int i = 1; i <= q; i++)
2004 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002005 IDirect3DSurface9 *upper = mTexStorage->getSurfaceLevel(i - 1, false);
2006 IDirect3DSurface9 *lower = mTexStorage->getSurfaceLevel(i, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002007
2008 if (upper != NULL && lower != NULL)
2009 {
2010 getBlitter()->boxFilter(upper, lower);
2011 }
2012
2013 if (upper != NULL) upper->Release();
2014 if (lower != NULL) lower->Release();
2015
2016 mImageArray[i].markClean();
2017 }
2018 }
2019 else
2020 {
2021 for (unsigned int i = 1; i <= q; i++)
2022 {
2023 if (mImageArray[i].getSurface() == NULL)
2024 {
2025 return error(GL_OUT_OF_MEMORY);
2026 }
2027
2028 if (FAILED(D3DXLoadSurfaceFromSurface(mImageArray[i].getSurface(), NULL, NULL, mImageArray[i - 1].getSurface(), NULL, NULL, D3DX_FILTER_BOX, 0)))
2029 {
2030 ERR(" failed to load filter %d to %d.", i - 1, i);
2031 }
2032
2033 mImageArray[i].markDirty();
2034 }
2035 }
2036}
2037
2038Renderbuffer *Texture2D::getRenderbuffer(GLenum target)
2039{
2040 if (target != GL_TEXTURE_2D)
2041 {
2042 return error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);
2043 }
2044
2045 if (mColorbufferProxy == NULL)
2046 {
2047 mColorbufferProxy = new Renderbuffer(id(), new RenderbufferTexture2D(this, target));
2048 }
2049
2050 return mColorbufferProxy;
2051}
2052
2053// Increments refcount on surface.
2054// caller must Release() the returned surface
2055IDirect3DSurface9 *Texture2D::getRenderTarget(GLenum target)
2056{
2057 ASSERT(target == GL_TEXTURE_2D);
2058
2059 // ensure the underlying texture is created
2060 if (getStorage(true) == NULL)
2061 {
2062 return NULL;
2063 }
2064
2065 updateTexture();
2066
2067 // ensure this is NOT a depth texture
2068 if (isDepth(0))
2069 {
2070 return NULL;
2071 }
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002072 return mTexStorage->getSurfaceLevel(0, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002073}
2074
2075// Increments refcount on surface.
2076// caller must Release() the returned surface
2077IDirect3DSurface9 *Texture2D::getDepthStencil(GLenum target)
2078{
2079 ASSERT(target == GL_TEXTURE_2D);
2080
2081 // ensure the underlying texture is created
2082 if (getStorage(true) == NULL)
2083 {
2084 return NULL;
2085 }
2086
2087 updateTexture();
2088
2089 // ensure this is actually a depth texture
2090 if (!isDepth(0))
2091 {
2092 return NULL;
2093 }
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002094 return mTexStorage->getSurfaceLevel(0, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002095}
2096
2097TextureStorage *Texture2D::getStorage(bool renderTarget)
2098{
2099 if (!mTexStorage || (renderTarget && !mTexStorage->isRenderTarget()))
2100 {
2101 if (renderTarget)
2102 {
2103 convertToRenderTarget();
2104 }
2105 else
2106 {
2107 createTexture();
2108 }
2109 }
2110
2111 return mTexStorage;
2112}
2113
2114TextureStorageCubeMap::TextureStorageCubeMap(int levels, D3DFORMAT format, DWORD usage, int size)
2115 : TextureStorage(usage), mFirstRenderTargetSerial(RenderbufferStorage::issueCubeSerials())
2116{
2117 mTexture = NULL;
2118 // if the size is not positive this should be treated as an incomplete texture
2119 // we handle that here by skipping the d3d texture creation
2120 if (size > 0)
2121 {
2122 IDirect3DDevice9 *device = getDevice();
jbauman@chromium.org68715282012-07-12 23:28:41 +00002123 int height = size;
daniel@transgaming.com4bb04be2012-10-17 18:29:55 +00002124 MakeValidSize(false, dx::IsCompressedFormat(format), &size, &height, &mLodOffset);
sminns@adobe.comce1189b2012-09-18 20:06:35 +00002125 HRESULT result = device->CreateCubeTexture(size, levels ? levels + mLodOffset : 0, getUsage(), format, getPool(), &mTexture, NULL);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002126
2127 if (FAILED(result))
2128 {
2129 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
2130 error(GL_OUT_OF_MEMORY);
2131 }
2132 }
2133}
2134
2135TextureStorageCubeMap::~TextureStorageCubeMap()
2136{
2137 if (mTexture)
2138 {
2139 mTexture->Release();
2140 }
2141}
2142
2143// Increments refcount on surface.
2144// caller must Release() the returned surface
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002145IDirect3DSurface9 *TextureStorageCubeMap::getCubeMapSurface(GLenum faceTarget, int level, bool dirty)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002146{
2147 IDirect3DSurface9 *surface = NULL;
2148
2149 if (mTexture)
2150 {
daniel@transgaming.com1ee986b2012-09-27 17:46:12 +00002151 D3DCUBEMAP_FACES face = es2dx::ConvertCubeFace(faceTarget);
2152 HRESULT result = mTexture->GetCubeMapSurface(face, level + mLodOffset, &surface);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002153 ASSERT(SUCCEEDED(result));
daniel@transgaming.com1ee986b2012-09-27 17:46:12 +00002154
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002155 // With managed textures the driver needs to be informed of updates to the lower mipmap levels
2156 if (level != 0 && isManaged() && dirty)
daniel@transgaming.com1ee986b2012-09-27 17:46:12 +00002157 {
2158 mTexture->AddDirtyRect(face, NULL);
2159 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002160 }
2161
2162 return surface;
2163}
2164
2165IDirect3DBaseTexture9 *TextureStorageCubeMap::getBaseTexture() const
2166{
2167 return mTexture;
2168}
2169
2170unsigned int TextureStorageCubeMap::getRenderTargetSerial(GLenum target) const
2171{
2172 return mFirstRenderTargetSerial + TextureCubeMap::faceIndex(target);
2173}
2174
2175TextureCubeMap::TextureCubeMap(GLuint id) : Texture(id)
2176{
2177 mTexStorage = NULL;
2178 for (int i = 0; i < 6; i++)
2179 {
2180 mFaceProxies[i] = NULL;
2181 mFaceProxyRefs[i] = 0;
2182 }
2183}
2184
2185TextureCubeMap::~TextureCubeMap()
2186{
2187 for (int i = 0; i < 6; i++)
2188 {
2189 mFaceProxies[i] = NULL;
2190 }
2191
2192 delete mTexStorage;
2193 mTexStorage = NULL;
2194}
2195
2196// We need to maintain a count of references to renderbuffers acting as
2197// proxies for this texture, so that the texture is not deleted while
2198// proxy references still exist. If the reference count drops to zero,
2199// we set our proxy pointer NULL, so that a new attempt at referencing
2200// will cause recreation.
2201void TextureCubeMap::addProxyRef(const Renderbuffer *proxy)
2202{
2203 for (int i = 0; i < 6; i++)
2204 {
2205 if (mFaceProxies[i] == proxy)
2206 mFaceProxyRefs[i]++;
2207 }
2208}
2209
2210void TextureCubeMap::releaseProxy(const Renderbuffer *proxy)
2211{
2212 for (int i = 0; i < 6; i++)
2213 {
2214 if (mFaceProxies[i] == proxy)
2215 {
2216 if (mFaceProxyRefs[i] > 0)
2217 mFaceProxyRefs[i]--;
2218
2219 if (mFaceProxyRefs[i] == 0)
2220 mFaceProxies[i] = NULL;
2221 }
2222 }
2223}
2224
2225GLenum TextureCubeMap::getTarget() const
2226{
2227 return GL_TEXTURE_CUBE_MAP;
2228}
2229
2230GLsizei TextureCubeMap::getWidth(GLenum target, GLint level) const
2231{
2232 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
2233 return mImageArray[faceIndex(target)][level].getWidth();
2234 else
2235 return 0;
2236}
2237
2238GLsizei TextureCubeMap::getHeight(GLenum target, GLint level) const
2239{
2240 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
2241 return mImageArray[faceIndex(target)][level].getHeight();
2242 else
2243 return 0;
2244}
2245
2246GLenum TextureCubeMap::getInternalFormat(GLenum target, GLint level) const
2247{
2248 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002249 return mImageArray[faceIndex(target)][level].getInternalFormat();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002250 else
2251 return GL_NONE;
2252}
2253
2254D3DFORMAT TextureCubeMap::getD3DFormat(GLenum target, GLint level) const
2255{
2256 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
2257 return mImageArray[faceIndex(target)][level].getD3DFormat();
2258 else
2259 return D3DFMT_UNKNOWN;
2260}
2261
2262void TextureCubeMap::setImagePosX(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
2263{
2264 setImage(0, level, width, height, format, type, unpackAlignment, pixels);
2265}
2266
2267void TextureCubeMap::setImageNegX(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
2268{
2269 setImage(1, level, width, height, format, type, unpackAlignment, pixels);
2270}
2271
2272void TextureCubeMap::setImagePosY(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
2273{
2274 setImage(2, level, width, height, format, type, unpackAlignment, pixels);
2275}
2276
2277void TextureCubeMap::setImageNegY(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
2278{
2279 setImage(3, level, width, height, format, type, unpackAlignment, pixels);
2280}
2281
2282void TextureCubeMap::setImagePosZ(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
2283{
2284 setImage(4, level, width, height, format, type, unpackAlignment, pixels);
2285}
2286
2287void TextureCubeMap::setImageNegZ(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
2288{
2289 setImage(5, level, width, height, format, type, unpackAlignment, pixels);
2290}
2291
2292void TextureCubeMap::setCompressedImage(GLenum face, GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
2293{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002294 // compressed formats don't have separate sized internal formats-- we can just use the compressed format directly
2295 redefineImage(faceIndex(face), level, format, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002296
2297 Texture::setCompressedImage(imageSize, pixels, &mImageArray[faceIndex(face)][level]);
2298}
2299
2300void TextureCubeMap::commitRect(int face, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
2301{
2302 ASSERT(mImageArray[face][level].getSurface() != NULL);
2303
2304 if (level < levelCount())
2305 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002306 IDirect3DSurface9 *destLevel = mTexStorage->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, level, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002307 ASSERT(destLevel != NULL);
2308
2309 if (destLevel != NULL)
2310 {
2311 Image *image = &mImageArray[face][level];
2312 image->updateSurface(destLevel, xoffset, yoffset, width, height);
2313
2314 destLevel->Release();
2315 image->markClean();
2316 }
2317 }
2318}
2319
2320void TextureCubeMap::subImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
2321{
2322 if (Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[faceIndex(target)][level]))
2323 {
2324 commitRect(faceIndex(target), level, xoffset, yoffset, width, height);
2325 }
2326}
2327
2328void TextureCubeMap::subImageCompressed(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
2329{
2330 if (Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, &mImageArray[faceIndex(target)][level]))
2331 {
2332 commitRect(faceIndex(target), level, xoffset, yoffset, width, height);
2333 }
2334}
2335
2336// Tests for cube map sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 86.
2337bool TextureCubeMap::isSamplerComplete() const
2338{
2339 int size = mImageArray[0][0].getWidth();
2340
2341 bool mipmapping;
2342
2343 switch (mMinFilter)
2344 {
2345 case GL_NEAREST:
2346 case GL_LINEAR:
2347 mipmapping = false;
2348 break;
2349 case GL_NEAREST_MIPMAP_NEAREST:
2350 case GL_LINEAR_MIPMAP_NEAREST:
2351 case GL_NEAREST_MIPMAP_LINEAR:
2352 case GL_LINEAR_MIPMAP_LINEAR:
2353 mipmapping = true;
2354 break;
2355 default:
2356 UNREACHABLE();
2357 return false;
2358 }
2359
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002360 if ((gl::ExtractType(getInternalFormat(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0)) == GL_FLOAT && !getContext()->supportsFloat32LinearFilter()) ||
2361 (gl::ExtractType(getInternalFormat(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0) == GL_HALF_FLOAT_OES) && !getContext()->supportsFloat16LinearFilter()))
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002362 {
2363 if (mMagFilter != GL_NEAREST || (mMinFilter != GL_NEAREST && mMinFilter != GL_NEAREST_MIPMAP_NEAREST))
2364 {
2365 return false;
2366 }
2367 }
2368
2369 if (!isPow2(size) && !getContext()->supportsNonPower2Texture())
2370 {
2371 if (getWrapS() != GL_CLAMP_TO_EDGE || getWrapT() != GL_CLAMP_TO_EDGE || mipmapping)
2372 {
2373 return false;
2374 }
2375 }
2376
2377 if (!mipmapping)
2378 {
2379 if (!isCubeComplete())
2380 {
2381 return false;
2382 }
2383 }
2384 else
2385 {
2386 if (!isMipmapCubeComplete()) // Also tests for isCubeComplete()
2387 {
2388 return false;
2389 }
2390 }
2391
2392 return true;
2393}
2394
2395// Tests for cube texture completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
2396bool TextureCubeMap::isCubeComplete() const
2397{
2398 if (mImageArray[0][0].getWidth() <= 0 || mImageArray[0][0].getHeight() != mImageArray[0][0].getWidth())
2399 {
2400 return false;
2401 }
2402
2403 for (unsigned int face = 1; face < 6; face++)
2404 {
2405 if (mImageArray[face][0].getWidth() != mImageArray[0][0].getWidth() ||
2406 mImageArray[face][0].getWidth() != mImageArray[0][0].getHeight() ||
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002407 mImageArray[face][0].getInternalFormat() != mImageArray[0][0].getInternalFormat())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002408 {
2409 return false;
2410 }
2411 }
2412
2413 return true;
2414}
2415
2416bool TextureCubeMap::isMipmapCubeComplete() const
2417{
2418 if (isImmutable())
2419 {
2420 return true;
2421 }
2422
2423 if (!isCubeComplete())
2424 {
2425 return false;
2426 }
2427
2428 GLsizei size = mImageArray[0][0].getWidth();
2429
2430 int q = log2(size);
2431
2432 for (int face = 0; face < 6; face++)
2433 {
2434 for (int level = 1; level <= q; level++)
2435 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002436 if (mImageArray[face][level].getInternalFormat() != mImageArray[0][0].getInternalFormat())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002437 {
2438 return false;
2439 }
2440
2441 if (mImageArray[face][level].getWidth() != std::max(1, size >> level))
2442 {
2443 return false;
2444 }
2445 }
2446 }
2447
2448 return true;
2449}
2450
2451bool TextureCubeMap::isCompressed(GLenum target, GLint level) const
2452{
2453 return IsCompressed(getInternalFormat(target, level));
2454}
2455
2456IDirect3DBaseTexture9 *TextureCubeMap::getBaseTexture() const
2457{
2458 return mTexStorage ? mTexStorage->getBaseTexture() : NULL;
2459}
2460
2461// Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one
2462void TextureCubeMap::createTexture()
2463{
2464 GLsizei size = mImageArray[0][0].getWidth();
daniel@transgaming.come6a09842012-09-17 21:28:55 +00002465
2466 if (!(size > 0))
2467 return; // do not attempt to create d3d textures for nonexistant data
2468
sminns@adobe.comce1189b2012-09-18 20:06:35 +00002469 GLint levels = creationLevels(size);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002470 D3DFORMAT d3dfmt = mImageArray[0][0].getD3DFormat();
2471 DWORD d3dusage = GetTextureUsage(d3dfmt, mUsage, false);
2472
2473 delete mTexStorage;
2474 mTexStorage = new TextureStorageCubeMap(levels, d3dfmt, d3dusage, size);
2475
2476 if (mTexStorage->isManaged())
2477 {
2478 int levels = levelCount();
2479
2480 for (int face = 0; face < 6; face++)
2481 {
2482 for (int level = 0; level < levels; level++)
2483 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002484 IDirect3DSurface9 *surface = mTexStorage->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, level, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002485 mImageArray[face][level].setManagedSurface(surface);
2486 }
2487 }
2488 }
2489
2490 mDirtyImages = true;
2491}
2492
2493void TextureCubeMap::updateTexture()
2494{
2495 for (int face = 0; face < 6; face++)
2496 {
2497 int levels = levelCount();
2498 for (int level = 0; level < levels; level++)
2499 {
2500 Image *image = &mImageArray[face][level];
2501
2502 if (image->isDirty())
2503 {
2504 commitRect(face, level, 0, 0, image->getWidth(), image->getHeight());
2505 }
2506 }
2507 }
2508}
2509
2510void TextureCubeMap::convertToRenderTarget()
2511{
2512 TextureStorageCubeMap *newTexStorage = NULL;
2513
2514 if (mImageArray[0][0].getWidth() != 0)
2515 {
2516 GLsizei size = mImageArray[0][0].getWidth();
sminns@adobe.comce1189b2012-09-18 20:06:35 +00002517 GLint levels = creationLevels(size);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002518 D3DFORMAT d3dfmt = mImageArray[0][0].getD3DFormat();
2519 DWORD d3dusage = GetTextureUsage(d3dfmt, GL_FRAMEBUFFER_ATTACHMENT_ANGLE, true);
2520
2521 newTexStorage = new TextureStorageCubeMap(levels, d3dfmt, d3dusage, size);
2522
2523 if (mTexStorage != NULL)
2524 {
2525 int levels = levelCount();
2526 for (int f = 0; f < 6; f++)
2527 {
2528 for (int i = 0; i < levels; i++)
2529 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002530 IDirect3DSurface9 *source = mTexStorage->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + f, i, false);
2531 IDirect3DSurface9 *dest = newTexStorage->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + f, i, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002532
2533 if (!copyToRenderTarget(dest, source, mTexStorage->isManaged()))
2534 {
2535 delete newTexStorage;
2536 if (source) source->Release();
2537 if (dest) dest->Release();
2538 return error(GL_OUT_OF_MEMORY);
2539 }
2540
2541 if (source) source->Release();
2542 if (dest) dest->Release();
2543 }
2544 }
2545 }
2546 }
2547
2548 delete mTexStorage;
2549 mTexStorage = newTexStorage;
2550
2551 mDirtyImages = true;
2552}
2553
2554void TextureCubeMap::setImage(int faceIndex, GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
2555{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002556 GLint internalformat = ConvertSizedInternalFormat(format, type);
2557 redefineImage(faceIndex, level, internalformat, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002558
2559 Texture::setImage(unpackAlignment, pixels, &mImageArray[faceIndex][level]);
2560}
2561
2562unsigned int TextureCubeMap::faceIndex(GLenum face)
2563{
2564 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_X - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 1);
2565 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 2);
2566 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 3);
2567 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 4);
2568 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 5);
2569
2570 return face - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
2571}
2572
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002573void TextureCubeMap::redefineImage(int face, GLint level, GLint internalformat, GLsizei width, GLsizei height)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002574{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002575 bool redefined = mImageArray[face][level].redefine(internalformat, width, height, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002576
2577 if (mTexStorage && redefined)
2578 {
2579 for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
2580 {
2581 for (int f = 0; f < 6; f++)
2582 {
2583 mImageArray[f][i].markDirty();
2584 }
2585 }
2586
2587 delete mTexStorage;
2588 mTexStorage = NULL;
2589
2590 mDirtyImages = true;
2591 }
2592}
2593
2594void TextureCubeMap::copyImage(GLenum target, GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
2595{
2596 IDirect3DSurface9 *renderTarget = source->getRenderTarget();
2597
2598 if (!renderTarget)
2599 {
2600 ERR("Failed to retrieve the render target.");
2601 return error(GL_OUT_OF_MEMORY);
2602 }
2603
2604 unsigned int faceindex = faceIndex(target);
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002605 GLint internalformat = gl::ConvertSizedInternalFormat(format, GL_UNSIGNED_BYTE);
2606 redefineImage(faceindex, level, internalformat, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002607
2608 if (!mImageArray[faceindex][level].isRenderableFormat())
2609 {
2610 mImageArray[faceindex][level].copy(0, 0, x, y, width, height, renderTarget);
2611 mDirtyImages = true;
2612 }
2613 else
2614 {
2615 if (!mTexStorage || !mTexStorage->isRenderTarget())
2616 {
2617 convertToRenderTarget();
2618 }
2619
2620 mImageArray[faceindex][level].markClean();
2621
2622 ASSERT(width == height);
2623
2624 if (width > 0 && level < levelCount())
2625 {
2626 RECT sourceRect;
2627 sourceRect.left = x;
2628 sourceRect.right = x + width;
2629 sourceRect.top = y;
2630 sourceRect.bottom = y + height;
2631
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002632 IDirect3DSurface9 *dest = mTexStorage->getCubeMapSurface(target, level, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002633
2634 if (dest)
2635 {
2636 getBlitter()->copy(renderTarget, sourceRect, format, 0, 0, dest);
2637 dest->Release();
2638 }
2639 }
2640 }
2641
2642 renderTarget->Release();
2643}
2644
2645void TextureCubeMap::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
2646{
2647 GLsizei size = mImageArray[faceIndex(target)][level].getWidth();
2648
2649 if (xoffset + width > size || yoffset + height > size)
2650 {
2651 return error(GL_INVALID_VALUE);
2652 }
2653
2654 IDirect3DSurface9 *renderTarget = source->getRenderTarget();
2655
2656 if (!renderTarget)
2657 {
2658 ERR("Failed to retrieve the render target.");
2659 return error(GL_OUT_OF_MEMORY);
2660 }
2661
2662 unsigned int faceindex = faceIndex(target);
2663
2664 if (!mImageArray[faceindex][level].isRenderableFormat() || (!mTexStorage && !isSamplerComplete()))
2665 {
2666 mImageArray[faceindex][level].copy(0, 0, x, y, width, height, renderTarget);
2667 mDirtyImages = true;
2668 }
2669 else
2670 {
2671 if (!mTexStorage || !mTexStorage->isRenderTarget())
2672 {
2673 convertToRenderTarget();
2674 }
2675
2676 updateTexture();
2677
2678 if (level < levelCount())
2679 {
2680 RECT sourceRect;
2681 sourceRect.left = x;
2682 sourceRect.right = x + width;
2683 sourceRect.top = y;
2684 sourceRect.bottom = y + height;
2685
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002686 IDirect3DSurface9 *dest = mTexStorage->getCubeMapSurface(target, level, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002687
2688 if (dest)
2689 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002690 getBlitter()->copy(renderTarget, sourceRect, gl::ExtractFormat(mImageArray[0][0].getInternalFormat()), xoffset, yoffset, dest);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002691 dest->Release();
2692 }
2693 }
2694 }
2695
2696 renderTarget->Release();
2697}
2698
2699void TextureCubeMap::storage(GLsizei levels, GLenum internalformat, GLsizei size)
2700{
daniel@transgaming.com6b1a0a02012-10-17 18:22:47 +00002701 D3DFORMAT d3dfmt = ConvertTextureInternalFormat(internalformat);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002702 DWORD d3dusage = GetTextureUsage(d3dfmt, mUsage, false);
2703
2704 delete mTexStorage;
2705 mTexStorage = new TextureStorageCubeMap(levels, d3dfmt, d3dusage, size);
2706 mImmutable = true;
2707
2708 for (int level = 0; level < levels; level++)
2709 {
2710 for (int face = 0; face < 6; face++)
2711 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002712 mImageArray[face][level].redefine(internalformat, size, size, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002713 size = std::max(1, size >> 1);
2714 }
2715 }
2716
2717 for (int level = levels; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
2718 {
2719 for (int face = 0; face < 6; face++)
2720 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002721 mImageArray[face][level].redefine(GL_NONE, 0, 0, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002722 }
2723 }
2724
2725 if (mTexStorage->isManaged())
2726 {
2727 int levels = levelCount();
2728
2729 for (int face = 0; face < 6; face++)
2730 {
2731 for (int level = 0; level < levels; level++)
2732 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002733 IDirect3DSurface9 *surface = mTexStorage->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, level, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002734 mImageArray[face][level].setManagedSurface(surface);
2735 }
2736 }
2737 }
2738}
2739
2740void TextureCubeMap::generateMipmaps()
2741{
2742 if (!isCubeComplete())
2743 {
2744 return error(GL_INVALID_OPERATION);
2745 }
2746
2747 if (!getContext()->supportsNonPower2Texture())
2748 {
2749 if (!isPow2(mImageArray[0][0].getWidth()))
2750 {
2751 return error(GL_INVALID_OPERATION);
2752 }
2753 }
2754
2755 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
2756 unsigned int q = log2(mImageArray[0][0].getWidth());
2757 for (unsigned int f = 0; f < 6; f++)
2758 {
2759 for (unsigned int i = 1; i <= q; i++)
2760 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002761 redefineImage(f, i, mImageArray[f][0].getInternalFormat(),
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002762 std::max(mImageArray[f][0].getWidth() >> i, 1),
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002763 std::max(mImageArray[f][0].getWidth() >> i, 1));
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002764 }
2765 }
2766
2767 if (mTexStorage && mTexStorage->isRenderTarget())
2768 {
2769 for (unsigned int f = 0; f < 6; f++)
2770 {
2771 for (unsigned int i = 1; i <= q; i++)
2772 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002773 IDirect3DSurface9 *upper = mTexStorage->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + f, i - 1, false);
2774 IDirect3DSurface9 *lower = mTexStorage->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + f, i, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002775
2776 if (upper != NULL && lower != NULL)
2777 {
2778 getBlitter()->boxFilter(upper, lower);
2779 }
2780
2781 if (upper != NULL) upper->Release();
2782 if (lower != NULL) lower->Release();
2783
2784 mImageArray[f][i].markClean();
2785 }
2786 }
2787 }
2788 else
2789 {
2790 for (unsigned int f = 0; f < 6; f++)
2791 {
2792 for (unsigned int i = 1; i <= q; i++)
2793 {
2794 if (mImageArray[f][i].getSurface() == NULL)
2795 {
2796 return error(GL_OUT_OF_MEMORY);
2797 }
2798
2799 if (FAILED(D3DXLoadSurfaceFromSurface(mImageArray[f][i].getSurface(), NULL, NULL, mImageArray[f][i - 1].getSurface(), NULL, NULL, D3DX_FILTER_BOX, 0)))
2800 {
2801 ERR(" failed to load filter %d to %d.", i - 1, i);
2802 }
2803
2804 mImageArray[f][i].markDirty();
2805 }
2806 }
2807 }
2808}
2809
2810Renderbuffer *TextureCubeMap::getRenderbuffer(GLenum target)
2811{
2812 if (!IsCubemapTextureTarget(target))
2813 {
2814 return error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);
2815 }
2816
2817 unsigned int face = faceIndex(target);
2818
2819 if (mFaceProxies[face] == NULL)
2820 {
2821 mFaceProxies[face] = new Renderbuffer(id(), new RenderbufferTextureCubeMap(this, target));
2822 }
2823
2824 return mFaceProxies[face];
2825}
2826
2827// Increments refcount on surface.
2828// caller must Release() the returned surface
2829IDirect3DSurface9 *TextureCubeMap::getRenderTarget(GLenum target)
2830{
2831 ASSERT(IsCubemapTextureTarget(target));
2832
2833 // ensure the underlying texture is created
2834 if (getStorage(true) == NULL)
2835 {
2836 return NULL;
2837 }
2838
2839 updateTexture();
2840
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002841 return mTexStorage->getCubeMapSurface(target, 0, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002842}
2843
2844TextureStorage *TextureCubeMap::getStorage(bool renderTarget)
2845{
2846 if (!mTexStorage || (renderTarget && !mTexStorage->isRenderTarget()))
2847 {
2848 if (renderTarget)
2849 {
2850 convertToRenderTarget();
2851 }
2852 else
2853 {
2854 createTexture();
2855 }
2856 }
2857
2858 return mTexStorage;
2859}
2860
2861}