blob: 86ed405d9f0b62f3e03463b359af7f28e784f3b0 [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 {
daniel@transgaming.comdabf0022012-10-17 18:29:59 +00001357 HRESULT result = D3DERR_OUTOFVIDEOMEMORY;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001358
1359 if (fromManaged)
1360 {
daniel@transgaming.comdabf0022012-10-17 18:29:59 +00001361 D3DSURFACE_DESC desc;
1362 source->GetDesc(&desc);
1363
1364 IDirect3DSurface9 *surf = 0;
1365 result = getDevice()->CreateOffscreenPlainSurface(desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &surf, NULL);
1366
1367 if (SUCCEEDED(result))
1368 {
1369 CopyLockableSurfaces(surf, source);
1370 result = getDevice()->UpdateSurface(surf, NULL, dest, NULL);
1371 surf->Release();
1372 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001373 }
1374 else
1375 {
1376 egl::Display *display = getDisplay();
1377 IDirect3DDevice9 *device = display->getDevice();
1378
1379 display->endScene();
1380 result = device->StretchRect(source, NULL, dest, NULL, D3DTEXF_NONE);
1381 }
1382
1383 if (FAILED(result))
1384 {
1385 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1386 return false;
1387 }
1388 }
1389
1390 return true;
1391}
1392
1393TextureStorage2D::TextureStorage2D(IDirect3DTexture9 *surfaceTexture) : TextureStorage(D3DUSAGE_RENDERTARGET), mRenderTargetSerial(RenderbufferStorage::issueSerial())
1394{
1395 mTexture = surfaceTexture;
1396}
1397
1398TextureStorage2D::TextureStorage2D(int levels, D3DFORMAT format, DWORD usage, int width, int height)
1399 : TextureStorage(usage), mRenderTargetSerial(RenderbufferStorage::issueSerial())
1400{
1401 mTexture = NULL;
1402 // if the width or height is not positive this should be treated as an incomplete texture
1403 // we handle that here by skipping the d3d texture creation
1404 if (width > 0 && height > 0)
1405 {
1406 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com4bb04be2012-10-17 18:29:55 +00001407 MakeValidSize(false, dx::IsCompressedFormat(format), &width, &height, &mLodOffset);
sminns@adobe.comce1189b2012-09-18 20:06:35 +00001408 HRESULT result = device->CreateTexture(width, height, levels ? levels + mLodOffset : 0, getUsage(), format, getPool(), &mTexture, NULL);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001409
1410 if (FAILED(result))
1411 {
1412 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1413 error(GL_OUT_OF_MEMORY);
1414 }
1415 }
1416}
1417
1418TextureStorage2D::~TextureStorage2D()
1419{
1420 if (mTexture)
1421 {
1422 mTexture->Release();
1423 }
1424}
1425
1426// Increments refcount on surface.
1427// caller must Release() the returned surface
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00001428IDirect3DSurface9 *TextureStorage2D::getSurfaceLevel(int level, bool dirty)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001429{
1430 IDirect3DSurface9 *surface = NULL;
1431
1432 if (mTexture)
1433 {
jbauman@chromium.org68715282012-07-12 23:28:41 +00001434 HRESULT result = mTexture->GetSurfaceLevel(level + mLodOffset, &surface);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001435 ASSERT(SUCCEEDED(result));
daniel@transgaming.com1ee986b2012-09-27 17:46:12 +00001436
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00001437 // With managed textures the driver needs to be informed of updates to the lower mipmap levels
1438 if (level != 0 && isManaged() && dirty)
daniel@transgaming.com1ee986b2012-09-27 17:46:12 +00001439 {
1440 mTexture->AddDirtyRect(NULL);
1441 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001442 }
1443
1444 return surface;
1445}
1446
1447IDirect3DBaseTexture9 *TextureStorage2D::getBaseTexture() const
1448{
1449 return mTexture;
1450}
1451
1452unsigned int TextureStorage2D::getRenderTargetSerial(GLenum target) const
1453{
1454 return mRenderTargetSerial;
1455}
1456
1457Texture2D::Texture2D(GLuint id) : Texture(id)
1458{
1459 mTexStorage = NULL;
1460 mSurface = NULL;
1461 mColorbufferProxy = NULL;
1462 mProxyRefs = 0;
1463}
1464
1465Texture2D::~Texture2D()
1466{
1467 mColorbufferProxy = NULL;
1468
1469 delete mTexStorage;
1470 mTexStorage = NULL;
1471
1472 if (mSurface)
1473 {
1474 mSurface->setBoundTexture(NULL);
1475 mSurface = NULL;
1476 }
1477}
1478
1479// We need to maintain a count of references to renderbuffers acting as
1480// proxies for this texture, so that we do not attempt to use a pointer
1481// to a renderbuffer proxy which has been deleted.
1482void Texture2D::addProxyRef(const Renderbuffer *proxy)
1483{
1484 mProxyRefs++;
1485}
1486
1487void Texture2D::releaseProxy(const Renderbuffer *proxy)
1488{
1489 if (mProxyRefs > 0)
1490 mProxyRefs--;
1491
1492 if (mProxyRefs == 0)
1493 mColorbufferProxy = NULL;
1494}
1495
1496GLenum Texture2D::getTarget() const
1497{
1498 return GL_TEXTURE_2D;
1499}
1500
1501GLsizei Texture2D::getWidth(GLint level) const
1502{
1503 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
1504 return mImageArray[level].getWidth();
1505 else
1506 return 0;
1507}
1508
1509GLsizei Texture2D::getHeight(GLint level) const
1510{
1511 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
1512 return mImageArray[level].getHeight();
1513 else
1514 return 0;
1515}
1516
1517GLenum Texture2D::getInternalFormat(GLint level) const
1518{
1519 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001520 return mImageArray[level].getInternalFormat();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001521 else
1522 return GL_NONE;
1523}
1524
1525D3DFORMAT Texture2D::getD3DFormat(GLint level) const
1526{
1527 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
1528 return mImageArray[level].getD3DFormat();
1529 else
1530 return D3DFMT_UNKNOWN;
1531}
1532
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001533void Texture2D::redefineImage(GLint level, GLint internalformat, GLsizei width, GLsizei height)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001534{
1535 releaseTexImage();
1536
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001537 bool redefined = mImageArray[level].redefine(internalformat, width, height, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001538
1539 if (mTexStorage && redefined)
1540 {
1541 for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
1542 {
1543 mImageArray[i].markDirty();
1544 }
1545
1546 delete mTexStorage;
1547 mTexStorage = NULL;
1548 mDirtyImages = true;
1549 }
1550}
1551
1552void Texture2D::setImage(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1553{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001554 GLint internalformat = ConvertSizedInternalFormat(format, type);
1555 redefineImage(level, internalformat, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001556
1557 Texture::setImage(unpackAlignment, pixels, &mImageArray[level]);
1558}
1559
1560void Texture2D::bindTexImage(egl::Surface *surface)
1561{
1562 releaseTexImage();
1563
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001564 GLint internalformat;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001565
1566 switch(surface->getFormat())
1567 {
1568 case D3DFMT_A8R8G8B8:
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001569 internalformat = GL_RGBA8_OES;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001570 break;
1571 case D3DFMT_X8R8G8B8:
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001572 internalformat = GL_RGB8_OES;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001573 break;
1574 default:
1575 UNIMPLEMENTED();
1576 return;
1577 }
1578
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001579 mImageArray[0].redefine(internalformat, surface->getWidth(), surface->getHeight(), true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001580
1581 delete mTexStorage;
1582 mTexStorage = new TextureStorage2D(surface->getOffscreenTexture());
1583
1584 mDirtyImages = true;
1585 mSurface = surface;
1586 mSurface->setBoundTexture(this);
1587}
1588
1589void Texture2D::releaseTexImage()
1590{
1591 if (mSurface)
1592 {
1593 mSurface->setBoundTexture(NULL);
1594 mSurface = NULL;
1595
1596 if (mTexStorage)
1597 {
1598 delete mTexStorage;
1599 mTexStorage = NULL;
1600 }
1601
1602 for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
1603 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001604 mImageArray[i].redefine(GL_RGBA8_OES, 0, 0, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001605 }
1606 }
1607}
1608
1609void Texture2D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
1610{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001611 // compressed formats don't have separate sized internal formats-- we can just use the compressed format directly
1612 redefineImage(level, format, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001613
1614 Texture::setCompressedImage(imageSize, pixels, &mImageArray[level]);
1615}
1616
1617void Texture2D::commitRect(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
1618{
1619 ASSERT(mImageArray[level].getSurface() != NULL);
1620
1621 if (level < levelCount())
1622 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00001623 IDirect3DSurface9 *destLevel = mTexStorage->getSurfaceLevel(level, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001624
1625 if (destLevel)
1626 {
1627 Image *image = &mImageArray[level];
1628 image->updateSurface(destLevel, xoffset, yoffset, width, height);
1629
1630 destLevel->Release();
1631 image->markClean();
1632 }
1633 }
1634}
1635
1636void Texture2D::subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1637{
1638 if (Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[level]))
1639 {
1640 commitRect(level, xoffset, yoffset, width, height);
1641 }
1642}
1643
1644void Texture2D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
1645{
1646 if (Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, &mImageArray[level]))
1647 {
1648 commitRect(level, xoffset, yoffset, width, height);
1649 }
1650}
1651
1652void Texture2D::copyImage(GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
1653{
1654 IDirect3DSurface9 *renderTarget = source->getRenderTarget();
1655
1656 if (!renderTarget)
1657 {
1658 ERR("Failed to retrieve the render target.");
1659 return error(GL_OUT_OF_MEMORY);
1660 }
1661
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001662 GLint internalformat = ConvertSizedInternalFormat(format, GL_UNSIGNED_BYTE);
1663 redefineImage(level, internalformat, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001664
1665 if (!mImageArray[level].isRenderableFormat())
1666 {
1667 mImageArray[level].copy(0, 0, x, y, width, height, renderTarget);
1668 mDirtyImages = true;
1669 }
1670 else
1671 {
1672 if (!mTexStorage || !mTexStorage->isRenderTarget())
1673 {
1674 convertToRenderTarget();
1675 }
1676
1677 mImageArray[level].markClean();
1678
1679 if (width != 0 && height != 0 && level < levelCount())
1680 {
1681 RECT sourceRect;
1682 sourceRect.left = x;
1683 sourceRect.right = x + width;
1684 sourceRect.top = y;
1685 sourceRect.bottom = y + height;
1686
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00001687 IDirect3DSurface9 *dest = mTexStorage->getSurfaceLevel(level, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001688
1689 if (dest)
1690 {
1691 getBlitter()->copy(renderTarget, sourceRect, format, 0, 0, dest);
1692 dest->Release();
1693 }
1694 }
1695 }
1696
1697 renderTarget->Release();
1698}
1699
1700void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
1701{
1702 if (xoffset + width > mImageArray[level].getWidth() || yoffset + height > mImageArray[level].getHeight())
1703 {
1704 return error(GL_INVALID_VALUE);
1705 }
1706
1707 IDirect3DSurface9 *renderTarget = source->getRenderTarget();
1708
1709 if (!renderTarget)
1710 {
1711 ERR("Failed to retrieve the render target.");
1712 return error(GL_OUT_OF_MEMORY);
1713 }
1714
1715 if (!mImageArray[level].isRenderableFormat() || (!mTexStorage && !isSamplerComplete()))
1716 {
1717 mImageArray[level].copy(xoffset, yoffset, x, y, width, height, renderTarget);
1718 mDirtyImages = true;
1719 }
1720 else
1721 {
1722 if (!mTexStorage || !mTexStorage->isRenderTarget())
1723 {
1724 convertToRenderTarget();
1725 }
1726
1727 updateTexture();
1728
1729 if (level < levelCount())
1730 {
1731 RECT sourceRect;
1732 sourceRect.left = x;
1733 sourceRect.right = x + width;
1734 sourceRect.top = y;
1735 sourceRect.bottom = y + height;
1736
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00001737 IDirect3DSurface9 *dest = mTexStorage->getSurfaceLevel(level, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001738
1739 if (dest)
1740 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001741 getBlitter()->copy(renderTarget, sourceRect,
1742 gl::ExtractFormat(mImageArray[0].getInternalFormat()),
1743 xoffset, yoffset, dest);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001744 dest->Release();
1745 }
1746 }
1747 }
1748
1749 renderTarget->Release();
1750}
1751
1752void Texture2D::storage(GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height)
1753{
daniel@transgaming.com6b1a0a02012-10-17 18:22:47 +00001754 D3DFORMAT d3dfmt = ConvertTextureInternalFormat(internalformat);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001755 DWORD d3dusage = GetTextureUsage(d3dfmt, mUsage, false);
1756
1757 delete mTexStorage;
1758 mTexStorage = new TextureStorage2D(levels, d3dfmt, d3dusage, width, height);
1759 mImmutable = true;
1760
1761 for (int level = 0; level < levels; level++)
1762 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001763 mImageArray[level].redefine(internalformat, width, height, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001764 width = std::max(1, width >> 1);
1765 height = std::max(1, height >> 1);
1766 }
1767
1768 for (int level = levels; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
1769 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001770 mImageArray[level].redefine(GL_NONE, 0, 0, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001771 }
1772
1773 if (mTexStorage->isManaged())
1774 {
1775 int levels = levelCount();
1776
1777 for (int level = 0; level < levels; level++)
1778 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00001779 IDirect3DSurface9 *surface = mTexStorage->getSurfaceLevel(level, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001780 mImageArray[level].setManagedSurface(surface);
1781 }
1782 }
1783}
1784
1785// Tests for 2D texture sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 85.
1786bool Texture2D::isSamplerComplete() const
1787{
1788 GLsizei width = mImageArray[0].getWidth();
1789 GLsizei height = mImageArray[0].getHeight();
1790
1791 if (width <= 0 || height <= 0)
1792 {
1793 return false;
1794 }
1795
1796 bool mipmapping = false;
1797
1798 switch (mMinFilter)
1799 {
1800 case GL_NEAREST:
1801 case GL_LINEAR:
1802 mipmapping = false;
1803 break;
1804 case GL_NEAREST_MIPMAP_NEAREST:
1805 case GL_LINEAR_MIPMAP_NEAREST:
1806 case GL_NEAREST_MIPMAP_LINEAR:
1807 case GL_LINEAR_MIPMAP_LINEAR:
1808 mipmapping = true;
1809 break;
1810 default: UNREACHABLE();
1811 }
1812
daniel@transgaming.com6b1a0a02012-10-17 18:22:47 +00001813 if ((IsFloat32Format(getInternalFormat(0)) && !getContext()->supportsFloat32LinearFilter()) ||
1814 (IsFloat16Format(getInternalFormat(0)) && !getContext()->supportsFloat16LinearFilter()))
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001815 {
1816 if (mMagFilter != GL_NEAREST || (mMinFilter != GL_NEAREST && mMinFilter != GL_NEAREST_MIPMAP_NEAREST))
1817 {
1818 return false;
1819 }
1820 }
1821
1822 bool npotSupport = getContext()->supportsNonPower2Texture();
1823
1824 if (!npotSupport)
1825 {
1826 if ((getWrapS() != GL_CLAMP_TO_EDGE && !isPow2(width)) ||
1827 (getWrapT() != GL_CLAMP_TO_EDGE && !isPow2(height)))
1828 {
1829 return false;
1830 }
1831 }
1832
1833 if (mipmapping)
1834 {
1835 if (!npotSupport)
1836 {
1837 if (!isPow2(width) || !isPow2(height))
1838 {
1839 return false;
1840 }
1841 }
1842
1843 if (!isMipmapComplete())
1844 {
1845 return false;
1846 }
1847 }
1848
1849 return true;
1850}
1851
1852// Tests for 2D texture (mipmap) completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
1853bool Texture2D::isMipmapComplete() const
1854{
1855 if (isImmutable())
1856 {
1857 return true;
1858 }
1859
1860 GLsizei width = mImageArray[0].getWidth();
1861 GLsizei height = mImageArray[0].getHeight();
1862
1863 if (width <= 0 || height <= 0)
1864 {
1865 return false;
1866 }
1867
1868 int q = log2(std::max(width, height));
1869
1870 for (int level = 1; level <= q; level++)
1871 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001872 if (mImageArray[level].getInternalFormat() != mImageArray[0].getInternalFormat())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001873 {
1874 return false;
1875 }
1876
1877 if (mImageArray[level].getWidth() != std::max(1, width >> level))
1878 {
1879 return false;
1880 }
1881
1882 if (mImageArray[level].getHeight() != std::max(1, height >> level))
1883 {
1884 return false;
1885 }
1886 }
1887
1888 return true;
1889}
1890
1891bool Texture2D::isCompressed(GLint level) const
1892{
1893 return IsCompressed(getInternalFormat(level));
1894}
1895
1896bool Texture2D::isDepth(GLint level) const
1897{
1898 return IsDepthTexture(getInternalFormat(level));
1899}
1900
1901IDirect3DBaseTexture9 *Texture2D::getBaseTexture() const
1902{
1903 return mTexStorage ? mTexStorage->getBaseTexture() : NULL;
1904}
1905
1906// Constructs a Direct3D 9 texture resource from the texture images
1907void Texture2D::createTexture()
1908{
1909 GLsizei width = mImageArray[0].getWidth();
1910 GLsizei height = mImageArray[0].getHeight();
daniel@transgaming.come6a09842012-09-17 21:28:55 +00001911
1912 if (!(width > 0 && height > 0))
1913 return; // do not attempt to create d3d textures for nonexistant data
1914
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001915 GLint levels = creationLevels(width, height);
1916 D3DFORMAT d3dfmt = mImageArray[0].getD3DFormat();
1917 DWORD d3dusage = GetTextureUsage(d3dfmt, mUsage, false);
1918
1919 delete mTexStorage;
1920 mTexStorage = new TextureStorage2D(levels, d3dfmt, d3dusage, width, height);
1921
1922 if (mTexStorage->isManaged())
1923 {
1924 int levels = levelCount();
1925
1926 for (int level = 0; level < levels; level++)
1927 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00001928 IDirect3DSurface9 *surface = mTexStorage->getSurfaceLevel(level, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001929 mImageArray[level].setManagedSurface(surface);
1930 }
1931 }
1932
1933 mDirtyImages = true;
1934}
1935
1936void Texture2D::updateTexture()
1937{
1938 int levels = levelCount();
1939
1940 for (int level = 0; level < levels; level++)
1941 {
1942 Image *image = &mImageArray[level];
1943
1944 if (image->isDirty())
1945 {
1946 commitRect(level, 0, 0, mImageArray[level].getWidth(), mImageArray[level].getHeight());
1947 }
1948 }
1949}
1950
1951void Texture2D::convertToRenderTarget()
1952{
1953 TextureStorage2D *newTexStorage = NULL;
1954
1955 if (mImageArray[0].getWidth() != 0 && mImageArray[0].getHeight() != 0)
1956 {
1957 GLsizei width = mImageArray[0].getWidth();
1958 GLsizei height = mImageArray[0].getHeight();
1959 GLint levels = creationLevels(width, height);
1960 D3DFORMAT d3dfmt = mImageArray[0].getD3DFormat();
1961 DWORD d3dusage = GetTextureUsage(d3dfmt, GL_FRAMEBUFFER_ATTACHMENT_ANGLE, true);
1962
1963 newTexStorage = new TextureStorage2D(levels, d3dfmt, d3dusage, width, height);
1964
1965 if (mTexStorage != NULL)
1966 {
1967 int levels = levelCount();
1968 for (int i = 0; i < levels; i++)
1969 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00001970 IDirect3DSurface9 *source = mTexStorage->getSurfaceLevel(i, false);
1971 IDirect3DSurface9 *dest = newTexStorage->getSurfaceLevel(i, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001972
1973 if (!copyToRenderTarget(dest, source, mTexStorage->isManaged()))
1974 {
1975 delete newTexStorage;
1976 if (source) source->Release();
1977 if (dest) dest->Release();
1978 return error(GL_OUT_OF_MEMORY);
1979 }
1980
1981 if (source) source->Release();
1982 if (dest) dest->Release();
1983 }
1984 }
1985 }
1986
1987 delete mTexStorage;
1988 mTexStorage = newTexStorage;
1989
1990 mDirtyImages = true;
1991}
1992
1993void Texture2D::generateMipmaps()
1994{
1995 if (!getContext()->supportsNonPower2Texture())
1996 {
1997 if (!isPow2(mImageArray[0].getWidth()) || !isPow2(mImageArray[0].getHeight()))
1998 {
1999 return error(GL_INVALID_OPERATION);
2000 }
2001 }
2002
2003 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
2004 unsigned int q = log2(std::max(mImageArray[0].getWidth(), mImageArray[0].getHeight()));
2005 for (unsigned int i = 1; i <= q; i++)
2006 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002007 redefineImage(i, mImageArray[0].getInternalFormat(),
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002008 std::max(mImageArray[0].getWidth() >> i, 1),
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002009 std::max(mImageArray[0].getHeight() >> i, 1));
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002010 }
2011
2012 if (mTexStorage && mTexStorage->isRenderTarget())
2013 {
2014 for (unsigned int i = 1; i <= q; i++)
2015 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002016 IDirect3DSurface9 *upper = mTexStorage->getSurfaceLevel(i - 1, false);
2017 IDirect3DSurface9 *lower = mTexStorage->getSurfaceLevel(i, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002018
2019 if (upper != NULL && lower != NULL)
2020 {
2021 getBlitter()->boxFilter(upper, lower);
2022 }
2023
2024 if (upper != NULL) upper->Release();
2025 if (lower != NULL) lower->Release();
2026
2027 mImageArray[i].markClean();
2028 }
2029 }
2030 else
2031 {
2032 for (unsigned int i = 1; i <= q; i++)
2033 {
2034 if (mImageArray[i].getSurface() == NULL)
2035 {
2036 return error(GL_OUT_OF_MEMORY);
2037 }
2038
2039 if (FAILED(D3DXLoadSurfaceFromSurface(mImageArray[i].getSurface(), NULL, NULL, mImageArray[i - 1].getSurface(), NULL, NULL, D3DX_FILTER_BOX, 0)))
2040 {
2041 ERR(" failed to load filter %d to %d.", i - 1, i);
2042 }
2043
2044 mImageArray[i].markDirty();
2045 }
2046 }
2047}
2048
2049Renderbuffer *Texture2D::getRenderbuffer(GLenum target)
2050{
2051 if (target != GL_TEXTURE_2D)
2052 {
2053 return error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);
2054 }
2055
2056 if (mColorbufferProxy == NULL)
2057 {
2058 mColorbufferProxy = new Renderbuffer(id(), new RenderbufferTexture2D(this, target));
2059 }
2060
2061 return mColorbufferProxy;
2062}
2063
2064// Increments refcount on surface.
2065// caller must Release() the returned surface
2066IDirect3DSurface9 *Texture2D::getRenderTarget(GLenum target)
2067{
2068 ASSERT(target == GL_TEXTURE_2D);
2069
2070 // ensure the underlying texture is created
2071 if (getStorage(true) == NULL)
2072 {
2073 return NULL;
2074 }
2075
2076 updateTexture();
2077
2078 // ensure this is NOT a depth texture
2079 if (isDepth(0))
2080 {
2081 return NULL;
2082 }
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002083 return mTexStorage->getSurfaceLevel(0, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002084}
2085
2086// Increments refcount on surface.
2087// caller must Release() the returned surface
2088IDirect3DSurface9 *Texture2D::getDepthStencil(GLenum target)
2089{
2090 ASSERT(target == GL_TEXTURE_2D);
2091
2092 // ensure the underlying texture is created
2093 if (getStorage(true) == NULL)
2094 {
2095 return NULL;
2096 }
2097
2098 updateTexture();
2099
2100 // ensure this is actually a depth texture
2101 if (!isDepth(0))
2102 {
2103 return NULL;
2104 }
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002105 return mTexStorage->getSurfaceLevel(0, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002106}
2107
2108TextureStorage *Texture2D::getStorage(bool renderTarget)
2109{
2110 if (!mTexStorage || (renderTarget && !mTexStorage->isRenderTarget()))
2111 {
2112 if (renderTarget)
2113 {
2114 convertToRenderTarget();
2115 }
2116 else
2117 {
2118 createTexture();
2119 }
2120 }
2121
2122 return mTexStorage;
2123}
2124
2125TextureStorageCubeMap::TextureStorageCubeMap(int levels, D3DFORMAT format, DWORD usage, int size)
2126 : TextureStorage(usage), mFirstRenderTargetSerial(RenderbufferStorage::issueCubeSerials())
2127{
2128 mTexture = NULL;
2129 // if the size is not positive this should be treated as an incomplete texture
2130 // we handle that here by skipping the d3d texture creation
2131 if (size > 0)
2132 {
2133 IDirect3DDevice9 *device = getDevice();
jbauman@chromium.org68715282012-07-12 23:28:41 +00002134 int height = size;
daniel@transgaming.com4bb04be2012-10-17 18:29:55 +00002135 MakeValidSize(false, dx::IsCompressedFormat(format), &size, &height, &mLodOffset);
sminns@adobe.comce1189b2012-09-18 20:06:35 +00002136 HRESULT result = device->CreateCubeTexture(size, levels ? levels + mLodOffset : 0, getUsage(), format, getPool(), &mTexture, NULL);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002137
2138 if (FAILED(result))
2139 {
2140 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
2141 error(GL_OUT_OF_MEMORY);
2142 }
2143 }
2144}
2145
2146TextureStorageCubeMap::~TextureStorageCubeMap()
2147{
2148 if (mTexture)
2149 {
2150 mTexture->Release();
2151 }
2152}
2153
2154// Increments refcount on surface.
2155// caller must Release() the returned surface
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002156IDirect3DSurface9 *TextureStorageCubeMap::getCubeMapSurface(GLenum faceTarget, int level, bool dirty)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002157{
2158 IDirect3DSurface9 *surface = NULL;
2159
2160 if (mTexture)
2161 {
daniel@transgaming.com1ee986b2012-09-27 17:46:12 +00002162 D3DCUBEMAP_FACES face = es2dx::ConvertCubeFace(faceTarget);
2163 HRESULT result = mTexture->GetCubeMapSurface(face, level + mLodOffset, &surface);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002164 ASSERT(SUCCEEDED(result));
daniel@transgaming.com1ee986b2012-09-27 17:46:12 +00002165
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002166 // With managed textures the driver needs to be informed of updates to the lower mipmap levels
2167 if (level != 0 && isManaged() && dirty)
daniel@transgaming.com1ee986b2012-09-27 17:46:12 +00002168 {
2169 mTexture->AddDirtyRect(face, NULL);
2170 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002171 }
2172
2173 return surface;
2174}
2175
2176IDirect3DBaseTexture9 *TextureStorageCubeMap::getBaseTexture() const
2177{
2178 return mTexture;
2179}
2180
2181unsigned int TextureStorageCubeMap::getRenderTargetSerial(GLenum target) const
2182{
2183 return mFirstRenderTargetSerial + TextureCubeMap::faceIndex(target);
2184}
2185
2186TextureCubeMap::TextureCubeMap(GLuint id) : Texture(id)
2187{
2188 mTexStorage = NULL;
2189 for (int i = 0; i < 6; i++)
2190 {
2191 mFaceProxies[i] = NULL;
2192 mFaceProxyRefs[i] = 0;
2193 }
2194}
2195
2196TextureCubeMap::~TextureCubeMap()
2197{
2198 for (int i = 0; i < 6; i++)
2199 {
2200 mFaceProxies[i] = NULL;
2201 }
2202
2203 delete mTexStorage;
2204 mTexStorage = NULL;
2205}
2206
2207// We need to maintain a count of references to renderbuffers acting as
2208// proxies for this texture, so that the texture is not deleted while
2209// proxy references still exist. If the reference count drops to zero,
2210// we set our proxy pointer NULL, so that a new attempt at referencing
2211// will cause recreation.
2212void TextureCubeMap::addProxyRef(const Renderbuffer *proxy)
2213{
2214 for (int i = 0; i < 6; i++)
2215 {
2216 if (mFaceProxies[i] == proxy)
2217 mFaceProxyRefs[i]++;
2218 }
2219}
2220
2221void TextureCubeMap::releaseProxy(const Renderbuffer *proxy)
2222{
2223 for (int i = 0; i < 6; i++)
2224 {
2225 if (mFaceProxies[i] == proxy)
2226 {
2227 if (mFaceProxyRefs[i] > 0)
2228 mFaceProxyRefs[i]--;
2229
2230 if (mFaceProxyRefs[i] == 0)
2231 mFaceProxies[i] = NULL;
2232 }
2233 }
2234}
2235
2236GLenum TextureCubeMap::getTarget() const
2237{
2238 return GL_TEXTURE_CUBE_MAP;
2239}
2240
2241GLsizei TextureCubeMap::getWidth(GLenum target, GLint level) const
2242{
2243 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
2244 return mImageArray[faceIndex(target)][level].getWidth();
2245 else
2246 return 0;
2247}
2248
2249GLsizei TextureCubeMap::getHeight(GLenum target, GLint level) const
2250{
2251 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
2252 return mImageArray[faceIndex(target)][level].getHeight();
2253 else
2254 return 0;
2255}
2256
2257GLenum TextureCubeMap::getInternalFormat(GLenum target, GLint level) const
2258{
2259 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002260 return mImageArray[faceIndex(target)][level].getInternalFormat();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002261 else
2262 return GL_NONE;
2263}
2264
2265D3DFORMAT TextureCubeMap::getD3DFormat(GLenum target, GLint level) const
2266{
2267 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
2268 return mImageArray[faceIndex(target)][level].getD3DFormat();
2269 else
2270 return D3DFMT_UNKNOWN;
2271}
2272
2273void TextureCubeMap::setImagePosX(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
2274{
2275 setImage(0, level, width, height, format, type, unpackAlignment, pixels);
2276}
2277
2278void TextureCubeMap::setImageNegX(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
2279{
2280 setImage(1, level, width, height, format, type, unpackAlignment, pixels);
2281}
2282
2283void TextureCubeMap::setImagePosY(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
2284{
2285 setImage(2, level, width, height, format, type, unpackAlignment, pixels);
2286}
2287
2288void TextureCubeMap::setImageNegY(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
2289{
2290 setImage(3, level, width, height, format, type, unpackAlignment, pixels);
2291}
2292
2293void TextureCubeMap::setImagePosZ(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
2294{
2295 setImage(4, level, width, height, format, type, unpackAlignment, pixels);
2296}
2297
2298void TextureCubeMap::setImageNegZ(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
2299{
2300 setImage(5, level, width, height, format, type, unpackAlignment, pixels);
2301}
2302
2303void TextureCubeMap::setCompressedImage(GLenum face, GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
2304{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002305 // compressed formats don't have separate sized internal formats-- we can just use the compressed format directly
2306 redefineImage(faceIndex(face), level, format, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002307
2308 Texture::setCompressedImage(imageSize, pixels, &mImageArray[faceIndex(face)][level]);
2309}
2310
2311void TextureCubeMap::commitRect(int face, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
2312{
2313 ASSERT(mImageArray[face][level].getSurface() != NULL);
2314
2315 if (level < levelCount())
2316 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002317 IDirect3DSurface9 *destLevel = mTexStorage->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, level, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002318 ASSERT(destLevel != NULL);
2319
2320 if (destLevel != NULL)
2321 {
2322 Image *image = &mImageArray[face][level];
2323 image->updateSurface(destLevel, xoffset, yoffset, width, height);
2324
2325 destLevel->Release();
2326 image->markClean();
2327 }
2328 }
2329}
2330
2331void TextureCubeMap::subImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
2332{
2333 if (Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[faceIndex(target)][level]))
2334 {
2335 commitRect(faceIndex(target), level, xoffset, yoffset, width, height);
2336 }
2337}
2338
2339void TextureCubeMap::subImageCompressed(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
2340{
2341 if (Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, &mImageArray[faceIndex(target)][level]))
2342 {
2343 commitRect(faceIndex(target), level, xoffset, yoffset, width, height);
2344 }
2345}
2346
2347// Tests for cube map sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 86.
2348bool TextureCubeMap::isSamplerComplete() const
2349{
2350 int size = mImageArray[0][0].getWidth();
2351
2352 bool mipmapping;
2353
2354 switch (mMinFilter)
2355 {
2356 case GL_NEAREST:
2357 case GL_LINEAR:
2358 mipmapping = false;
2359 break;
2360 case GL_NEAREST_MIPMAP_NEAREST:
2361 case GL_LINEAR_MIPMAP_NEAREST:
2362 case GL_NEAREST_MIPMAP_LINEAR:
2363 case GL_LINEAR_MIPMAP_LINEAR:
2364 mipmapping = true;
2365 break;
2366 default:
2367 UNREACHABLE();
2368 return false;
2369 }
2370
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002371 if ((gl::ExtractType(getInternalFormat(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0)) == GL_FLOAT && !getContext()->supportsFloat32LinearFilter()) ||
2372 (gl::ExtractType(getInternalFormat(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0) == GL_HALF_FLOAT_OES) && !getContext()->supportsFloat16LinearFilter()))
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002373 {
2374 if (mMagFilter != GL_NEAREST || (mMinFilter != GL_NEAREST && mMinFilter != GL_NEAREST_MIPMAP_NEAREST))
2375 {
2376 return false;
2377 }
2378 }
2379
2380 if (!isPow2(size) && !getContext()->supportsNonPower2Texture())
2381 {
2382 if (getWrapS() != GL_CLAMP_TO_EDGE || getWrapT() != GL_CLAMP_TO_EDGE || mipmapping)
2383 {
2384 return false;
2385 }
2386 }
2387
2388 if (!mipmapping)
2389 {
2390 if (!isCubeComplete())
2391 {
2392 return false;
2393 }
2394 }
2395 else
2396 {
2397 if (!isMipmapCubeComplete()) // Also tests for isCubeComplete()
2398 {
2399 return false;
2400 }
2401 }
2402
2403 return true;
2404}
2405
2406// Tests for cube texture completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
2407bool TextureCubeMap::isCubeComplete() const
2408{
2409 if (mImageArray[0][0].getWidth() <= 0 || mImageArray[0][0].getHeight() != mImageArray[0][0].getWidth())
2410 {
2411 return false;
2412 }
2413
2414 for (unsigned int face = 1; face < 6; face++)
2415 {
2416 if (mImageArray[face][0].getWidth() != mImageArray[0][0].getWidth() ||
2417 mImageArray[face][0].getWidth() != mImageArray[0][0].getHeight() ||
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002418 mImageArray[face][0].getInternalFormat() != mImageArray[0][0].getInternalFormat())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002419 {
2420 return false;
2421 }
2422 }
2423
2424 return true;
2425}
2426
2427bool TextureCubeMap::isMipmapCubeComplete() const
2428{
2429 if (isImmutable())
2430 {
2431 return true;
2432 }
2433
2434 if (!isCubeComplete())
2435 {
2436 return false;
2437 }
2438
2439 GLsizei size = mImageArray[0][0].getWidth();
2440
2441 int q = log2(size);
2442
2443 for (int face = 0; face < 6; face++)
2444 {
2445 for (int level = 1; level <= q; level++)
2446 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002447 if (mImageArray[face][level].getInternalFormat() != mImageArray[0][0].getInternalFormat())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002448 {
2449 return false;
2450 }
2451
2452 if (mImageArray[face][level].getWidth() != std::max(1, size >> level))
2453 {
2454 return false;
2455 }
2456 }
2457 }
2458
2459 return true;
2460}
2461
2462bool TextureCubeMap::isCompressed(GLenum target, GLint level) const
2463{
2464 return IsCompressed(getInternalFormat(target, level));
2465}
2466
2467IDirect3DBaseTexture9 *TextureCubeMap::getBaseTexture() const
2468{
2469 return mTexStorage ? mTexStorage->getBaseTexture() : NULL;
2470}
2471
2472// Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one
2473void TextureCubeMap::createTexture()
2474{
2475 GLsizei size = mImageArray[0][0].getWidth();
daniel@transgaming.come6a09842012-09-17 21:28:55 +00002476
2477 if (!(size > 0))
2478 return; // do not attempt to create d3d textures for nonexistant data
2479
sminns@adobe.comce1189b2012-09-18 20:06:35 +00002480 GLint levels = creationLevels(size);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002481 D3DFORMAT d3dfmt = mImageArray[0][0].getD3DFormat();
2482 DWORD d3dusage = GetTextureUsage(d3dfmt, mUsage, false);
2483
2484 delete mTexStorage;
2485 mTexStorage = new TextureStorageCubeMap(levels, d3dfmt, d3dusage, size);
2486
2487 if (mTexStorage->isManaged())
2488 {
2489 int levels = levelCount();
2490
2491 for (int face = 0; face < 6; face++)
2492 {
2493 for (int level = 0; level < levels; level++)
2494 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002495 IDirect3DSurface9 *surface = mTexStorage->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, level, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002496 mImageArray[face][level].setManagedSurface(surface);
2497 }
2498 }
2499 }
2500
2501 mDirtyImages = true;
2502}
2503
2504void TextureCubeMap::updateTexture()
2505{
2506 for (int face = 0; face < 6; face++)
2507 {
2508 int levels = levelCount();
2509 for (int level = 0; level < levels; level++)
2510 {
2511 Image *image = &mImageArray[face][level];
2512
2513 if (image->isDirty())
2514 {
2515 commitRect(face, level, 0, 0, image->getWidth(), image->getHeight());
2516 }
2517 }
2518 }
2519}
2520
2521void TextureCubeMap::convertToRenderTarget()
2522{
2523 TextureStorageCubeMap *newTexStorage = NULL;
2524
2525 if (mImageArray[0][0].getWidth() != 0)
2526 {
2527 GLsizei size = mImageArray[0][0].getWidth();
sminns@adobe.comce1189b2012-09-18 20:06:35 +00002528 GLint levels = creationLevels(size);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002529 D3DFORMAT d3dfmt = mImageArray[0][0].getD3DFormat();
2530 DWORD d3dusage = GetTextureUsage(d3dfmt, GL_FRAMEBUFFER_ATTACHMENT_ANGLE, true);
2531
2532 newTexStorage = new TextureStorageCubeMap(levels, d3dfmt, d3dusage, size);
2533
2534 if (mTexStorage != NULL)
2535 {
2536 int levels = levelCount();
2537 for (int f = 0; f < 6; f++)
2538 {
2539 for (int i = 0; i < levels; i++)
2540 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002541 IDirect3DSurface9 *source = mTexStorage->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + f, i, false);
2542 IDirect3DSurface9 *dest = newTexStorage->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + f, i, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002543
2544 if (!copyToRenderTarget(dest, source, mTexStorage->isManaged()))
2545 {
2546 delete newTexStorage;
2547 if (source) source->Release();
2548 if (dest) dest->Release();
2549 return error(GL_OUT_OF_MEMORY);
2550 }
2551
2552 if (source) source->Release();
2553 if (dest) dest->Release();
2554 }
2555 }
2556 }
2557 }
2558
2559 delete mTexStorage;
2560 mTexStorage = newTexStorage;
2561
2562 mDirtyImages = true;
2563}
2564
2565void TextureCubeMap::setImage(int faceIndex, GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
2566{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002567 GLint internalformat = ConvertSizedInternalFormat(format, type);
2568 redefineImage(faceIndex, level, internalformat, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002569
2570 Texture::setImage(unpackAlignment, pixels, &mImageArray[faceIndex][level]);
2571}
2572
2573unsigned int TextureCubeMap::faceIndex(GLenum face)
2574{
2575 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_X - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 1);
2576 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 2);
2577 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 3);
2578 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 4);
2579 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 5);
2580
2581 return face - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
2582}
2583
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002584void TextureCubeMap::redefineImage(int face, GLint level, GLint internalformat, GLsizei width, GLsizei height)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002585{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002586 bool redefined = mImageArray[face][level].redefine(internalformat, width, height, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002587
2588 if (mTexStorage && redefined)
2589 {
2590 for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
2591 {
2592 for (int f = 0; f < 6; f++)
2593 {
2594 mImageArray[f][i].markDirty();
2595 }
2596 }
2597
2598 delete mTexStorage;
2599 mTexStorage = NULL;
2600
2601 mDirtyImages = true;
2602 }
2603}
2604
2605void TextureCubeMap::copyImage(GLenum target, GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
2606{
2607 IDirect3DSurface9 *renderTarget = source->getRenderTarget();
2608
2609 if (!renderTarget)
2610 {
2611 ERR("Failed to retrieve the render target.");
2612 return error(GL_OUT_OF_MEMORY);
2613 }
2614
2615 unsigned int faceindex = faceIndex(target);
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002616 GLint internalformat = gl::ConvertSizedInternalFormat(format, GL_UNSIGNED_BYTE);
2617 redefineImage(faceindex, level, internalformat, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002618
2619 if (!mImageArray[faceindex][level].isRenderableFormat())
2620 {
2621 mImageArray[faceindex][level].copy(0, 0, x, y, width, height, renderTarget);
2622 mDirtyImages = true;
2623 }
2624 else
2625 {
2626 if (!mTexStorage || !mTexStorage->isRenderTarget())
2627 {
2628 convertToRenderTarget();
2629 }
2630
2631 mImageArray[faceindex][level].markClean();
2632
2633 ASSERT(width == height);
2634
2635 if (width > 0 && level < levelCount())
2636 {
2637 RECT sourceRect;
2638 sourceRect.left = x;
2639 sourceRect.right = x + width;
2640 sourceRect.top = y;
2641 sourceRect.bottom = y + height;
2642
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002643 IDirect3DSurface9 *dest = mTexStorage->getCubeMapSurface(target, level, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002644
2645 if (dest)
2646 {
2647 getBlitter()->copy(renderTarget, sourceRect, format, 0, 0, dest);
2648 dest->Release();
2649 }
2650 }
2651 }
2652
2653 renderTarget->Release();
2654}
2655
2656void TextureCubeMap::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
2657{
2658 GLsizei size = mImageArray[faceIndex(target)][level].getWidth();
2659
2660 if (xoffset + width > size || yoffset + height > size)
2661 {
2662 return error(GL_INVALID_VALUE);
2663 }
2664
2665 IDirect3DSurface9 *renderTarget = source->getRenderTarget();
2666
2667 if (!renderTarget)
2668 {
2669 ERR("Failed to retrieve the render target.");
2670 return error(GL_OUT_OF_MEMORY);
2671 }
2672
2673 unsigned int faceindex = faceIndex(target);
2674
2675 if (!mImageArray[faceindex][level].isRenderableFormat() || (!mTexStorage && !isSamplerComplete()))
2676 {
2677 mImageArray[faceindex][level].copy(0, 0, x, y, width, height, renderTarget);
2678 mDirtyImages = true;
2679 }
2680 else
2681 {
2682 if (!mTexStorage || !mTexStorage->isRenderTarget())
2683 {
2684 convertToRenderTarget();
2685 }
2686
2687 updateTexture();
2688
2689 if (level < levelCount())
2690 {
2691 RECT sourceRect;
2692 sourceRect.left = x;
2693 sourceRect.right = x + width;
2694 sourceRect.top = y;
2695 sourceRect.bottom = y + height;
2696
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002697 IDirect3DSurface9 *dest = mTexStorage->getCubeMapSurface(target, level, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002698
2699 if (dest)
2700 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002701 getBlitter()->copy(renderTarget, sourceRect, gl::ExtractFormat(mImageArray[0][0].getInternalFormat()), xoffset, yoffset, dest);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002702 dest->Release();
2703 }
2704 }
2705 }
2706
2707 renderTarget->Release();
2708}
2709
2710void TextureCubeMap::storage(GLsizei levels, GLenum internalformat, GLsizei size)
2711{
daniel@transgaming.com6b1a0a02012-10-17 18:22:47 +00002712 D3DFORMAT d3dfmt = ConvertTextureInternalFormat(internalformat);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002713 DWORD d3dusage = GetTextureUsage(d3dfmt, mUsage, false);
2714
2715 delete mTexStorage;
2716 mTexStorage = new TextureStorageCubeMap(levels, d3dfmt, d3dusage, size);
2717 mImmutable = true;
2718
2719 for (int level = 0; level < levels; level++)
2720 {
2721 for (int face = 0; face < 6; face++)
2722 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002723 mImageArray[face][level].redefine(internalformat, size, size, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002724 size = std::max(1, size >> 1);
2725 }
2726 }
2727
2728 for (int level = levels; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
2729 {
2730 for (int face = 0; face < 6; face++)
2731 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002732 mImageArray[face][level].redefine(GL_NONE, 0, 0, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002733 }
2734 }
2735
2736 if (mTexStorage->isManaged())
2737 {
2738 int levels = levelCount();
2739
2740 for (int face = 0; face < 6; face++)
2741 {
2742 for (int level = 0; level < levels; level++)
2743 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002744 IDirect3DSurface9 *surface = mTexStorage->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, level, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002745 mImageArray[face][level].setManagedSurface(surface);
2746 }
2747 }
2748 }
2749}
2750
2751void TextureCubeMap::generateMipmaps()
2752{
2753 if (!isCubeComplete())
2754 {
2755 return error(GL_INVALID_OPERATION);
2756 }
2757
2758 if (!getContext()->supportsNonPower2Texture())
2759 {
2760 if (!isPow2(mImageArray[0][0].getWidth()))
2761 {
2762 return error(GL_INVALID_OPERATION);
2763 }
2764 }
2765
2766 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
2767 unsigned int q = log2(mImageArray[0][0].getWidth());
2768 for (unsigned int f = 0; f < 6; f++)
2769 {
2770 for (unsigned int i = 1; i <= q; i++)
2771 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002772 redefineImage(f, i, mImageArray[f][0].getInternalFormat(),
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002773 std::max(mImageArray[f][0].getWidth() >> i, 1),
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00002774 std::max(mImageArray[f][0].getWidth() >> i, 1));
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002775 }
2776 }
2777
2778 if (mTexStorage && mTexStorage->isRenderTarget())
2779 {
2780 for (unsigned int f = 0; f < 6; f++)
2781 {
2782 for (unsigned int i = 1; i <= q; i++)
2783 {
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002784 IDirect3DSurface9 *upper = mTexStorage->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + f, i - 1, false);
2785 IDirect3DSurface9 *lower = mTexStorage->getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + f, i, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002786
2787 if (upper != NULL && lower != NULL)
2788 {
2789 getBlitter()->boxFilter(upper, lower);
2790 }
2791
2792 if (upper != NULL) upper->Release();
2793 if (lower != NULL) lower->Release();
2794
2795 mImageArray[f][i].markClean();
2796 }
2797 }
2798 }
2799 else
2800 {
2801 for (unsigned int f = 0; f < 6; f++)
2802 {
2803 for (unsigned int i = 1; i <= q; i++)
2804 {
2805 if (mImageArray[f][i].getSurface() == NULL)
2806 {
2807 return error(GL_OUT_OF_MEMORY);
2808 }
2809
2810 if (FAILED(D3DXLoadSurfaceFromSurface(mImageArray[f][i].getSurface(), NULL, NULL, mImageArray[f][i - 1].getSurface(), NULL, NULL, D3DX_FILTER_BOX, 0)))
2811 {
2812 ERR(" failed to load filter %d to %d.", i - 1, i);
2813 }
2814
2815 mImageArray[f][i].markDirty();
2816 }
2817 }
2818 }
2819}
2820
2821Renderbuffer *TextureCubeMap::getRenderbuffer(GLenum target)
2822{
2823 if (!IsCubemapTextureTarget(target))
2824 {
2825 return error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);
2826 }
2827
2828 unsigned int face = faceIndex(target);
2829
2830 if (mFaceProxies[face] == NULL)
2831 {
2832 mFaceProxies[face] = new Renderbuffer(id(), new RenderbufferTextureCubeMap(this, target));
2833 }
2834
2835 return mFaceProxies[face];
2836}
2837
2838// Increments refcount on surface.
2839// caller must Release() the returned surface
2840IDirect3DSurface9 *TextureCubeMap::getRenderTarget(GLenum target)
2841{
2842 ASSERT(IsCubemapTextureTarget(target));
2843
2844 // ensure the underlying texture is created
2845 if (getStorage(true) == NULL)
2846 {
2847 return NULL;
2848 }
2849
2850 updateTexture();
2851
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002852 return mTexStorage->getCubeMapSurface(target, 0, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00002853}
2854
2855TextureStorage *TextureCubeMap::getStorage(bool renderTarget)
2856{
2857 if (!mTexStorage || (renderTarget && !mTexStorage->isRenderTarget()))
2858 {
2859 if (renderTarget)
2860 {
2861 convertToRenderTarget();
2862 }
2863 else
2864 {
2865 createTexture();
2866 }
2867 }
2868
2869 return mTexStorage;
2870}
2871
2872}