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