blob: 7af5556b3f90b5e7583a0d7b533e4788a7e0f5d6 [file] [log] [blame]
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001//
2// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
3// 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>
16
17#include "common/debug.h"
18
19#include "libGLESv2/main.h"
20#include "libGLESv2/mathutil.h"
21#include "libGLESv2/utilities.h"
22#include "libGLESv2/Blit.h"
23#include "libGLESv2/Framebuffer.h"
24
25namespace gl
26{
27
28Texture::Image::Image()
29 : width(0), height(0), dirty(false), surface(NULL), format(GL_NONE)
30{
31}
32
33Texture::Image::~Image()
34{
35 if (surface) surface->Release();
36}
37
38Texture::Texture(GLuint id) : RefCountObject(id)
39{
daniel@transgaming.comaed18322011-03-21 16:38:13 +000040 mWidth = 0;
41 mHeight = 0;
42
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +000043 mMinFilter = GL_NEAREST_MIPMAP_LINEAR;
44 mMagFilter = GL_LINEAR;
45 mWrapS = GL_REPEAT;
46 mWrapT = GL_REPEAT;
daniel@transgaming.comaed18322011-03-21 16:38:13 +000047 mDirtyParameters = true;
48
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +000049 mIsRenderable = false;
50 mType = GL_UNSIGNED_BYTE;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +000051}
52
53Texture::~Texture()
54{
55}
56
57Blit *Texture::getBlitter()
58{
59 Context *context = getContext();
60 return context->getBlitter();
61}
62
63// Returns true on successful filter state update (valid enum parameter)
64bool Texture::setMinFilter(GLenum filter)
65{
66 switch (filter)
67 {
68 case GL_NEAREST:
69 case GL_LINEAR:
70 case GL_NEAREST_MIPMAP_NEAREST:
71 case GL_LINEAR_MIPMAP_NEAREST:
72 case GL_NEAREST_MIPMAP_LINEAR:
73 case GL_LINEAR_MIPMAP_LINEAR:
74 {
75 if (mMinFilter != filter)
76 {
77 mMinFilter = filter;
daniel@transgaming.comaed18322011-03-21 16:38:13 +000078 mDirtyParameters = true;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +000079 }
80 return true;
81 }
82 default:
83 return false;
84 }
85}
86
87// Returns true on successful filter state update (valid enum parameter)
88bool Texture::setMagFilter(GLenum filter)
89{
90 switch (filter)
91 {
92 case GL_NEAREST:
93 case GL_LINEAR:
94 {
95 if (mMagFilter != filter)
96 {
97 mMagFilter = filter;
daniel@transgaming.comaed18322011-03-21 16:38:13 +000098 mDirtyParameters = true;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +000099 }
100 return true;
101 }
102 default:
103 return false;
104 }
105}
106
107// Returns true on successful wrap state update (valid enum parameter)
108bool Texture::setWrapS(GLenum wrap)
109{
110 switch (wrap)
111 {
112 case GL_REPEAT:
113 case GL_CLAMP_TO_EDGE:
114 case GL_MIRRORED_REPEAT:
115 {
116 if (mWrapS != wrap)
117 {
118 mWrapS = wrap;
daniel@transgaming.comaed18322011-03-21 16:38:13 +0000119 mDirtyParameters = true;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +0000120 }
121 return true;
122 }
123 default:
124 return false;
125 }
126}
127
128// Returns true on successful wrap state update (valid enum parameter)
129bool Texture::setWrapT(GLenum wrap)
130{
131 switch (wrap)
132 {
133 case GL_REPEAT:
134 case GL_CLAMP_TO_EDGE:
135 case GL_MIRRORED_REPEAT:
136 {
137 if (mWrapT != wrap)
138 {
139 mWrapT = wrap;
daniel@transgaming.comaed18322011-03-21 16:38:13 +0000140 mDirtyParameters = true;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +0000141 }
142 return true;
143 }
144 default:
145 return false;
146 }
147}
148
149GLenum Texture::getMinFilter() const
150{
151 return mMinFilter;
152}
153
154GLenum Texture::getMagFilter() const
155{
156 return mMagFilter;
157}
158
159GLenum Texture::getWrapS() const
160{
161 return mWrapS;
162}
163
164GLenum Texture::getWrapT() const
165{
166 return mWrapT;
167}
168
169GLsizei Texture::getWidth() const
170{
171 return mWidth;
172}
173
174GLsizei Texture::getHeight() const
175{
176 return mHeight;
177}
178
179bool Texture::isFloatingPoint() const
180{
181 return (mType == GL_FLOAT || mType == GL_HALF_FLOAT_OES);
182}
183
184bool Texture::isRenderableFormat() const
185{
186 D3DFORMAT format = getD3DFormat();
187
188 switch(format)
189 {
190 case D3DFMT_L8:
191 case D3DFMT_A8L8:
192 case D3DFMT_DXT1:
193 return false;
194 case D3DFMT_A8R8G8B8:
195 case D3DFMT_X8R8G8B8:
196 case D3DFMT_A16B16G16R16F:
197 case D3DFMT_A32B32G32R32F:
198 return true;
199 default:
200 UNREACHABLE();
201 }
202
203 return false;
204}
205
206// Selects an internal Direct3D 9 format for storing an Image
207D3DFORMAT Texture::selectFormat(GLenum format, GLenum type)
208{
209 if (format == GL_COMPRESSED_RGB_S3TC_DXT1_EXT ||
210 format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT)
211 {
212 return D3DFMT_DXT1;
213 }
214 else if (type == GL_FLOAT)
215 {
216 return D3DFMT_A32B32G32R32F;
217 }
218 else if (type == GL_HALF_FLOAT_OES)
219 {
220 return D3DFMT_A16B16G16R16F;
221 }
222 else if (type == GL_UNSIGNED_BYTE)
223 {
224 if (format == GL_LUMINANCE && getContext()->supportsLuminanceTextures())
225 {
226 return D3DFMT_L8;
227 }
228 else if (format == GL_LUMINANCE_ALPHA && getContext()->supportsLuminanceAlphaTextures())
229 {
230 return D3DFMT_A8L8;
231 }
232 else if (format == GL_RGB)
233 {
234 return D3DFMT_X8R8G8B8;
235 }
236
237 return D3DFMT_A8R8G8B8;
238 }
239
240 return D3DFMT_A8R8G8B8;
241}
242
243// Store the pixel rectangle designated by xoffset,yoffset,width,height with pixels stored as format/type at input
244// into the target pixel rectangle at output with outputPitch bytes in between each line.
245void Texture::loadImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type,
246 GLint unpackAlignment, const void *input, size_t outputPitch, void *output, D3DSURFACE_DESC *description) const
247{
248 GLsizei inputPitch = -ComputePitch(width, format, type, unpackAlignment);
249 input = ((char*)input) - inputPitch * (height - 1);
250
251 switch (type)
252 {
253 case GL_UNSIGNED_BYTE:
254 switch (format)
255 {
256 case GL_ALPHA:
257 loadAlphaImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
258 break;
259 case GL_LUMINANCE:
260 loadLuminanceImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output, description->Format == D3DFMT_L8);
261 break;
262 case GL_LUMINANCE_ALPHA:
263 loadLuminanceAlphaImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output, description->Format == D3DFMT_A8L8);
264 break;
265 case GL_RGB:
266 loadRGBUByteImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
267 break;
268 case GL_RGBA:
269 loadRGBAUByteImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
270 break;
271 case GL_BGRA_EXT:
272 loadBGRAImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
273 break;
274 default: UNREACHABLE();
275 }
276 break;
277 case GL_UNSIGNED_SHORT_5_6_5:
278 switch (format)
279 {
280 case GL_RGB:
281 loadRGB565ImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
282 break;
283 default: UNREACHABLE();
284 }
285 break;
286 case GL_UNSIGNED_SHORT_4_4_4_4:
287 switch (format)
288 {
289 case GL_RGBA:
290 loadRGBA4444ImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
291 break;
292 default: UNREACHABLE();
293 }
294 break;
295 case GL_UNSIGNED_SHORT_5_5_5_1:
296 switch (format)
297 {
298 case GL_RGBA:
299 loadRGBA5551ImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
300 break;
301 default: UNREACHABLE();
302 }
303 break;
304 case GL_FLOAT:
305 switch (format)
306 {
307 // float textures are converted to RGBA, not BGRA, as they're stored that way in D3D
308 case GL_ALPHA:
309 loadAlphaFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
310 break;
311 case GL_LUMINANCE:
312 loadLuminanceFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
313 break;
314 case GL_LUMINANCE_ALPHA:
315 loadLuminanceAlphaFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
316 break;
317 case GL_RGB:
318 loadRGBFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
319 break;
320 case GL_RGBA:
321 loadRGBAFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
322 break;
323 default: UNREACHABLE();
324 }
325 break;
326 case GL_HALF_FLOAT_OES:
327 switch (format)
328 {
329 // float textures are converted to RGBA, not BGRA, as they're stored that way in D3D
330 case GL_ALPHA:
331 loadAlphaHalfFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
332 break;
333 case GL_LUMINANCE:
334 loadLuminanceHalfFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
335 break;
336 case GL_LUMINANCE_ALPHA:
337 loadLuminanceAlphaHalfFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
338 break;
339 case GL_RGB:
340 loadRGBHalfFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
341 break;
342 case GL_RGBA:
343 loadRGBAHalfFloatImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
344 break;
345 default: UNREACHABLE();
346 }
347 break;
348 default: UNREACHABLE();
349 }
350}
351
352void Texture::loadAlphaImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
353 int inputPitch, const void *input, size_t outputPitch, void *output) const
354{
355 const unsigned char *source = NULL;
356 unsigned char *dest = NULL;
357
358 for (int y = 0; y < height; y++)
359 {
360 source = static_cast<const unsigned char*>(input) + y * inputPitch;
361 dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
362 for (int x = 0; x < width; x++)
363 {
364 dest[4 * x + 0] = 0;
365 dest[4 * x + 1] = 0;
366 dest[4 * x + 2] = 0;
367 dest[4 * x + 3] = source[x];
368 }
369 }
370}
371
372void Texture::loadAlphaFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
373 int inputPitch, const void *input, size_t outputPitch, void *output) const
374{
375 const float *source = NULL;
376 float *dest = NULL;
377
378 for (int y = 0; y < height; y++)
379 {
380 source = reinterpret_cast<const float*>(static_cast<const unsigned char*>(input) + y * inputPitch);
381 dest = reinterpret_cast<float*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 16);
382 for (int x = 0; x < width; x++)
383 {
384 dest[4 * x + 0] = 0;
385 dest[4 * x + 1] = 0;
386 dest[4 * x + 2] = 0;
387 dest[4 * x + 3] = source[x];
388 }
389 }
390}
391
392void Texture::loadAlphaHalfFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
393 int inputPitch, const void *input, size_t outputPitch, void *output) const
394{
395 const unsigned short *source = NULL;
396 unsigned short *dest = NULL;
397
398 for (int y = 0; y < height; y++)
399 {
400 source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
401 dest = reinterpret_cast<unsigned short*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 8);
402 for (int x = 0; x < width; x++)
403 {
404 dest[4 * x + 0] = 0;
405 dest[4 * x + 1] = 0;
406 dest[4 * x + 2] = 0;
407 dest[4 * x + 3] = source[x];
408 }
409 }
410}
411
412void Texture::loadLuminanceImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
413 int inputPitch, const void *input, size_t outputPitch, void *output, bool native) const
414{
415 const int destBytesPerPixel = native? 1: 4;
416 const unsigned char *source = NULL;
417 unsigned char *dest = NULL;
418
419 for (int y = 0; y < height; y++)
420 {
421 source = static_cast<const unsigned char*>(input) + y * inputPitch;
422 dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * destBytesPerPixel;
423
424 if (!native) // BGRA8 destination format
425 {
426 for (int x = 0; x < width; x++)
427 {
428 dest[4 * x + 0] = source[x];
429 dest[4 * x + 1] = source[x];
430 dest[4 * x + 2] = source[x];
431 dest[4 * x + 3] = 0xFF;
432 }
433 }
434 else // L8 destination format
435 {
436 memcpy(dest, source, width);
437 }
438 }
439}
440
441void Texture::loadLuminanceFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
442 int inputPitch, const void *input, size_t outputPitch, void *output) const
443{
444 const float *source = NULL;
445 float *dest = NULL;
446
447 for (int y = 0; y < height; y++)
448 {
449 source = reinterpret_cast<const float*>(static_cast<const unsigned char*>(input) + y * inputPitch);
450 dest = reinterpret_cast<float*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 16);
451 for (int x = 0; x < width; x++)
452 {
453 dest[4 * x + 0] = source[x];
454 dest[4 * x + 1] = source[x];
455 dest[4 * x + 2] = source[x];
456 dest[4 * x + 3] = 1.0f;
457 }
458 }
459}
460
461void Texture::loadLuminanceHalfFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
462 int inputPitch, const void *input, size_t outputPitch, void *output) const
463{
464 const unsigned short *source = NULL;
465 unsigned short *dest = NULL;
466
467 for (int y = 0; y < height; y++)
468 {
469 source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
470 dest = reinterpret_cast<unsigned short*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 8);
471 for (int x = 0; x < width; x++)
472 {
473 dest[4 * x + 0] = source[x];
474 dest[4 * x + 1] = source[x];
475 dest[4 * x + 2] = source[x];
476 dest[4 * x + 3] = 0x3C00; // SEEEEEMMMMMMMMMM, S = 0, E = 15, M = 0: 16bit flpt representation of 1
477 }
478 }
479}
480
481void Texture::loadLuminanceAlphaImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
482 int inputPitch, const void *input, size_t outputPitch, void *output, bool native) const
483{
484 const int destBytesPerPixel = native? 2: 4;
485 const unsigned char *source = NULL;
486 unsigned char *dest = NULL;
487
488 for (int y = 0; y < height; y++)
489 {
490 source = static_cast<const unsigned char*>(input) + y * inputPitch;
491 dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * destBytesPerPixel;
492
493 if (!native) // BGRA8 destination format
494 {
495 for (int x = 0; x < width; x++)
496 {
497 dest[4 * x + 0] = source[2*x+0];
498 dest[4 * x + 1] = source[2*x+0];
499 dest[4 * x + 2] = source[2*x+0];
500 dest[4 * x + 3] = source[2*x+1];
501 }
502 }
503 else
504 {
505 memcpy(dest, source, width * 2);
506 }
507 }
508}
509
510void Texture::loadLuminanceAlphaFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
511 int inputPitch, const void *input, size_t outputPitch, void *output) const
512{
513 const float *source = NULL;
514 float *dest = NULL;
515
516 for (int y = 0; y < height; y++)
517 {
518 source = reinterpret_cast<const float*>(static_cast<const unsigned char*>(input) + y * inputPitch);
519 dest = reinterpret_cast<float*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 16);
520 for (int x = 0; x < width; x++)
521 {
522 dest[4 * x + 0] = source[2*x+0];
523 dest[4 * x + 1] = source[2*x+0];
524 dest[4 * x + 2] = source[2*x+0];
525 dest[4 * x + 3] = source[2*x+1];
526 }
527 }
528}
529
530void Texture::loadLuminanceAlphaHalfFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
531 int inputPitch, const void *input, size_t outputPitch, void *output) const
532{
533 const unsigned short *source = NULL;
534 unsigned short *dest = NULL;
535
536 for (int y = 0; y < height; y++)
537 {
538 source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
539 dest = reinterpret_cast<unsigned short*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 8);
540 for (int x = 0; x < width; x++)
541 {
542 dest[4 * x + 0] = source[2*x+0];
543 dest[4 * x + 1] = source[2*x+0];
544 dest[4 * x + 2] = source[2*x+0];
545 dest[4 * x + 3] = source[2*x+1];
546 }
547 }
548}
549
550void Texture::loadRGBUByteImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
551 int inputPitch, const void *input, size_t outputPitch, void *output) const
552{
553 const unsigned char *source = NULL;
554 unsigned char *dest = NULL;
555
556 for (int y = 0; y < height; y++)
557 {
558 source = static_cast<const unsigned char*>(input) + y * inputPitch;
559 dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
560 for (int x = 0; x < width; x++)
561 {
562 dest[4 * x + 0] = source[x * 3 + 2];
563 dest[4 * x + 1] = source[x * 3 + 1];
564 dest[4 * x + 2] = source[x * 3 + 0];
565 dest[4 * x + 3] = 0xFF;
566 }
567 }
568}
569
570void Texture::loadRGB565ImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
571 int inputPitch, const void *input, size_t outputPitch, void *output) const
572{
573 const unsigned short *source = NULL;
574 unsigned char *dest = NULL;
575
576 for (int y = 0; y < height; y++)
577 {
578 source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
579 dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
580 for (int x = 0; x < width; x++)
581 {
582 unsigned short rgba = source[x];
583 dest[4 * x + 0] = ((rgba & 0x001F) << 3) | ((rgba & 0x001F) >> 2);
584 dest[4 * x + 1] = ((rgba & 0x07E0) >> 3) | ((rgba & 0x07E0) >> 9);
585 dest[4 * x + 2] = ((rgba & 0xF800) >> 8) | ((rgba & 0xF800) >> 13);
586 dest[4 * x + 3] = 0xFF;
587 }
588 }
589}
590
591void Texture::loadRGBFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
592 int inputPitch, const void *input, size_t outputPitch, void *output) const
593{
594 const float *source = NULL;
595 float *dest = NULL;
596
597 for (int y = 0; y < height; y++)
598 {
599 source = reinterpret_cast<const float*>(static_cast<const unsigned char*>(input) + y * inputPitch);
600 dest = reinterpret_cast<float*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 16);
601 for (int x = 0; x < width; x++)
602 {
603 dest[4 * x + 0] = source[x * 3 + 0];
604 dest[4 * x + 1] = source[x * 3 + 1];
605 dest[4 * x + 2] = source[x * 3 + 2];
606 dest[4 * x + 3] = 1.0f;
607 }
608 }
609}
610
611void Texture::loadRGBHalfFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
612 int inputPitch, const void *input, size_t outputPitch, void *output) const
613{
614 const unsigned short *source = NULL;
615 unsigned short *dest = NULL;
616
617 for (int y = 0; y < height; y++)
618 {
619 source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
620 dest = reinterpret_cast<unsigned short*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 8);
621 for (int x = 0; x < width; x++)
622 {
623 dest[4 * x + 0] = source[x * 3 + 0];
624 dest[4 * x + 1] = source[x * 3 + 1];
625 dest[4 * x + 2] = source[x * 3 + 2];
626 dest[4 * x + 3] = 0x3C00; // SEEEEEMMMMMMMMMM, S = 0, E = 15, M = 0: 16bit flpt representation of 1
627 }
628 }
629}
630
631void Texture::loadRGBAUByteImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
632 int inputPitch, const void *input, size_t outputPitch, void *output) const
633{
634 const unsigned char *source = NULL;
635 unsigned char *dest = NULL;
636
637 for (int y = 0; y < height; y++)
638 {
639 source = static_cast<const unsigned char*>(input) + y * inputPitch;
640 dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
641 for (int x = 0; x < width; x++)
642 {
643 dest[4 * x + 0] = source[x * 4 + 2];
644 dest[4 * x + 1] = source[x * 4 + 1];
645 dest[4 * x + 2] = source[x * 4 + 0];
646 dest[4 * x + 3] = source[x * 4 + 3];
647 }
648 }
649}
650
651void Texture::loadRGBA4444ImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
652 int inputPitch, const void *input, size_t outputPitch, void *output) const
653{
654 const unsigned short *source = NULL;
655 unsigned char *dest = NULL;
656
657 for (int y = 0; y < height; y++)
658 {
659 source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
660 dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
661 for (int x = 0; x < width; x++)
662 {
663 unsigned short rgba = source[x];
664 dest[4 * x + 0] = ((rgba & 0x00F0) << 0) | ((rgba & 0x00F0) >> 4);
665 dest[4 * x + 1] = ((rgba & 0x0F00) >> 4) | ((rgba & 0x0F00) >> 8);
666 dest[4 * x + 2] = ((rgba & 0xF000) >> 8) | ((rgba & 0xF000) >> 12);
667 dest[4 * x + 3] = ((rgba & 0x000F) << 4) | ((rgba & 0x000F) >> 0);
668 }
669 }
670}
671
672void Texture::loadRGBA5551ImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
673 int inputPitch, const void *input, size_t outputPitch, void *output) const
674{
675 const unsigned short *source = NULL;
676 unsigned char *dest = NULL;
677
678 for (int y = 0; y < height; y++)
679 {
680 source = reinterpret_cast<const unsigned short*>(static_cast<const unsigned char*>(input) + y * inputPitch);
681 dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
682 for (int x = 0; x < width; x++)
683 {
684 unsigned short rgba = source[x];
685 dest[4 * x + 0] = ((rgba & 0x003E) << 2) | ((rgba & 0x003E) >> 3);
686 dest[4 * x + 1] = ((rgba & 0x07C0) >> 3) | ((rgba & 0x07C0) >> 8);
687 dest[4 * x + 2] = ((rgba & 0xF800) >> 8) | ((rgba & 0xF800) >> 13);
688 dest[4 * x + 3] = (rgba & 0x0001) ? 0xFF : 0;
689 }
690 }
691}
692
693void Texture::loadRGBAFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
694 int inputPitch, const void *input, size_t outputPitch, void *output) const
695{
696 const float *source = NULL;
697 float *dest = NULL;
698
699 for (int y = 0; y < height; y++)
700 {
701 source = reinterpret_cast<const float*>(static_cast<const unsigned char*>(input) + y * inputPitch);
702 dest = reinterpret_cast<float*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 16);
703 memcpy(dest, source, width * 16);
704 }
705}
706
707void Texture::loadRGBAHalfFloatImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
708 int inputPitch, const void *input, size_t outputPitch, void *output) const
709{
710 const unsigned char *source = NULL;
711 unsigned char *dest = NULL;
712
713 for (int y = 0; y < height; y++)
714 {
715 source = static_cast<const unsigned char*>(input) + y * inputPitch;
716 dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 8;
717 memcpy(dest, source, width * 8);
718 }
719}
720
721void Texture::loadBGRAImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
722 int inputPitch, const void *input, size_t outputPitch, void *output) const
723{
724 const unsigned char *source = NULL;
725 unsigned char *dest = NULL;
726
727 for (int y = 0; y < height; y++)
728 {
729 source = static_cast<const unsigned char*>(input) + y * inputPitch;
730 dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
731 memcpy(dest, source, width*4);
732 }
733}
734
735void Texture::loadCompressedImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
736 int inputPitch, const void *input, size_t outputPitch, void *output) const
737{
738 ASSERT(xoffset % 4 == 0);
739 ASSERT(yoffset % 4 == 0);
740 ASSERT(width % 4 == 0 || width == 2 || width == 1);
741 ASSERT(inputPitch % 8 == 0);
742 ASSERT(outputPitch % 8 == 0);
743
744 const unsigned int *source = reinterpret_cast<const unsigned int*>(input);
745 unsigned int *dest = reinterpret_cast<unsigned int*>(output);
746
747 switch (height)
748 {
749 case 1:
750 // Round width up in case it is 1.
751 for (int x = 0; x < (width + 1) / 2; x += 2)
752 {
753 // First 32-bits is two RGB565 colors shared by tile and does not need to be modified.
754 dest[x] = source[x];
755
756 // Second 32-bits contains 4 rows of 4 2-bit interpolants between the colors, the last 3 rows being unused. No flipping should occur.
757 dest[x + 1] = source[x + 1];
758 }
759 break;
760 case 2:
761 // Round width up in case it is 1.
762 for (int x = 0; x < (width + 1) / 2; x += 2)
763 {
764 // First 32-bits is two RGB565 colors shared by tile and does not need to be modified.
765 dest[x] = source[x];
766
767 // Second 32-bits contains 4 rows of 4 2-bit interpolants between the colors, the last 2 rows being unused. Only the top 2 rows should be flipped.
768 dest[x + 1] = ((source[x + 1] << 8) & 0x0000FF00) |
769 ((source[x + 1] >> 8) & 0x000000FF);
770 }
771 break;
772 default:
773 ASSERT(height % 4 == 0);
774 for (int y = 0; y < height / 4; ++y)
775 {
776 const unsigned int *source = reinterpret_cast<const unsigned int*>(static_cast<const unsigned char*>(input) + y * inputPitch);
777 unsigned int *dest = reinterpret_cast<unsigned int*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 8);
778
779 // Round width up in case it is 1.
780 for (int x = 0; x < (width + 1) / 2; x += 2)
781 {
782 // First 32-bits is two RGB565 colors shared by tile and does not need to be modified.
783 dest[x] = source[x];
784
785 // Second 32-bits contains 4 rows of 4 2-bit interpolants between the colors. All rows should be flipped.
786 dest[x + 1] = (source[x + 1] >> 24) |
787 ((source[x + 1] << 8) & 0x00FF0000) |
788 ((source[x + 1] >> 8) & 0x0000FF00) |
789 (source[x + 1] << 24);
790 }
791 }
792 break;
793 }
794}
795
796void Texture::createSurface(GLsizei width, GLsizei height, GLenum format, GLenum type, Image *img)
797{
798 IDirect3DTexture9 *newTexture = NULL;
799 IDirect3DSurface9 *newSurface = NULL;
800
801 if (width != 0 && height != 0)
802 {
803 int levelToFetch = 0;
804 GLsizei requestWidth = width;
805 GLsizei requestHeight = height;
806 if (IsCompressed(format) && (width % 4 != 0 || height % 4 != 0))
807 {
808 bool isMult4 = false;
809 int upsampleCount = 0;
810 while (!isMult4)
811 {
812 requestWidth <<= 1;
813 requestHeight <<= 1;
814 upsampleCount++;
815 if (requestWidth % 4 == 0 && requestHeight % 4 == 0)
816 {
817 isMult4 = true;
818 }
819 }
820 levelToFetch = upsampleCount;
821 }
822
823 HRESULT result = getDevice()->CreateTexture(requestWidth, requestHeight, levelToFetch + 1, NULL, selectFormat(format, type),
824 D3DPOOL_SYSTEMMEM, &newTexture, NULL);
825
826 if (FAILED(result))
827 {
828 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
829 return error(GL_OUT_OF_MEMORY);
830 }
831
832 newTexture->GetSurfaceLevel(levelToFetch, &newSurface);
833 newTexture->Release();
834 }
835
836 if (img->surface) img->surface->Release();
837 img->surface = newSurface;
838
839 img->width = width;
840 img->height = height;
841 img->format = format;
842}
843
844void Texture::setImage(GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, Image *img)
845{
846 createSurface(width, height, format, type, img);
847
848 if (pixels != NULL && img->surface != NULL)
849 {
850 D3DSURFACE_DESC description;
851 img->surface->GetDesc(&description);
852
853 D3DLOCKED_RECT locked;
854 HRESULT result = img->surface->LockRect(&locked, NULL, 0);
855
856 ASSERT(SUCCEEDED(result));
857
858 if (SUCCEEDED(result))
859 {
860 loadImageData(0, 0, width, height, format, type, unpackAlignment, pixels, locked.Pitch, locked.pBits, &description);
861 img->surface->UnlockRect();
862 }
863
864 img->dirty = true;
865 }
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +0000866}
867
868void Texture::setCompressedImage(GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels, Image *img)
869{
870 createSurface(width, height, format, GL_UNSIGNED_BYTE, img);
871
872 if (pixels != NULL && img->surface != NULL)
873 {
874 D3DLOCKED_RECT locked;
875 HRESULT result = img->surface->LockRect(&locked, NULL, 0);
876
877 ASSERT(SUCCEEDED(result));
878
879 if (SUCCEEDED(result))
880 {
881 int inputPitch = ComputeCompressedPitch(width, format);
882 int inputSize = ComputeCompressedSize(width, height, format);
883 loadCompressedImageData(0, 0, width, height, -inputPitch, static_cast<const char*>(pixels) + inputSize - inputPitch, locked.Pitch, locked.pBits);
884 img->surface->UnlockRect();
885 }
886
887 img->dirty = true;
888 }
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +0000889}
890
891bool Texture::subImage(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, Image *img)
892{
893 if (width + xoffset > img->width || height + yoffset > img->height)
894 {
895 error(GL_INVALID_VALUE);
896 return false;
897 }
898
899 if (!img->surface)
900 {
901 createSurface(img->width, img->height, format, type, img);
902 }
903
904 if (pixels != NULL && img->surface != NULL)
905 {
906 D3DSURFACE_DESC description;
907 img->surface->GetDesc(&description);
908
909 D3DLOCKED_RECT locked;
910 HRESULT result = img->surface->LockRect(&locked, NULL, 0);
911
912 ASSERT(SUCCEEDED(result));
913
914 if (SUCCEEDED(result))
915 {
916 loadImageData(xoffset, transformPixelYOffset(yoffset, height, img->height), width, height, format, type, unpackAlignment, pixels, locked.Pitch, locked.pBits, &description);
917 img->surface->UnlockRect();
918 }
919
920 img->dirty = true;
921 }
922
923 return true;
924}
925
926bool Texture::subImageCompressed(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels, Image *img)
927{
928 if (width + xoffset > img->width || height + yoffset > img->height)
929 {
930 error(GL_INVALID_VALUE);
931 return false;
932 }
933
934 if (format != getInternalFormat())
935 {
936 error(GL_INVALID_OPERATION);
937 return false;
938 }
939
940 if (!img->surface)
941 {
942 createSurface(img->width, img->height, format, GL_UNSIGNED_BYTE, img);
943 }
944
945 if (pixels != NULL && img->surface != NULL)
946 {
947 RECT updateRegion;
948 updateRegion.left = xoffset;
949 updateRegion.right = xoffset + width;
950 updateRegion.bottom = yoffset + height;
951 updateRegion.top = yoffset;
952
953 D3DLOCKED_RECT locked;
954 HRESULT result = img->surface->LockRect(&locked, &updateRegion, 0);
955
956 ASSERT(SUCCEEDED(result));
957
958 if (SUCCEEDED(result))
959 {
960 int inputPitch = ComputeCompressedPitch(width, format);
961 int inputSize = ComputeCompressedSize(width, height, format);
962 loadCompressedImageData(xoffset, transformPixelYOffset(yoffset, height, img->height), width, height, -inputPitch, static_cast<const char*>(pixels) + inputSize - inputPitch, locked.Pitch, locked.pBits);
963 img->surface->UnlockRect();
964 }
965
966 img->dirty = true;
967 }
968
969 return true;
970}
971
972// This implements glCopyTex[Sub]Image2D for non-renderable internal texture formats
973void Texture::copyNonRenderable(Image *image, GLenum internalFormat, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, IDirect3DSurface9 *renderTarget)
974{
975 IDirect3DDevice9 *device = getDevice();
976 IDirect3DSurface9 *surface = NULL;
977 D3DSURFACE_DESC description;
978 renderTarget->GetDesc(&description);
979
980 HRESULT result = device->CreateOffscreenPlainSurface(description.Width, description.Height, description.Format, D3DPOOL_SYSTEMMEM, &surface, NULL);
981
982 if (!SUCCEEDED(result))
983 {
984 ERR("Could not create matching destination surface.");
985 return error(GL_OUT_OF_MEMORY);
986 }
987
988 result = device->GetRenderTargetData(renderTarget, surface);
989
990 if (!SUCCEEDED(result))
991 {
992 ERR("GetRenderTargetData unexpectedly failed.");
993 surface->Release();
994 return error(GL_OUT_OF_MEMORY);
995 }
996
997 D3DLOCKED_RECT sourceLock = {0};
998 RECT sourceRect = transformPixelRect(x, y, width, height, description.Height);
999 result = surface->LockRect(&sourceLock, &sourceRect, 0);
1000
1001 if (FAILED(result))
1002 {
1003 ERR("Failed to lock the source surface (rectangle might be invalid).");
1004 surface->UnlockRect();
1005 surface->Release();
1006 return error(GL_OUT_OF_MEMORY);
1007 }
1008
1009 if (!image->surface)
1010 {
1011 createSurface(width, height, internalFormat, mType, image);
1012 }
1013
1014 if (image->surface == NULL)
1015 {
1016 ERR("Failed to create an image surface.");
1017 surface->UnlockRect();
1018 surface->Release();
1019 return error(GL_OUT_OF_MEMORY);
1020 }
1021
1022 D3DLOCKED_RECT destLock = {0};
1023 int destYOffset = transformPixelYOffset(yoffset, height, image->height);
1024 RECT destRect = {xoffset, destYOffset, xoffset + width, destYOffset + height};
1025 result = image->surface->LockRect(&destLock, &destRect, 0);
1026
1027 if (FAILED(result))
1028 {
1029 ERR("Failed to lock the destination surface (rectangle might be invalid).");
1030 surface->UnlockRect();
1031 surface->Release();
1032 return error(GL_OUT_OF_MEMORY);
1033 }
1034
1035 if (destLock.pBits && sourceLock.pBits)
1036 {
1037 unsigned char *source = (unsigned char*)sourceLock.pBits;
1038 unsigned char *dest = (unsigned char*)destLock.pBits;
1039
1040 switch (description.Format)
1041 {
1042 case D3DFMT_X8R8G8B8:
1043 case D3DFMT_A8R8G8B8:
1044 switch(getD3DFormat())
1045 {
1046 case D3DFMT_L8:
1047 for(int y = 0; y < height; y++)
1048 {
1049 for(int x = 0; x < width; x++)
1050 {
1051 dest[x] = source[x * 4 + 2];
1052 }
1053
1054 source += sourceLock.Pitch;
1055 dest += destLock.Pitch;
1056 }
1057 break;
1058 case D3DFMT_A8L8:
1059 for(int y = 0; y < height; y++)
1060 {
1061 for(int x = 0; x < width; x++)
1062 {
1063 dest[x * 2 + 0] = source[x * 4 + 2];
1064 dest[x * 2 + 1] = source[x * 4 + 3];
1065 }
1066
1067 source += sourceLock.Pitch;
1068 dest += destLock.Pitch;
1069 }
1070 break;
1071 default:
1072 UNREACHABLE();
1073 }
1074 break;
1075 case D3DFMT_R5G6B5:
1076 switch(getD3DFormat())
1077 {
1078 case D3DFMT_L8:
1079 for(int y = 0; y < height; y++)
1080 {
1081 for(int x = 0; x < width; x++)
1082 {
1083 unsigned char red = source[x * 2 + 1] & 0xF8;
1084 dest[x] = red | (red >> 5);
1085 }
1086
1087 source += sourceLock.Pitch;
1088 dest += destLock.Pitch;
1089 }
1090 break;
1091 default:
1092 UNREACHABLE();
1093 }
1094 break;
1095 case D3DFMT_A1R5G5B5:
1096 switch(getD3DFormat())
1097 {
1098 case D3DFMT_L8:
1099 for(int y = 0; y < height; y++)
1100 {
1101 for(int x = 0; x < width; x++)
1102 {
1103 unsigned char red = source[x * 2 + 1] & 0x7C;
1104 dest[x] = (red << 1) | (red >> 4);
1105 }
1106
1107 source += sourceLock.Pitch;
1108 dest += destLock.Pitch;
1109 }
1110 break;
1111 case D3DFMT_A8L8:
1112 for(int y = 0; y < height; y++)
1113 {
1114 for(int x = 0; x < width; x++)
1115 {
1116 unsigned char red = source[x * 2 + 1] & 0x7C;
1117 dest[x * 2 + 0] = (red << 1) | (red >> 4);
1118 dest[x * 2 + 1] = (signed char)source[x * 2 + 1] >> 7;
1119 }
1120
1121 source += sourceLock.Pitch;
1122 dest += destLock.Pitch;
1123 }
1124 break;
1125 default:
1126 UNREACHABLE();
1127 }
1128 break;
1129 default:
1130 UNREACHABLE();
1131 }
1132
1133 image->dirty = true;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001134 }
1135
1136 image->surface->UnlockRect();
1137 surface->UnlockRect();
1138 surface->Release();
1139}
1140
1141D3DFORMAT Texture::getD3DFormat() const
1142{
1143 return selectFormat(getInternalFormat(), mType);
1144}
1145
1146IDirect3DBaseTexture9 *Texture::getTexture()
1147{
1148 if (!isComplete())
1149 {
1150 return NULL;
1151 }
1152
daniel@transgaming.comaed18322011-03-21 16:38:13 +00001153 if (!getBaseTexture())
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001154 {
daniel@transgaming.com68076a02011-03-21 16:38:09 +00001155 createTexture();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001156 }
daniel@transgaming.comaed18322011-03-21 16:38:13 +00001157
1158 if (dirtyImageData())
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001159 {
1160 updateTexture();
1161 }
1162
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001163 ASSERT(!dirtyImageData());
1164
daniel@transgaming.com68076a02011-03-21 16:38:09 +00001165 return getBaseTexture();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001166}
1167
1168bool Texture::isDirty() const
1169{
daniel@transgaming.comaed18322011-03-21 16:38:13 +00001170 return true;//(mDirty || mDirtyMetaData || dirtyImageData());
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001171}
1172
1173// Returns the top-level texture surface as a render target
1174void Texture::needRenderTarget()
1175{
1176 if (!mIsRenderable)
1177 {
daniel@transgaming.com68076a02011-03-21 16:38:09 +00001178 convertToRenderTarget();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001179 }
1180
1181 if (dirtyImageData())
1182 {
1183 updateTexture();
1184 }
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001185}
1186
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001187GLint Texture::creationLevels(GLsizei width, GLsizei height, GLint maxlevel) const
1188{
1189 if (isPow2(width) && isPow2(height))
1190 {
1191 return maxlevel;
1192 }
1193 else
1194 {
1195 // OpenGL ES 2.0 without GL_OES_texture_npot does not permit NPOT mipmaps.
1196 return 1;
1197 }
1198}
1199
1200GLint Texture::creationLevels(GLsizei size, GLint maxlevel) const
1201{
1202 return creationLevels(size, size, maxlevel);
1203}
1204
1205int Texture::levelCount() const
1206{
daniel@transgaming.com68076a02011-03-21 16:38:09 +00001207 return getBaseTexture() ? getBaseTexture()->GetLevelCount() : 0;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001208}
1209
1210Texture2D::Texture2D(GLuint id) : Texture(id)
1211{
1212 mTexture = NULL;
1213}
1214
1215Texture2D::~Texture2D()
1216{
1217 mColorbufferProxy.set(NULL);
1218
1219 if (mTexture)
1220 {
1221 mTexture->Release();
1222 mTexture = NULL;
1223 }
1224}
1225
1226GLenum Texture2D::getTarget() const
1227{
1228 return GL_TEXTURE_2D;
1229}
1230
1231GLenum Texture2D::getInternalFormat() const
1232{
1233 return mImageArray[0].format;
1234}
1235
1236// While OpenGL doesn't check texture consistency until draw-time, D3D9 requires a complete texture
1237// for render-to-texture (such as CopyTexImage). We have no way of keeping individual inconsistent levels.
1238// Call this when a particular level of the texture must be defined with a specific format, width and height.
1239//
1240// Returns true if the existing texture was unsuitable and had to be destroyed. If so, it will also set
1241// a new height and width for the texture by working backwards from the given width and height.
1242bool Texture2D::redefineTexture(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum type)
1243{
1244 bool widthOkay = (mWidth >> level == width);
1245 bool heightOkay = (mHeight >> level == height);
1246
1247 bool sizeOkay = ((widthOkay && heightOkay)
1248 || (widthOkay && mHeight >> level == 0 && height == 1)
1249 || (heightOkay && mWidth >> level == 0 && width == 1));
1250
1251 bool typeOkay = (type == mType);
1252
1253 bool textureOkay = (sizeOkay && typeOkay && internalFormat == mImageArray[0].format);
1254
1255 if (!textureOkay)
1256 {
1257 TRACE("Redefining 2D texture (%d, 0x%04X, %d, %d => 0x%04X, %d, %d).", level,
1258 mImageArray[0].format, mWidth, mHeight,
1259 internalFormat, width, height);
1260
1261 // Purge all the levels and the texture.
1262
1263 for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
1264 {
1265 if (mImageArray[i].surface != NULL)
1266 {
1267 mImageArray[i].dirty = false;
1268
1269 mImageArray[i].surface->Release();
1270 mImageArray[i].surface = NULL;
1271 }
1272 }
1273
1274 if (mTexture != NULL)
1275 {
1276 mTexture->Release();
1277 mTexture = NULL;
daniel@transgaming.com68076a02011-03-21 16:38:09 +00001278 mIsRenderable = false;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001279 }
1280
1281 mWidth = width << level;
1282 mHeight = height << level;
1283 mImageArray[0].format = internalFormat;
1284 mType = type;
1285 }
1286
1287 return !textureOkay;
1288}
1289
1290void Texture2D::setImage(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1291{
1292 redefineTexture(level, internalFormat, width, height, type);
1293
1294 Texture::setImage(width, height, format, type, unpackAlignment, pixels, &mImageArray[level]);
1295}
1296
1297void Texture2D::setCompressedImage(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
1298{
1299 redefineTexture(level, internalFormat, width, height, GL_UNSIGNED_BYTE);
1300
1301 Texture::setCompressedImage(width, height, internalFormat, imageSize, pixels, &mImageArray[level]);
1302}
1303
1304void Texture2D::commitRect(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
1305{
1306 ASSERT(mImageArray[level].surface != NULL);
1307
1308 if (level < levelCount())
1309 {
1310 IDirect3DSurface9 *destLevel = NULL;
1311 HRESULT result = mTexture->GetSurfaceLevel(level, &destLevel);
1312
1313 ASSERT(SUCCEEDED(result));
1314
1315 if (SUCCEEDED(result))
1316 {
1317 Image *img = &mImageArray[level];
1318
1319 RECT sourceRect = transformPixelRect(xoffset, yoffset, width, height, img->height);;
1320
1321 POINT destPoint;
1322 destPoint.x = sourceRect.left;
1323 destPoint.y = sourceRect.top;
1324
1325 result = getDevice()->UpdateSurface(img->surface, &sourceRect, destLevel, &destPoint);
1326 ASSERT(SUCCEEDED(result));
1327
1328 destLevel->Release();
1329
1330 img->dirty = false;
1331 }
1332 }
1333}
1334
1335void Texture2D::subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1336{
1337 if (Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[level]))
1338 {
1339 commitRect(level, xoffset, yoffset, width, height);
1340 }
1341}
1342
1343void Texture2D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
1344{
1345 if (Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, &mImageArray[level]))
1346 {
1347 commitRect(level, xoffset, yoffset, width, height);
1348 }
1349}
1350
1351void Texture2D::copyImage(GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
1352{
1353 IDirect3DSurface9 *renderTarget = source->getRenderTarget();
1354
1355 if (!renderTarget)
1356 {
1357 ERR("Failed to retrieve the render target.");
1358 return error(GL_OUT_OF_MEMORY);
1359 }
1360
1361 bool redefined = redefineTexture(level, internalFormat, width, height, mType);
1362
1363 if (!isRenderableFormat())
1364 {
1365 copyNonRenderable(&mImageArray[level], internalFormat, 0, 0, x, y, width, height, renderTarget);
1366 }
1367 else
1368 {
1369 if (redefined)
1370 {
1371 convertToRenderTarget();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001372 }
1373 else
1374 {
1375 needRenderTarget();
1376 }
1377
1378 if (width != 0 && height != 0 && level < levelCount())
1379 {
1380 RECT sourceRect = transformPixelRect(x, y, width, height, source->getColorbuffer()->getHeight());
1381 sourceRect.left = clamp(sourceRect.left, 0, source->getColorbuffer()->getWidth());
1382 sourceRect.top = clamp(sourceRect.top, 0, source->getColorbuffer()->getHeight());
1383 sourceRect.right = clamp(sourceRect.right, 0, source->getColorbuffer()->getWidth());
1384 sourceRect.bottom = clamp(sourceRect.bottom, 0, source->getColorbuffer()->getHeight());
1385
1386 IDirect3DSurface9 *dest;
1387 HRESULT hr = mTexture->GetSurfaceLevel(level, &dest);
1388
1389 getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, internalFormat, 0, 0, dest);
1390 dest->Release();
1391 }
1392 }
1393
1394 mImageArray[level].width = width;
1395 mImageArray[level].height = height;
1396 mImageArray[level].format = internalFormat;
1397}
1398
1399void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
1400{
1401 if (xoffset + width > mImageArray[level].width || yoffset + height > mImageArray[level].height)
1402 {
1403 return error(GL_INVALID_VALUE);
1404 }
1405
1406 IDirect3DSurface9 *renderTarget = source->getRenderTarget();
1407
1408 if (!renderTarget)
1409 {
1410 ERR("Failed to retrieve the render target.");
1411 return error(GL_OUT_OF_MEMORY);
1412 }
1413
1414 bool redefined = redefineTexture(0, mImageArray[0].format, mImageArray[0].width, mImageArray[0].height, mType);
1415
1416 if (!isRenderableFormat())
1417 {
1418 copyNonRenderable(&mImageArray[level], getInternalFormat(), xoffset, yoffset, x, y, width, height, renderTarget);
1419 }
1420 else
1421 {
1422 if (redefined)
1423 {
1424 convertToRenderTarget();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001425 }
1426 else
1427 {
1428 needRenderTarget();
1429 }
1430
1431 if (level < levelCount())
1432 {
1433 RECT sourceRect = transformPixelRect(x, y, width, height, source->getColorbuffer()->getHeight());
1434 sourceRect.left = clamp(sourceRect.left, 0, source->getColorbuffer()->getWidth());
1435 sourceRect.top = clamp(sourceRect.top, 0, source->getColorbuffer()->getHeight());
1436 sourceRect.right = clamp(sourceRect.right, 0, source->getColorbuffer()->getWidth());
1437 sourceRect.bottom = clamp(sourceRect.bottom, 0, source->getColorbuffer()->getHeight());
1438
1439 GLint destYOffset = transformPixelYOffset(yoffset, height, mImageArray[level].height);
1440
1441 IDirect3DSurface9 *dest;
1442 HRESULT hr = mTexture->GetSurfaceLevel(level, &dest);
1443
1444 getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, mImageArray[0].format, xoffset, destYOffset, dest);
1445 dest->Release();
1446 }
1447 }
1448}
1449
1450// Tests for GL texture object completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
1451bool Texture2D::isComplete() const
1452{
1453 GLsizei width = mImageArray[0].width;
1454 GLsizei height = mImageArray[0].height;
1455
1456 if (width <= 0 || height <= 0)
1457 {
1458 return false;
1459 }
1460
1461 bool mipmapping = false;
1462
1463 switch (mMinFilter)
1464 {
1465 case GL_NEAREST:
1466 case GL_LINEAR:
1467 mipmapping = false;
1468 break;
1469 case GL_NEAREST_MIPMAP_NEAREST:
1470 case GL_LINEAR_MIPMAP_NEAREST:
1471 case GL_NEAREST_MIPMAP_LINEAR:
1472 case GL_LINEAR_MIPMAP_LINEAR:
1473 mipmapping = true;
1474 break;
1475 default: UNREACHABLE();
1476 }
1477
1478 if ((getInternalFormat() == GL_FLOAT && !getContext()->supportsFloatLinearFilter()) ||
1479 (getInternalFormat() == GL_HALF_FLOAT_OES && !getContext()->supportsHalfFloatLinearFilter()))
1480 {
1481 if (mMagFilter != GL_NEAREST || (mMinFilter != GL_NEAREST && mMinFilter != GL_NEAREST_MIPMAP_NEAREST))
1482 {
1483 return false;
1484 }
1485 }
1486
1487
1488 if ((getWrapS() != GL_CLAMP_TO_EDGE && !isPow2(width))
1489 || (getWrapT() != GL_CLAMP_TO_EDGE && !isPow2(height)))
1490 {
1491 return false;
1492 }
1493
1494 if (mipmapping)
1495 {
1496 if (!isPow2(width) || !isPow2(height))
1497 {
1498 return false;
1499 }
1500
1501 int q = log2(std::max(width, height));
1502
1503 for (int level = 1; level <= q; level++)
1504 {
1505 if (mImageArray[level].format != mImageArray[0].format)
1506 {
1507 return false;
1508 }
1509
1510 if (mImageArray[level].width != std::max(1, width >> level))
1511 {
1512 return false;
1513 }
1514
1515 if (mImageArray[level].height != std::max(1, height >> level))
1516 {
1517 return false;
1518 }
1519 }
1520 }
1521
1522 return true;
1523}
1524
1525bool Texture2D::isCompressed() const
1526{
1527 return IsCompressed(getInternalFormat());
1528}
1529
daniel@transgaming.com68076a02011-03-21 16:38:09 +00001530IDirect3DBaseTexture9 *Texture2D::getBaseTexture() const
1531{
1532 return mTexture;
1533}
1534
1535// Constructs a Direct3D 9 texture resource from the texture images
1536void Texture2D::createTexture()
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001537{
1538 IDirect3DTexture9 *texture;
1539
1540 IDirect3DDevice9 *device = getDevice();
1541 D3DFORMAT format = selectFormat(mImageArray[0].format, mType);
1542
1543 HRESULT result = device->CreateTexture(mWidth, mHeight, creationLevels(mWidth, mHeight, 0), 0, format, D3DPOOL_DEFAULT, &texture, NULL);
1544
1545 if (FAILED(result))
1546 {
1547 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
daniel@transgaming.com68076a02011-03-21 16:38:09 +00001548 return error(GL_OUT_OF_MEMORY);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001549 }
1550
daniel@transgaming.com68076a02011-03-21 16:38:09 +00001551 if (mTexture)
1552 {
1553 mTexture->Release();
1554 }
1555
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001556 mTexture = texture;
daniel@transgaming.comaed18322011-03-21 16:38:13 +00001557 mIsRenderable = false;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001558}
1559
1560void Texture2D::updateTexture()
1561{
1562 IDirect3DDevice9 *device = getDevice();
1563
1564 int levels = levelCount();
1565
1566 for (int level = 0; level < levels; level++)
1567 {
1568 if (mImageArray[level].dirty)
1569 {
1570 IDirect3DSurface9 *levelSurface = NULL;
1571 HRESULT result = mTexture->GetSurfaceLevel(level, &levelSurface);
1572
1573 ASSERT(SUCCEEDED(result));
1574
1575 if (SUCCEEDED(result))
1576 {
1577 result = device->UpdateSurface(mImageArray[level].surface, NULL, levelSurface, NULL);
1578 ASSERT(SUCCEEDED(result));
1579
1580 levelSurface->Release();
1581
1582 mImageArray[level].dirty = false;
1583 }
1584 }
1585 }
1586}
1587
daniel@transgaming.com68076a02011-03-21 16:38:09 +00001588void Texture2D::convertToRenderTarget()
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001589{
1590 IDirect3DTexture9 *texture = NULL;
1591
1592 if (mWidth != 0 && mHeight != 0)
1593 {
1594 egl::Display *display = getDisplay();
1595 IDirect3DDevice9 *device = getDevice();
1596 D3DFORMAT format = selectFormat(mImageArray[0].format, mType);
1597
1598 HRESULT result = device->CreateTexture(mWidth, mHeight, creationLevels(mWidth, mHeight, 0), D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture, NULL);
1599
1600 if (FAILED(result))
1601 {
1602 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
daniel@transgaming.com68076a02011-03-21 16:38:09 +00001603 return error(GL_OUT_OF_MEMORY);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001604 }
1605
1606 if (mTexture != NULL)
1607 {
1608 int levels = levelCount();
1609 for (int i = 0; i < levels; i++)
1610 {
1611 IDirect3DSurface9 *source;
1612 result = mTexture->GetSurfaceLevel(i, &source);
1613
1614 if (FAILED(result))
1615 {
1616 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1617
1618 texture->Release();
1619
daniel@transgaming.com68076a02011-03-21 16:38:09 +00001620 return error(GL_OUT_OF_MEMORY);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001621 }
1622
1623 IDirect3DSurface9 *dest;
1624 result = texture->GetSurfaceLevel(i, &dest);
1625
1626 if (FAILED(result))
1627 {
1628 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1629
1630 texture->Release();
1631 source->Release();
1632
daniel@transgaming.com68076a02011-03-21 16:38:09 +00001633 return error(GL_OUT_OF_MEMORY);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001634 }
1635
1636 display->endScene();
1637 result = device->StretchRect(source, NULL, dest, NULL, D3DTEXF_NONE);
1638
1639 if (FAILED(result))
1640 {
1641 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1642
1643 texture->Release();
1644 source->Release();
1645 dest->Release();
1646
daniel@transgaming.com68076a02011-03-21 16:38:09 +00001647 return error(GL_OUT_OF_MEMORY);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001648 }
1649
1650 source->Release();
1651 dest->Release();
1652 }
1653 }
1654 }
1655
1656 if (mTexture != NULL)
1657 {
1658 mTexture->Release();
1659 }
1660
1661 mTexture = texture;
daniel@transgaming.comaed18322011-03-21 16:38:13 +00001662 mIsRenderable = true;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001663}
1664
1665bool Texture2D::dirtyImageData() const
1666{
1667 int q = log2(std::max(mWidth, mHeight));
1668
1669 for (int i = 0; i <= q; i++)
1670 {
1671 if (mImageArray[i].dirty) return true;
1672 }
1673
1674 return false;
1675}
1676
1677void Texture2D::generateMipmaps()
1678{
1679 if (!isPow2(mImageArray[0].width) || !isPow2(mImageArray[0].height))
1680 {
1681 return error(GL_INVALID_OPERATION);
1682 }
1683
1684 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
1685 unsigned int q = log2(std::max(mWidth, mHeight));
1686 for (unsigned int i = 1; i <= q; i++)
1687 {
1688 if (mImageArray[i].surface != NULL)
1689 {
1690 mImageArray[i].surface->Release();
1691 mImageArray[i].surface = NULL;
1692 }
1693
1694 mImageArray[i].dirty = false;
1695
1696 mImageArray[i].format = mImageArray[0].format;
1697 mImageArray[i].width = std::max(mImageArray[0].width >> i, 1);
1698 mImageArray[i].height = std::max(mImageArray[0].height >> i, 1);
1699 }
1700
daniel@transgaming.com68076a02011-03-21 16:38:09 +00001701 if (mIsRenderable)
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001702 {
1703 if (mTexture == NULL)
1704 {
1705 ERR(" failed because mTexture was null.");
1706 return;
1707 }
1708
1709 for (unsigned int i = 1; i <= q; i++)
1710 {
1711 IDirect3DSurface9 *upper = NULL;
1712 IDirect3DSurface9 *lower = NULL;
1713
1714 mTexture->GetSurfaceLevel(i-1, &upper);
1715 mTexture->GetSurfaceLevel(i, &lower);
1716
1717 if (upper != NULL && lower != NULL)
1718 {
1719 getBlitter()->boxFilter(upper, lower);
1720 }
1721
1722 if (upper != NULL) upper->Release();
1723 if (lower != NULL) lower->Release();
1724 }
1725 }
1726 else
1727 {
1728 for (unsigned int i = 1; i <= q; i++)
1729 {
1730 createSurface(mImageArray[i].width, mImageArray[i].height, mImageArray[i].format, mType, &mImageArray[i]);
1731 if (mImageArray[i].surface == NULL)
1732 {
1733 return error(GL_OUT_OF_MEMORY);
1734 }
1735
1736 if (FAILED(D3DXLoadSurfaceFromSurface(mImageArray[i].surface, NULL, NULL, mImageArray[i - 1].surface, NULL, NULL, D3DX_FILTER_BOX, 0)))
1737 {
1738 ERR(" failed to load filter %d to %d.", i - 1, i);
1739 }
1740
1741 mImageArray[i].dirty = true;
1742 }
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001743 }
1744}
1745
1746Renderbuffer *Texture2D::getRenderbuffer(GLenum target)
1747{
1748 if (target != GL_TEXTURE_2D)
1749 {
1750 return error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);
1751 }
1752
1753 if (mColorbufferProxy.get() == NULL)
1754 {
1755 mColorbufferProxy.set(new Renderbuffer(id(), new Colorbuffer(this, target)));
1756 }
1757
1758 return mColorbufferProxy.get();
1759}
1760
1761IDirect3DSurface9 *Texture2D::getRenderTarget(GLenum target)
1762{
1763 ASSERT(target == GL_TEXTURE_2D);
1764
1765 needRenderTarget();
1766
1767 if (mTexture == NULL)
1768 {
1769 return NULL;
1770 }
1771
1772 IDirect3DSurface9 *renderTarget = NULL;
1773 mTexture->GetSurfaceLevel(0, &renderTarget);
1774
1775 return renderTarget;
1776}
1777
1778TextureCubeMap::TextureCubeMap(GLuint id) : Texture(id)
1779{
1780 mTexture = NULL;
1781}
1782
1783TextureCubeMap::~TextureCubeMap()
1784{
1785 for (int i = 0; i < 6; i++)
1786 {
1787 mFaceProxies[i].set(NULL);
1788 }
1789
1790 if (mTexture)
1791 {
1792 mTexture->Release();
1793 mTexture = NULL;
1794 }
1795}
1796
1797GLenum TextureCubeMap::getTarget() const
1798{
1799 return GL_TEXTURE_CUBE_MAP;
1800}
1801
1802GLenum TextureCubeMap::getInternalFormat() const
1803{
1804 return mImageArray[0][0].format;
1805}
1806
1807void TextureCubeMap::setImagePosX(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1808{
1809 setImage(0, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
1810}
1811
1812void TextureCubeMap::setImageNegX(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1813{
1814 setImage(1, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
1815}
1816
1817void TextureCubeMap::setImagePosY(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1818{
1819 setImage(2, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
1820}
1821
1822void TextureCubeMap::setImageNegY(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1823{
1824 setImage(3, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
1825}
1826
1827void TextureCubeMap::setImagePosZ(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1828{
1829 setImage(4, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
1830}
1831
1832void TextureCubeMap::setImageNegZ(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1833{
1834 setImage(5, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
1835}
1836
1837void TextureCubeMap::setCompressedImage(GLenum face, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
1838{
1839 redefineTexture(level, internalFormat, width);
1840
1841 Texture::setCompressedImage(width, height, internalFormat, imageSize, pixels, &mImageArray[faceIndex(face)][level]);
1842}
1843
1844void TextureCubeMap::commitRect(GLenum faceTarget, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
1845{
1846 int face = faceIndex(faceTarget);
1847 ASSERT(mImageArray[face][level].surface != NULL);
1848
1849 if (level < levelCount())
1850 {
1851 IDirect3DSurface9 *destLevel = getCubeMapSurface(faceTarget, level);
1852 ASSERT(destLevel != NULL);
1853
1854 if (destLevel != NULL)
1855 {
1856 Image *img = &mImageArray[face][level];
1857
1858 RECT sourceRect = transformPixelRect(xoffset, yoffset, width, height, img->height);;
1859
1860 POINT destPoint;
1861 destPoint.x = sourceRect.left;
1862 destPoint.y = sourceRect.top;
1863
1864 HRESULT result = getDevice()->UpdateSurface(img->surface, &sourceRect, destLevel, &destPoint);
1865 ASSERT(SUCCEEDED(result));
1866
1867 destLevel->Release();
1868
1869 img->dirty = false;
1870 }
1871 }
1872}
1873
1874void TextureCubeMap::subImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1875{
1876 if (Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[faceIndex(target)][level]))
1877 {
1878 commitRect(target, level, xoffset, yoffset, width, height);
1879 }
1880}
1881
1882void TextureCubeMap::subImageCompressed(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
1883{
1884 if (Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, &mImageArray[faceIndex(target)][level]))
1885 {
1886 commitRect(target, level, xoffset, yoffset, width, height);
1887 }
1888}
1889
1890// Tests for GL texture object completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
1891bool TextureCubeMap::isComplete() const
1892{
1893 int size = mImageArray[0][0].width;
1894
1895 if (size <= 0)
1896 {
1897 return false;
1898 }
1899
1900 bool mipmapping;
1901
1902 switch (mMinFilter)
1903 {
1904 case GL_NEAREST:
1905 case GL_LINEAR:
1906 mipmapping = false;
1907 break;
1908 case GL_NEAREST_MIPMAP_NEAREST:
1909 case GL_LINEAR_MIPMAP_NEAREST:
1910 case GL_NEAREST_MIPMAP_LINEAR:
1911 case GL_LINEAR_MIPMAP_LINEAR:
1912 mipmapping = true;
1913 break;
1914 default: UNREACHABLE();
1915 }
1916
1917 for (int face = 0; face < 6; face++)
1918 {
1919 if (mImageArray[face][0].width != size || mImageArray[face][0].height != size)
1920 {
1921 return false;
1922 }
1923 }
1924
1925 if ((getInternalFormat() == GL_FLOAT && !getContext()->supportsFloatLinearFilter()) ||
1926 (getInternalFormat() == GL_HALF_FLOAT_OES && !getContext()->supportsHalfFloatLinearFilter()))
1927 {
1928 if (mMagFilter != GL_NEAREST || (mMinFilter != GL_NEAREST && mMinFilter != GL_NEAREST_MIPMAP_NEAREST))
1929 {
1930 return false;
1931 }
1932 }
1933
1934 if (mipmapping)
1935 {
1936 if (!isPow2(size) && (getWrapS() != GL_CLAMP_TO_EDGE || getWrapT() != GL_CLAMP_TO_EDGE))
1937 {
1938 return false;
1939 }
1940
1941 int q = log2(size);
1942
1943 for (int face = 0; face < 6; face++)
1944 {
1945 for (int level = 1; level <= q; level++)
1946 {
1947 if (mImageArray[face][level].format != mImageArray[0][0].format)
1948 {
1949 return false;
1950 }
1951
1952 if (mImageArray[face][level].width != std::max(1, size >> level))
1953 {
1954 return false;
1955 }
1956
1957 ASSERT(mImageArray[face][level].height == mImageArray[face][level].width);
1958 }
1959 }
1960 }
1961
1962 return true;
1963}
1964
1965bool TextureCubeMap::isCompressed() const
1966{
1967 return IsCompressed(getInternalFormat());
1968}
1969
daniel@transgaming.com68076a02011-03-21 16:38:09 +00001970IDirect3DBaseTexture9 *TextureCubeMap::getBaseTexture() const
1971{
1972 return mTexture;
1973}
1974
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001975// Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one
daniel@transgaming.com68076a02011-03-21 16:38:09 +00001976void TextureCubeMap::createTexture()
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001977{
1978 IDirect3DDevice9 *device = getDevice();
1979 D3DFORMAT format = selectFormat(mImageArray[0][0].format, mType);
1980
1981 IDirect3DCubeTexture9 *texture;
1982
1983 HRESULT result = device->CreateCubeTexture(mWidth, creationLevels(mWidth, 0), 0, format, D3DPOOL_DEFAULT, &texture, NULL);
1984
1985 if (FAILED(result))
1986 {
1987 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
daniel@transgaming.com68076a02011-03-21 16:38:09 +00001988 return error(GL_OUT_OF_MEMORY);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001989 }
1990
daniel@transgaming.com68076a02011-03-21 16:38:09 +00001991 if (mTexture)
1992 {
1993 mTexture->Release();
1994 }
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001995
1996 mTexture = texture;
daniel@transgaming.comaed18322011-03-21 16:38:13 +00001997 mIsRenderable = false;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00001998}
1999
2000void TextureCubeMap::updateTexture()
2001{
2002 IDirect3DDevice9 *device = getDevice();
2003
2004 for (int face = 0; face < 6; face++)
2005 {
2006 int levels = levelCount();
2007 for (int level = 0; level < levels; level++)
2008 {
2009 Image *img = &mImageArray[face][level];
2010
2011 if (img->dirty)
2012 {
2013 IDirect3DSurface9 *levelSurface = getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, level);
2014 ASSERT(levelSurface != NULL);
2015
2016 if (levelSurface != NULL)
2017 {
2018 HRESULT result = device->UpdateSurface(img->surface, NULL, levelSurface, NULL);
2019 ASSERT(SUCCEEDED(result));
2020
2021 levelSurface->Release();
2022
2023 img->dirty = false;
2024 }
2025 }
2026 }
2027 }
2028}
2029
daniel@transgaming.com68076a02011-03-21 16:38:09 +00002030void TextureCubeMap::convertToRenderTarget()
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002031{
2032 IDirect3DCubeTexture9 *texture = NULL;
2033
2034 if (mWidth != 0)
2035 {
2036 egl::Display *display = getDisplay();
2037 IDirect3DDevice9 *device = getDevice();
2038 D3DFORMAT format = selectFormat(mImageArray[0][0].format, mType);
2039
2040 HRESULT result = device->CreateCubeTexture(mWidth, creationLevels(mWidth, 0), D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture, NULL);
2041
2042 if (FAILED(result))
2043 {
2044 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
daniel@transgaming.com68076a02011-03-21 16:38:09 +00002045 return error(GL_OUT_OF_MEMORY);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002046 }
2047
2048 if (mTexture != NULL)
2049 {
2050 int levels = levelCount();
2051 for (int f = 0; f < 6; f++)
2052 {
2053 for (int i = 0; i < levels; i++)
2054 {
2055 IDirect3DSurface9 *source;
2056 result = mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(f), i, &source);
2057
2058 if (FAILED(result))
2059 {
2060 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
2061
2062 texture->Release();
2063
daniel@transgaming.com68076a02011-03-21 16:38:09 +00002064 return error(GL_OUT_OF_MEMORY);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002065 }
2066
2067 IDirect3DSurface9 *dest;
2068 result = texture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(f), i, &dest);
2069
2070 if (FAILED(result))
2071 {
2072 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
2073
2074 texture->Release();
2075 source->Release();
2076
daniel@transgaming.com68076a02011-03-21 16:38:09 +00002077 return error(GL_OUT_OF_MEMORY);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002078 }
2079
2080 display->endScene();
2081 result = device->StretchRect(source, NULL, dest, NULL, D3DTEXF_NONE);
2082
2083 if (FAILED(result))
2084 {
2085 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
2086
2087 texture->Release();
2088 source->Release();
2089 dest->Release();
2090
daniel@transgaming.com68076a02011-03-21 16:38:09 +00002091 return error(GL_OUT_OF_MEMORY);
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002092 }
2093 }
2094 }
2095 }
2096 }
2097
2098 if (mTexture != NULL)
2099 {
2100 mTexture->Release();
2101 }
2102
2103 mTexture = texture;
daniel@transgaming.comaed18322011-03-21 16:38:13 +00002104 mIsRenderable = true;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002105}
2106
2107void TextureCubeMap::setImage(int face, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
2108{
2109 redefineTexture(level, internalFormat, width);
2110
2111 Texture::setImage(width, height, format, type, unpackAlignment, pixels, &mImageArray[face][level]);
2112}
2113
2114unsigned int TextureCubeMap::faceIndex(GLenum face)
2115{
2116 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_X - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 1);
2117 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 2);
2118 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 3);
2119 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 4);
2120 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 5);
2121
2122 return face - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
2123}
2124
2125bool TextureCubeMap::dirtyImageData() const
2126{
2127 int q = log2(mWidth);
2128
2129 for (int f = 0; f < 6; f++)
2130 {
2131 for (int i = 0; i <= q; i++)
2132 {
2133 if (mImageArray[f][i].dirty) return true;
2134 }
2135 }
2136
2137 return false;
2138}
2139
2140// While OpenGL doesn't check texture consistency until draw-time, D3D9 requires a complete texture
2141// for render-to-texture (such as CopyTexImage). We have no way of keeping individual inconsistent levels & faces.
2142// Call this when a particular level of the texture must be defined with a specific format, width and height.
2143//
2144// Returns true if the existing texture was unsuitable had to be destroyed. If so, it will also set
2145// a new size for the texture by working backwards from the given size.
2146bool TextureCubeMap::redefineTexture(GLint level, GLenum internalFormat, GLsizei width)
2147{
2148 // Are these settings compatible with level 0?
2149 bool sizeOkay = (mImageArray[0][0].width >> level == width);
2150
2151 bool textureOkay = (sizeOkay && internalFormat == mImageArray[0][0].format);
2152
2153 if (!textureOkay)
2154 {
2155 TRACE("Redefining cube texture (%d, 0x%04X, %d => 0x%04X, %d).", level,
2156 mImageArray[0][0].format, mImageArray[0][0].width,
2157 internalFormat, width);
2158
2159 // Purge all the levels and the texture.
2160 for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
2161 {
2162 for (int f = 0; f < 6; f++)
2163 {
2164 if (mImageArray[f][i].surface != NULL)
2165 {
2166 mImageArray[f][i].dirty = false;
2167
2168 mImageArray[f][i].surface->Release();
2169 mImageArray[f][i].surface = NULL;
2170 }
2171 }
2172 }
2173
2174 if (mTexture != NULL)
2175 {
2176 mTexture->Release();
2177 mTexture = NULL;
daniel@transgaming.com68076a02011-03-21 16:38:09 +00002178 mIsRenderable = false;
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002179 }
2180
2181 mWidth = width << level;
2182 mImageArray[0][0].width = width << level;
2183 mHeight = width << level;
2184 mImageArray[0][0].height = width << level;
2185
2186 mImageArray[0][0].format = internalFormat;
2187 }
2188
2189 return !textureOkay;
2190}
2191
2192void TextureCubeMap::copyImage(GLenum target, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
2193{
2194 IDirect3DSurface9 *renderTarget = source->getRenderTarget();
2195
2196 if (!renderTarget)
2197 {
2198 ERR("Failed to retrieve the render target.");
2199 return error(GL_OUT_OF_MEMORY);
2200 }
2201
2202 unsigned int faceindex = faceIndex(target);
2203 bool redefined = redefineTexture(level, internalFormat, width);
2204
2205 if (!isRenderableFormat())
2206 {
2207 copyNonRenderable(&mImageArray[faceindex][level], internalFormat, 0, 0, x, y, width, height, renderTarget);
2208 }
2209 else
2210 {
2211 if (redefined)
2212 {
2213 convertToRenderTarget();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002214 }
2215 else
2216 {
2217 needRenderTarget();
2218 }
2219
2220 ASSERT(width == height);
2221
2222 if (width > 0 && level < levelCount())
2223 {
2224 RECT sourceRect = transformPixelRect(x, y, width, height, source->getColorbuffer()->getHeight());
2225 sourceRect.left = clamp(sourceRect.left, 0, source->getColorbuffer()->getWidth());
2226 sourceRect.top = clamp(sourceRect.top, 0, source->getColorbuffer()->getHeight());
2227 sourceRect.right = clamp(sourceRect.right, 0, source->getColorbuffer()->getWidth());
2228 sourceRect.bottom = clamp(sourceRect.bottom, 0, source->getColorbuffer()->getHeight());
2229
2230 IDirect3DSurface9 *dest = getCubeMapSurface(target, level);
2231
2232 getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, internalFormat, 0, 0, dest);
2233 dest->Release();
2234 }
2235 }
2236
2237 mImageArray[faceindex][level].width = width;
2238 mImageArray[faceindex][level].height = height;
2239 mImageArray[faceindex][level].format = internalFormat;
2240}
2241
2242IDirect3DSurface9 *TextureCubeMap::getCubeMapSurface(GLenum face, unsigned int level)
2243{
2244 if (mTexture == NULL)
2245 {
2246 UNREACHABLE();
2247 return NULL;
2248 }
2249
2250 IDirect3DSurface9 *surface = NULL;
2251
2252 HRESULT hr = mTexture->GetCubeMapSurface(es2dx::ConvertCubeFace(face), level, &surface);
2253
2254 return (SUCCEEDED(hr)) ? surface : NULL;
2255}
2256
2257void TextureCubeMap::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
2258{
2259 GLsizei size = mImageArray[faceIndex(target)][level].width;
2260
2261 if (xoffset + width > size || yoffset + height > size)
2262 {
2263 return error(GL_INVALID_VALUE);
2264 }
2265
2266 IDirect3DSurface9 *renderTarget = source->getRenderTarget();
2267
2268 if (!renderTarget)
2269 {
2270 ERR("Failed to retrieve the render target.");
2271 return error(GL_OUT_OF_MEMORY);
2272 }
2273
2274 unsigned int faceindex = faceIndex(target);
2275 bool redefined = redefineTexture(0, mImageArray[0][0].format, mImageArray[0][0].width);
2276
2277 if (!isRenderableFormat())
2278 {
2279 copyNonRenderable(&mImageArray[faceindex][level], getInternalFormat(), 0, 0, x, y, width, height, renderTarget);
2280 }
2281 else
2282 {
2283 if (redefined)
2284 {
2285 convertToRenderTarget();
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002286 }
2287 else
2288 {
2289 needRenderTarget();
2290 }
2291
2292 if (level < levelCount())
2293 {
2294 RECT sourceRect = transformPixelRect(x, y, width, height, source->getColorbuffer()->getHeight());
2295 sourceRect.left = clamp(sourceRect.left, 0, source->getColorbuffer()->getWidth());
2296 sourceRect.top = clamp(sourceRect.top, 0, source->getColorbuffer()->getHeight());
2297 sourceRect.right = clamp(sourceRect.right, 0, source->getColorbuffer()->getWidth());
2298 sourceRect.bottom = clamp(sourceRect.bottom, 0, source->getColorbuffer()->getHeight());
2299
2300 GLint destYOffset = transformPixelYOffset(yoffset, height, mImageArray[faceindex][level].width);
2301
2302 IDirect3DSurface9 *dest = getCubeMapSurface(target, level);
2303
2304 getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, mImageArray[0][0].format, xoffset, destYOffset, dest);
2305 dest->Release();
2306 }
2307 }
2308}
2309
2310bool TextureCubeMap::isCubeComplete() const
2311{
2312 if (mImageArray[0][0].width == 0)
2313 {
2314 return false;
2315 }
2316
2317 for (unsigned int f = 1; f < 6; f++)
2318 {
2319 if (mImageArray[f][0].width != mImageArray[0][0].width
2320 || mImageArray[f][0].format != mImageArray[0][0].format)
2321 {
2322 return false;
2323 }
2324 }
2325
2326 return true;
2327}
2328
2329void TextureCubeMap::generateMipmaps()
2330{
2331 if (!isPow2(mImageArray[0][0].width) || !isCubeComplete())
2332 {
2333 return error(GL_INVALID_OPERATION);
2334 }
2335
2336 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
2337 unsigned int q = log2(mImageArray[0][0].width);
2338 for (unsigned int f = 0; f < 6; f++)
2339 {
2340 for (unsigned int i = 1; i <= q; i++)
2341 {
2342 if (mImageArray[f][i].surface != NULL)
2343 {
2344 mImageArray[f][i].surface->Release();
2345 mImageArray[f][i].surface = NULL;
2346 }
2347
2348 mImageArray[f][i].dirty = false;
2349
2350 mImageArray[f][i].format = mImageArray[f][0].format;
2351 mImageArray[f][i].width = std::max(mImageArray[f][0].width >> i, 1);
2352 mImageArray[f][i].height = mImageArray[f][i].width;
2353 }
2354 }
2355
daniel@transgaming.com68076a02011-03-21 16:38:09 +00002356 if (mIsRenderable)
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002357 {
2358 if (mTexture == NULL)
2359 {
2360 return;
2361 }
2362
2363 for (unsigned int f = 0; f < 6; f++)
2364 {
2365 for (unsigned int i = 1; i <= q; i++)
2366 {
2367 IDirect3DSurface9 *upper = getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + f, i-1);
2368 IDirect3DSurface9 *lower = getCubeMapSurface(GL_TEXTURE_CUBE_MAP_POSITIVE_X + f, i);
2369
2370 if (upper != NULL && lower != NULL)
2371 {
2372 getBlitter()->boxFilter(upper, lower);
2373 }
2374
2375 if (upper != NULL) upper->Release();
2376 if (lower != NULL) lower->Release();
2377 }
2378 }
2379 }
2380 else
2381 {
2382 for (unsigned int f = 0; f < 6; f++)
2383 {
2384 for (unsigned int i = 1; i <= q; i++)
2385 {
2386 createSurface(mImageArray[f][i].width, mImageArray[f][i].height, mImageArray[f][i].format, mType, &mImageArray[f][i]);
2387 if (mImageArray[f][i].surface == NULL)
2388 {
2389 return error(GL_OUT_OF_MEMORY);
2390 }
2391
2392 if (FAILED(D3DXLoadSurfaceFromSurface(mImageArray[f][i].surface, NULL, NULL, mImageArray[f][i - 1].surface, NULL, NULL, D3DX_FILTER_BOX, 0)))
2393 {
2394 ERR(" failed to load filter %d to %d.", i - 1, i);
2395 }
2396
2397 mImageArray[f][i].dirty = true;
2398 }
2399 }
daniel@transgaming.comd2fd4f22011-02-01 18:49:11 +00002400 }
2401}
2402
2403Renderbuffer *TextureCubeMap::getRenderbuffer(GLenum target)
2404{
2405 if (!IsCubemapTextureTarget(target))
2406 {
2407 return error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);
2408 }
2409
2410 unsigned int face = faceIndex(target);
2411
2412 if (mFaceProxies[face].get() == NULL)
2413 {
2414 mFaceProxies[face].set(new Renderbuffer(id(), new Colorbuffer(this, target)));
2415 }
2416
2417 return mFaceProxies[face].get();
2418}
2419
2420IDirect3DSurface9 *TextureCubeMap::getRenderTarget(GLenum target)
2421{
2422 ASSERT(IsCubemapTextureTarget(target));
2423
2424 needRenderTarget();
2425
2426 if (mTexture == NULL)
2427 {
2428 return NULL;
2429 }
2430
2431 IDirect3DSurface9 *renderTarget = NULL;
2432 mTexture->GetCubeMapSurface(es2dx::ConvertCubeFace(target), 0, &renderTarget);
2433
2434 return renderTarget;
2435}
2436
2437}