blob: b33c471b2142cfeac1ee7844a11aec9e0876da46 [file] [log] [blame]
Brandon Jonesf47bebc2014-07-09 14:28:42 -07001#include "precompiled.h"
2//
3// Copyright 2014 The ANGLE Project Authors. All rights reserved.
4// Use of this source code is governed by a BSD-style license that can be
5// found in the LICENSE file.
6//
7
8// TextureD3D.cpp: Implementations of the Texture interfaces shared betweeen the D3D backends.
9
10#include "common/mathutil.h"
Brandon Jones0511e802014-07-14 16:27:26 -070011#include "common/utilities.h"
Brandon Jonesf47bebc2014-07-09 14:28:42 -070012#include "libEGL/Surface.h"
13#include "libGLESv2/Buffer.h"
14#include "libGLESv2/Framebuffer.h"
Brandon Jonescef06ff2014-08-05 13:27:48 -070015#include "libGLESv2/Texture.h"
Brandon Jonesf47bebc2014-07-09 14:28:42 -070016#include "libGLESv2/main.h"
17#include "libGLESv2/formatutils.h"
18#include "libGLESv2/renderer/BufferImpl.h"
19#include "libGLESv2/renderer/RenderTarget.h"
20#include "libGLESv2/renderer/Renderer.h"
21#include "libGLESv2/renderer/d3d/ImageD3D.h"
22#include "libGLESv2/renderer/d3d/TextureD3D.h"
23#include "libGLESv2/renderer/d3d/TextureStorage.h"
24
25namespace rx
26{
27
28bool IsMipmapFiltered(const gl::SamplerState &samplerState)
29{
30 switch (samplerState.minFilter)
31 {
32 case GL_NEAREST:
33 case GL_LINEAR:
34 return false;
35 case GL_NEAREST_MIPMAP_NEAREST:
36 case GL_LINEAR_MIPMAP_NEAREST:
37 case GL_NEAREST_MIPMAP_LINEAR:
38 case GL_LINEAR_MIPMAP_LINEAR:
39 return true;
40 default: UNREACHABLE();
41 return false;
42 }
43}
44
45bool IsRenderTargetUsage(GLenum usage)
46{
47 return (usage == GL_FRAMEBUFFER_ATTACHMENT_ANGLE);
48}
49
Brandon Jones78b1acd2014-07-15 15:33:07 -070050TextureD3D::TextureD3D(Renderer *renderer)
Brandon Jonesf47bebc2014-07-09 14:28:42 -070051 : mRenderer(renderer),
52 mUsage(GL_NONE),
53 mDirtyImages(true),
54 mImmutable(false)
55{
56}
57
58TextureD3D::~TextureD3D()
59{
60}
61
62GLint TextureD3D::getBaseLevelWidth() const
63{
Brandon Jones78b1acd2014-07-15 15:33:07 -070064 const Image *baseImage = getBaseLevelImage();
Brandon Jonesf47bebc2014-07-09 14:28:42 -070065 return (baseImage ? baseImage->getWidth() : 0);
66}
67
68GLint TextureD3D::getBaseLevelHeight() const
69{
Brandon Jones78b1acd2014-07-15 15:33:07 -070070 const Image *baseImage = getBaseLevelImage();
Brandon Jonesf47bebc2014-07-09 14:28:42 -070071 return (baseImage ? baseImage->getHeight() : 0);
72}
73
74GLint TextureD3D::getBaseLevelDepth() const
75{
Brandon Jones78b1acd2014-07-15 15:33:07 -070076 const Image *baseImage = getBaseLevelImage();
Brandon Jonesf47bebc2014-07-09 14:28:42 -070077 return (baseImage ? baseImage->getDepth() : 0);
78}
79
80// Note: "base level image" is loosely defined to be any image from the base level,
81// where in the base of 2D array textures and cube maps there are several. Don't use
82// the base level image for anything except querying texture format and size.
83GLenum TextureD3D::getBaseLevelInternalFormat() const
84{
Brandon Jones78b1acd2014-07-15 15:33:07 -070085 const Image *baseImage = getBaseLevelImage();
Brandon Jonesf47bebc2014-07-09 14:28:42 -070086 return (baseImage ? baseImage->getInternalFormat() : GL_NONE);
87}
88
Brandon Jones78b1acd2014-07-15 15:33:07 -070089void TextureD3D::setImage(const gl::PixelUnpackState &unpack, GLenum type, const void *pixels, Image *image)
Brandon Jonesf47bebc2014-07-09 14:28:42 -070090{
91 // No-op
92 if (image->getWidth() == 0 || image->getHeight() == 0 || image->getDepth() == 0)
93 {
94 return;
95 }
96
97 // We no longer need the "GLenum format" parameter to TexImage to determine what data format "pixels" contains.
98 // From our image internal format we know how many channels to expect, and "type" gives the format of pixel's components.
99 const void *pixelData = pixels;
100
101 if (unpack.pixelBuffer.id() != 0)
102 {
103 // Do a CPU readback here, if we have an unpack buffer bound and the fast GPU path is not supported
104 gl::Buffer *pixelBuffer = unpack.pixelBuffer.get();
105 ptrdiff_t offset = reinterpret_cast<ptrdiff_t>(pixels);
106 // TODO: setImage/subImage is the only place outside of renderer that asks for a buffers raw data.
107 // This functionality should be moved into renderer and the getData method of BufferImpl removed.
108 const void *bufferData = pixelBuffer->getImplementation()->getData();
109 pixelData = static_cast<const unsigned char *>(bufferData) + offset;
110 }
111
112 if (pixelData != NULL)
113 {
114 image->loadData(0, 0, 0, image->getWidth(), image->getHeight(), image->getDepth(), unpack.alignment, type, pixelData);
115 mDirtyImages = true;
116 }
117}
118
119bool TextureD3D::subImage(GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth,
Brandon Jones78b1acd2014-07-15 15:33:07 -0700120 GLenum format, GLenum type, const gl::PixelUnpackState &unpack, const void *pixels, Image *image)
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700121{
122 const void *pixelData = pixels;
123
124 // CPU readback & copy where direct GPU copy is not supported
125 if (unpack.pixelBuffer.id() != 0)
126 {
127 gl::Buffer *pixelBuffer = unpack.pixelBuffer.get();
128 unsigned int offset = reinterpret_cast<unsigned int>(pixels);
129 // TODO: setImage/subImage is the only place outside of renderer that asks for a buffers raw data.
130 // This functionality should be moved into renderer and the getData method of BufferImpl removed.
131 const void *bufferData = pixelBuffer->getImplementation()->getData();
132 pixelData = static_cast<const unsigned char *>(bufferData) + offset;
133 }
134
135 if (pixelData != NULL)
136 {
137 image->loadData(xoffset, yoffset, zoffset, width, height, depth, unpack.alignment, type, pixelData);
138 mDirtyImages = true;
139 }
140
141 return true;
142}
143
Brandon Jones78b1acd2014-07-15 15:33:07 -0700144void TextureD3D::setCompressedImage(GLsizei imageSize, const void *pixels, Image *image)
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700145{
146 if (pixels != NULL)
147 {
148 image->loadCompressedData(0, 0, 0, image->getWidth(), image->getHeight(), image->getDepth(), pixels);
149 mDirtyImages = true;
150 }
151}
152
153bool TextureD3D::subImageCompressed(GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth,
Brandon Jones78b1acd2014-07-15 15:33:07 -0700154 GLenum format, GLsizei imageSize, const void *pixels, Image *image)
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700155{
156 if (pixels != NULL)
157 {
158 image->loadCompressedData(xoffset, yoffset, zoffset, width, height, depth, pixels);
159 mDirtyImages = true;
160 }
161
162 return true;
163}
164
165bool TextureD3D::isFastUnpackable(const gl::PixelUnpackState &unpack, GLenum sizedInternalFormat)
166{
167 return unpack.pixelBuffer.id() != 0 && mRenderer->supportsFastCopyBufferToTexture(sizedInternalFormat);
168}
169
170bool TextureD3D::fastUnpackPixels(const gl::PixelUnpackState &unpack, const void *pixels, const gl::Box &destArea,
Brandon Jones78b1acd2014-07-15 15:33:07 -0700171 GLenum sizedInternalFormat, GLenum type, RenderTarget *destRenderTarget)
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700172{
173 if (destArea.width <= 0 && destArea.height <= 0 && destArea.depth <= 0)
174 {
175 return true;
176 }
177
178 // In order to perform the fast copy through the shader, we must have the right format, and be able
179 // to create a render target.
180 ASSERT(mRenderer->supportsFastCopyBufferToTexture(sizedInternalFormat));
181
182 unsigned int offset = reinterpret_cast<unsigned int>(pixels);
183
184 return mRenderer->fastCopyBufferToTexture(unpack, offset, destRenderTarget, sizedInternalFormat, type, destArea);
185}
186
187GLint TextureD3D::creationLevels(GLsizei width, GLsizei height, GLsizei depth) const
188{
189 if ((gl::isPow2(width) && gl::isPow2(height) && gl::isPow2(depth)) || mRenderer->getRendererExtensions().textureNPOT)
190 {
191 // Maximum number of levels
192 return gl::log2(std::max(std::max(width, height), depth)) + 1;
193 }
194 else
195 {
196 // OpenGL ES 2.0 without GL_OES_texture_npot does not permit NPOT mipmaps.
197 return 1;
198 }
199}
200
201int TextureD3D::mipLevels() const
202{
203 return gl::log2(std::max(std::max(getBaseLevelWidth(), getBaseLevelHeight()), getBaseLevelDepth())) + 1;
204}
205
206
Brandon Jones78b1acd2014-07-15 15:33:07 -0700207TextureD3D_2D::TextureD3D_2D(Renderer *renderer)
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700208 : TextureD3D(renderer),
209 Texture2DImpl(),
210 mTexStorage(NULL)
211{
212 for (int i = 0; i < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS; ++i)
213 {
214 mImageArray[i] = ImageD3D::makeImageD3D(renderer->createImage());
215 }
216}
217
218TextureD3D_2D::~TextureD3D_2D()
219{
220 SafeDelete(mTexStorage);
221
222 for (int i = 0; i < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS; ++i)
223 {
224 delete mImageArray[i];
225 }
226}
227
228TextureD3D_2D *TextureD3D_2D::makeTextureD3D_2D(Texture2DImpl *texture)
229{
230 ASSERT(HAS_DYNAMIC_TYPE(TextureD3D_2D*, texture));
231 return static_cast<TextureD3D_2D*>(texture);
232}
233
234TextureStorageInterface *TextureD3D_2D::getNativeTexture()
235{
236 // ensure the underlying texture is created
237 initializeStorage(false);
238
Brandon Jones78b1acd2014-07-15 15:33:07 -0700239 TextureStorageInterface *storage = getBaseLevelStorage();
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700240 if (storage)
241 {
242 updateStorage();
243 }
244
245 return storage;
246}
247
Brandon Jonescef06ff2014-08-05 13:27:48 -0700248Image *TextureD3D_2D::getImage(int level, int layer) const
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700249{
250 ASSERT(level < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS);
Brandon Jonescef06ff2014-08-05 13:27:48 -0700251 ASSERT(layer == 0);
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700252 return mImageArray[level];
253}
254
Brandon Jonescef06ff2014-08-05 13:27:48 -0700255GLsizei TextureD3D_2D::getLayerCount(int level) const
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700256{
Brandon Jonescef06ff2014-08-05 13:27:48 -0700257 ASSERT(level < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS);
258 return 1;
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700259}
260
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700261GLsizei TextureD3D_2D::getWidth(GLint level) const
262{
263 if (level < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS)
264 return mImageArray[level]->getWidth();
265 else
266 return 0;
267}
268
269GLsizei TextureD3D_2D::getHeight(GLint level) const
270{
271 if (level < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS)
272 return mImageArray[level]->getHeight();
273 else
274 return 0;
275}
276
277GLenum TextureD3D_2D::getInternalFormat(GLint level) const
278{
279 if (level < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS)
280 return mImageArray[level]->getInternalFormat();
281 else
282 return GL_NONE;
283}
284
285GLenum TextureD3D_2D::getActualFormat(GLint level) const
286{
287 if (level < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS)
288 return mImageArray[level]->getActualFormat();
289 else
290 return GL_NONE;
291}
292
293bool TextureD3D_2D::isDepth(GLint level) const
294{
Geoff Lang5d601382014-07-22 15:14:06 -0400295 return gl::GetInternalFormatInfo(getInternalFormat(level)).depthBits > 0;
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700296}
297
Brandon Jonescef06ff2014-08-05 13:27:48 -0700298void TextureD3D_2D::setImage(GLenum target, GLint level, GLsizei width, GLsizei height, GLsizei depth, GLenum internalFormat, GLenum format, GLenum type, const gl::PixelUnpackState &unpack, const void *pixels)
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700299{
Brandon Jonescef06ff2014-08-05 13:27:48 -0700300 ASSERT(target == GL_TEXTURE_2D && depth == 1);
301
Geoff Lang5d601382014-07-22 15:14:06 -0400302 GLenum sizedInternalFormat = gl::GetSizedInternalFormat(internalFormat, type);
303
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700304 bool fastUnpacked = false;
305
Brandon Jonescef06ff2014-08-05 13:27:48 -0700306 redefineImage(level, sizedInternalFormat, width, height);
307
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700308 // Attempt a fast gpu copy of the pixel data to the surface
309 if (isFastUnpackable(unpack, sizedInternalFormat) && isLevelComplete(level))
310 {
311 // Will try to create RT storage if it does not exist
Brandon Jonescef06ff2014-08-05 13:27:48 -0700312 RenderTarget *destRenderTarget = getRenderTarget(level, 0);
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700313 gl::Box destArea(0, 0, 0, getWidth(level), getHeight(level), 1);
314
315 if (destRenderTarget && fastUnpackPixels(unpack, pixels, destArea, sizedInternalFormat, type, destRenderTarget))
316 {
317 // Ensure we don't overwrite our newly initialized data
318 mImageArray[level]->markClean();
319
320 fastUnpacked = true;
321 }
322 }
323
324 if (!fastUnpacked)
325 {
326 TextureD3D::setImage(unpack, type, pixels, mImageArray[level]);
327 }
328}
329
Brandon Jonescef06ff2014-08-05 13:27:48 -0700330void TextureD3D_2D::setCompressedImage(GLenum target, GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei depth, GLsizei imageSize, const void *pixels)
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700331{
Brandon Jonescef06ff2014-08-05 13:27:48 -0700332 ASSERT(target == GL_TEXTURE_2D && depth == 1);
333
334 // compressed formats don't have separate sized internal formats-- we can just use the compressed format directly
335 redefineImage(level, format, width, height);
336
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700337 TextureD3D::setCompressedImage(imageSize, pixels, mImageArray[level]);
338}
339
Brandon Jonescef06ff2014-08-05 13:27:48 -0700340void TextureD3D_2D::subImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const gl::PixelUnpackState &unpack, const void *pixels)
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700341{
Brandon Jonescef06ff2014-08-05 13:27:48 -0700342 ASSERT(target == GL_TEXTURE_2D && depth == 1 && zoffset == 0);
343
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700344 bool fastUnpacked = false;
345
346 if (isFastUnpackable(unpack, getInternalFormat(level)) && isLevelComplete(level))
347 {
Brandon Jonescef06ff2014-08-05 13:27:48 -0700348 RenderTarget *renderTarget = getRenderTarget(level, 0);
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700349 gl::Box destArea(xoffset, yoffset, 0, width, height, 1);
350
351 if (renderTarget && fastUnpackPixels(unpack, pixels, destArea, getInternalFormat(level), type, renderTarget))
352 {
353 // Ensure we don't overwrite our newly initialized data
354 mImageArray[level]->markClean();
355
356 fastUnpacked = true;
357 }
358 }
359
360 if (!fastUnpacked && TextureD3D::subImage(xoffset, yoffset, 0, width, height, 1, format, type, unpack, pixels, mImageArray[level]))
361 {
362 commitRect(level, xoffset, yoffset, width, height);
363 }
364}
365
Brandon Jonescef06ff2014-08-05 13:27:48 -0700366void TextureD3D_2D::subImageCompressed(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *pixels)
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700367{
Brandon Jonescef06ff2014-08-05 13:27:48 -0700368 ASSERT(target == GL_TEXTURE_2D && depth == 1 && zoffset == 0);
369
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700370 if (TextureD3D::subImageCompressed(xoffset, yoffset, 0, width, height, 1, format, imageSize, pixels, mImageArray[level]))
371 {
372 commitRect(level, xoffset, yoffset, width, height);
373 }
374}
375
Brandon Jonescef06ff2014-08-05 13:27:48 -0700376void TextureD3D_2D::copyImage(GLenum target, GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, gl::Framebuffer *source)
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700377{
Brandon Jonescef06ff2014-08-05 13:27:48 -0700378 ASSERT(target == GL_TEXTURE_2D);
379
380 GLenum sizedInternalFormat = gl::GetSizedInternalFormat(format, GL_UNSIGNED_BYTE);
381 redefineImage(level, sizedInternalFormat, width, height);
382
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700383 if (!mImageArray[level]->isRenderableFormat())
384 {
385 mImageArray[level]->copy(0, 0, 0, x, y, width, height, source);
386 mDirtyImages = true;
387 }
388 else
389 {
390 ensureRenderTarget();
391 mImageArray[level]->markClean();
392
393 if (width != 0 && height != 0 && isValidLevel(level))
394 {
395 gl::Rectangle sourceRect;
396 sourceRect.x = x;
397 sourceRect.width = width;
398 sourceRect.y = y;
399 sourceRect.height = height;
400
401 mRenderer->copyImage(source, sourceRect, format, 0, 0, mTexStorage, level);
402 }
403 }
404}
405
406void TextureD3D_2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, gl::Framebuffer *source)
407{
Brandon Jonescef06ff2014-08-05 13:27:48 -0700408 ASSERT(target == GL_TEXTURE_2D && zoffset == 0);
409
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700410 // can only make our texture storage to a render target if level 0 is defined (with a width & height) and
411 // the current level we're copying to is defined (with appropriate format, width & height)
412 bool canCreateRenderTarget = isLevelComplete(level) && isLevelComplete(0);
413
414 if (!mImageArray[level]->isRenderableFormat() || (!mTexStorage && !canCreateRenderTarget))
415 {
416 mImageArray[level]->copy(xoffset, yoffset, 0, x, y, width, height, source);
417 mDirtyImages = true;
418 }
419 else
420 {
421 ensureRenderTarget();
422
423 if (isValidLevel(level))
424 {
425 updateStorageLevel(level);
426
427 gl::Rectangle sourceRect;
428 sourceRect.x = x;
429 sourceRect.width = width;
430 sourceRect.y = y;
431 sourceRect.height = height;
432
433 mRenderer->copyImage(source, sourceRect,
Geoff Lang5d601382014-07-22 15:14:06 -0400434 gl::GetInternalFormatInfo(getBaseLevelInternalFormat()).format,
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700435 xoffset, yoffset, mTexStorage, level);
436 }
437 }
438}
439
Brandon Jonescef06ff2014-08-05 13:27:48 -0700440void TextureD3D_2D::storage(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth)
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700441{
Brandon Jonescef06ff2014-08-05 13:27:48 -0700442 ASSERT(target == GL_TEXTURE_2D && depth == 1);
443
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700444 for (int level = 0; level < levels; level++)
445 {
446 GLsizei levelWidth = std::max(1, width >> level);
447 GLsizei levelHeight = std::max(1, height >> level);
448 mImageArray[level]->redefine(mRenderer, GL_TEXTURE_2D, internalformat, levelWidth, levelHeight, 1, true);
449 }
450
451 for (int level = levels; level < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
452 {
453 mImageArray[level]->redefine(mRenderer, GL_TEXTURE_2D, GL_NONE, 0, 0, 0, true);
454 }
455
456 mImmutable = true;
457
Brandon Jones78b1acd2014-07-15 15:33:07 -0700458 setCompleteTexStorage(new TextureStorageInterface2D(mRenderer, internalformat, IsRenderTargetUsage(mUsage), width, height, levels));
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700459}
460
461// Tests for 2D texture sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 85.
462bool TextureD3D_2D::isSamplerComplete(const gl::SamplerState &samplerState) const
463{
464 GLsizei width = getBaseLevelWidth();
465 GLsizei height = getBaseLevelHeight();
466
467 if (width <= 0 || height <= 0)
468 {
469 return false;
470 }
471
Geoff Lang6cf8e1b2014-07-03 13:03:57 -0400472 if (!mRenderer->getRendererTextureCaps().get(getInternalFormat(0)).filterable)
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700473 {
474 if (samplerState.magFilter != GL_NEAREST ||
475 (samplerState.minFilter != GL_NEAREST && samplerState.minFilter != GL_NEAREST_MIPMAP_NEAREST))
476 {
477 return false;
478 }
479 }
480
481 // TODO(geofflang): use context's extensions
482 bool npotSupport = mRenderer->getRendererExtensions().textureNPOT;
483
484 if (!npotSupport)
485 {
486 if ((samplerState.wrapS != GL_CLAMP_TO_EDGE && !gl::isPow2(width)) ||
487 (samplerState.wrapT != GL_CLAMP_TO_EDGE && !gl::isPow2(height)))
488 {
489 return false;
490 }
491 }
492
493 if (IsMipmapFiltered(samplerState))
494 {
495 if (!npotSupport)
496 {
497 if (!gl::isPow2(width) || !gl::isPow2(height))
498 {
499 return false;
500 }
501 }
502
503 if (!isMipmapComplete())
504 {
505 return false;
506 }
507 }
508
509 // OpenGLES 3.0.2 spec section 3.8.13 states that a texture is not mipmap complete if:
510 // The internalformat specified for the texture arrays is a sized internal depth or
511 // depth and stencil format (see table 3.13), the value of TEXTURE_COMPARE_-
512 // MODE is NONE, and either the magnification filter is not NEAREST or the mini-
513 // fication filter is neither NEAREST nor NEAREST_MIPMAP_NEAREST.
Geoff Lang5d601382014-07-22 15:14:06 -0400514 const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(getInternalFormat(0));
515 if (formatInfo.depthBits > 0 && mRenderer->getCurrentClientVersion() > 2)
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700516 {
517 if (samplerState.compareMode == GL_NONE)
518 {
519 if ((samplerState.minFilter != GL_NEAREST && samplerState.minFilter != GL_NEAREST_MIPMAP_NEAREST) ||
520 samplerState.magFilter != GL_NEAREST)
521 {
522 return false;
523 }
524 }
525 }
526
527 return true;
528}
529
530void TextureD3D_2D::bindTexImage(egl::Surface *surface)
531{
532 GLenum internalformat = surface->getFormat();
533
534 mImageArray[0]->redefine(mRenderer, GL_TEXTURE_2D, internalformat, surface->getWidth(), surface->getHeight(), 1, true);
535
536 if (mTexStorage)
537 {
538 SafeDelete(mTexStorage);
539 }
Brandon Jones78b1acd2014-07-15 15:33:07 -0700540 mTexStorage = new TextureStorageInterface2D(mRenderer, surface->getSwapChain());
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700541
542 mDirtyImages = true;
543}
544
545void TextureD3D_2D::releaseTexImage()
546{
547 if (mTexStorage)
548 {
549 SafeDelete(mTexStorage);
550 }
551
552 for (int i = 0; i < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
553 {
554 mImageArray[i]->redefine(mRenderer, GL_TEXTURE_2D, GL_NONE, 0, 0, 0, true);
555 }
556}
557
558void TextureD3D_2D::generateMipmaps()
559{
Brandon Jonescef06ff2014-08-05 13:27:48 -0700560 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700561 int levelCount = mipLevels();
Brandon Jonescef06ff2014-08-05 13:27:48 -0700562 for (int level = 1; level < levelCount; level++)
563 {
564 redefineImage(level, getBaseLevelInternalFormat(),
565 std::max(getBaseLevelWidth() >> level, 1),
566 std::max(getBaseLevelHeight() >> level, 1));
567 }
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700568
569 if (mTexStorage && mTexStorage->isRenderTarget())
570 {
571 for (int level = 1; level < levelCount; level++)
572 {
573 mTexStorage->generateMipmap(level);
574
575 mImageArray[level]->markClean();
576 }
577 }
578 else
579 {
580 for (int level = 1; level < levelCount; level++)
581 {
582 mRenderer->generateMipmap(mImageArray[level], mImageArray[level - 1]);
583 }
584 }
585}
586
Brandon Jonescef06ff2014-08-05 13:27:48 -0700587unsigned int TextureD3D_2D::getRenderTargetSerial(GLint level, GLint layer)
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700588{
Brandon Jonescef06ff2014-08-05 13:27:48 -0700589 ASSERT(layer == 0);
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700590 return (ensureRenderTarget() ? mTexStorage->getRenderTargetSerial(level) : 0);
591}
592
Brandon Jonescef06ff2014-08-05 13:27:48 -0700593RenderTarget *TextureD3D_2D::getRenderTarget(GLint level, GLint layer)
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700594{
Brandon Jonescef06ff2014-08-05 13:27:48 -0700595 ASSERT(layer == 0);
596
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700597 // ensure the underlying texture is created
598 if (!ensureRenderTarget())
599 {
600 return NULL;
601 }
602
603 updateStorageLevel(level);
604
605 // ensure this is NOT a depth texture
606 if (isDepth(level))
607 {
608 return NULL;
609 }
610
611 return mTexStorage->getRenderTarget(level);
612}
613
Brandon Jonescef06ff2014-08-05 13:27:48 -0700614RenderTarget *TextureD3D_2D::getDepthStencil(GLint level, GLint layer)
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700615{
Brandon Jonescef06ff2014-08-05 13:27:48 -0700616 ASSERT(layer == 0);
617
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700618 // ensure the underlying texture is created
619 if (!ensureRenderTarget())
620 {
621 return NULL;
622 }
623
624 updateStorageLevel(level);
625
626 // ensure this is actually a depth texture
627 if (!isDepth(level))
628 {
629 return NULL;
630 }
631
632 return mTexStorage->getRenderTarget(level);
633}
634
635// Tests for 2D texture (mipmap) completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
636bool TextureD3D_2D::isMipmapComplete() const
637{
638 int levelCount = mipLevels();
639
640 for (int level = 0; level < levelCount; level++)
641 {
642 if (!isLevelComplete(level))
643 {
644 return false;
645 }
646 }
647
648 return true;
649}
650
651bool TextureD3D_2D::isValidLevel(int level) const
652{
653 return (mTexStorage ? (level >= 0 && level < mTexStorage->getLevelCount()) : false);
654}
655
656bool TextureD3D_2D::isLevelComplete(int level) const
657{
658 if (isImmutable())
659 {
660 return true;
661 }
662
Brandon Jones78b1acd2014-07-15 15:33:07 -0700663 const Image *baseImage = getBaseLevelImage();
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700664
665 GLsizei width = baseImage->getWidth();
666 GLsizei height = baseImage->getHeight();
667
668 if (width <= 0 || height <= 0)
669 {
670 return false;
671 }
672
673 // The base image level is complete if the width and height are positive
674 if (level == 0)
675 {
676 return true;
677 }
678
679 ASSERT(level >= 1 && level <= (int)ArraySize(mImageArray) && mImageArray[level] != NULL);
Brandon Jones78b1acd2014-07-15 15:33:07 -0700680 ImageD3D *image = mImageArray[level];
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700681
682 if (image->getInternalFormat() != baseImage->getInternalFormat())
683 {
684 return false;
685 }
686
687 if (image->getWidth() != std::max(1, width >> level))
688 {
689 return false;
690 }
691
692 if (image->getHeight() != std::max(1, height >> level))
693 {
694 return false;
695 }
696
697 return true;
698}
699
700// Constructs a native texture resource from the texture images
701void TextureD3D_2D::initializeStorage(bool renderTarget)
702{
703 // Only initialize the first time this texture is used as a render target or shader resource
704 if (mTexStorage)
705 {
706 return;
707 }
708
709 // do not attempt to create storage for nonexistant data
710 if (!isLevelComplete(0))
711 {
712 return;
713 }
714
715 bool createRenderTarget = (renderTarget || IsRenderTargetUsage(mUsage));
716
717 setCompleteTexStorage(createCompleteStorage(createRenderTarget));
718 ASSERT(mTexStorage);
719
720 // flush image data to the storage
721 updateStorage();
722}
723
Brandon Jones78b1acd2014-07-15 15:33:07 -0700724TextureStorageInterface2D *TextureD3D_2D::createCompleteStorage(bool renderTarget) const
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700725{
726 GLsizei width = getBaseLevelWidth();
727 GLsizei height = getBaseLevelHeight();
728
729 ASSERT(width > 0 && height > 0);
730
731 // use existing storage level count, when previously specified by TexStorage*D
732 GLint levels = (mTexStorage ? mTexStorage->getLevelCount() : creationLevels(width, height, 1));
733
Brandon Jones78b1acd2014-07-15 15:33:07 -0700734 return new TextureStorageInterface2D(mRenderer, getBaseLevelInternalFormat(), renderTarget, width, height, levels);
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700735}
736
737void TextureD3D_2D::setCompleteTexStorage(TextureStorageInterface2D *newCompleteTexStorage)
738{
739 SafeDelete(mTexStorage);
740 mTexStorage = newCompleteTexStorage;
741
742 if (mTexStorage && mTexStorage->isManaged())
743 {
744 for (int level = 0; level < mTexStorage->getLevelCount(); level++)
745 {
746 mImageArray[level]->setManagedSurface(mTexStorage, level);
747 }
748 }
749
750 mDirtyImages = true;
751}
752
753void TextureD3D_2D::updateStorage()
754{
755 ASSERT(mTexStorage != NULL);
756 GLint storageLevels = mTexStorage->getLevelCount();
757 for (int level = 0; level < storageLevels; level++)
758 {
759 if (mImageArray[level]->isDirty() && isLevelComplete(level))
760 {
761 updateStorageLevel(level);
762 }
763 }
764}
765
766bool TextureD3D_2D::ensureRenderTarget()
767{
768 initializeStorage(true);
769
770 if (getBaseLevelWidth() > 0 && getBaseLevelHeight() > 0)
771 {
772 ASSERT(mTexStorage);
773 if (!mTexStorage->isRenderTarget())
774 {
Brandon Jones78b1acd2014-07-15 15:33:07 -0700775 TextureStorageInterface2D *newRenderTargetStorage = createCompleteStorage(true);
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700776
777 if (!mRenderer->copyToRenderTarget(newRenderTargetStorage, mTexStorage))
778 {
779 delete newRenderTargetStorage;
780 return gl::error(GL_OUT_OF_MEMORY, false);
781 }
782
783 setCompleteTexStorage(newRenderTargetStorage);
784 }
785 }
786
787 return (mTexStorage && mTexStorage->isRenderTarget());
788}
789
790TextureStorageInterface *TextureD3D_2D::getBaseLevelStorage()
791{
792 return mTexStorage;
793}
794
795const ImageD3D *TextureD3D_2D::getBaseLevelImage() const
796{
797 return mImageArray[0];
798}
799
800void TextureD3D_2D::updateStorageLevel(int level)
801{
802 ASSERT(level <= (int)ArraySize(mImageArray) && mImageArray[level] != NULL);
803 ASSERT(isLevelComplete(level));
804
805 if (mImageArray[level]->isDirty())
806 {
807 commitRect(level, 0, 0, getWidth(level), getHeight(level));
808 }
809}
810
811void TextureD3D_2D::redefineImage(GLint level, GLenum internalformat, GLsizei width, GLsizei height)
812{
813 // If there currently is a corresponding storage texture image, it has these parameters
814 const int storageWidth = std::max(1, getBaseLevelWidth() >> level);
815 const int storageHeight = std::max(1, getBaseLevelHeight() >> level);
816 const GLenum storageFormat = getBaseLevelInternalFormat();
817
818 mImageArray[level]->redefine(mRenderer, GL_TEXTURE_2D, internalformat, width, height, 1, false);
819
820 if (mTexStorage)
821 {
822 const int storageLevels = mTexStorage->getLevelCount();
823
824 if ((level >= storageLevels && storageLevels != 0) ||
825 width != storageWidth ||
826 height != storageHeight ||
827 internalformat != storageFormat) // Discard mismatched storage
828 {
829 for (int i = 0; i < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
830 {
831 mImageArray[i]->markDirty();
832 }
833
834 SafeDelete(mTexStorage);
835 mDirtyImages = true;
836 }
837 }
838}
839
840void TextureD3D_2D::commitRect(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
841{
842 if (isValidLevel(level))
843 {
Brandon Jones78b1acd2014-07-15 15:33:07 -0700844 ImageD3D *image = mImageArray[level];
Brandon Jonesf47bebc2014-07-09 14:28:42 -0700845 if (image->copyToStorage(mTexStorage, level, xoffset, yoffset, width, height))
846 {
847 image->markClean();
848 }
849 }
850}
851
Brandon Jones0511e802014-07-14 16:27:26 -0700852
Brandon Jones78b1acd2014-07-15 15:33:07 -0700853TextureD3D_Cube::TextureD3D_Cube(Renderer *renderer)
Brandon Jones0511e802014-07-14 16:27:26 -0700854 : TextureCubeImpl(),
855 TextureD3D(renderer),
856 mTexStorage(NULL)
857{
858 for (int i = 0; i < 6; i++)
859 {
860 for (int j = 0; j < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS; ++j)
861 {
862 mImageArray[i][j] = ImageD3D::makeImageD3D(renderer->createImage());
863 }
864 }
865}
866
867TextureD3D_Cube::~TextureD3D_Cube()
868{
869 SafeDelete(mTexStorage);
870
871 for (int i = 0; i < 6; i++)
872 {
873 for (int j = 0; j < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS; ++j)
874 {
875 SafeDelete(mImageArray[i][j]);
876 }
877 }
878}
879
880TextureD3D_Cube *TextureD3D_Cube::makeTextureD3D_Cube(TextureCubeImpl *texture)
881{
882 ASSERT(HAS_DYNAMIC_TYPE(TextureD3D_Cube*, texture));
883 return static_cast<TextureD3D_Cube*>(texture);
884}
885
886TextureStorageInterface *TextureD3D_Cube::getNativeTexture()
887{
888 // ensure the underlying texture is created
889 initializeStorage(false);
890
Brandon Jones78b1acd2014-07-15 15:33:07 -0700891 TextureStorageInterface *storage = getBaseLevelStorage();
Brandon Jones0511e802014-07-14 16:27:26 -0700892 if (storage)
893 {
894 updateStorage();
895 }
896
897 return storage;
898}
899
Brandon Jonescef06ff2014-08-05 13:27:48 -0700900Image *TextureD3D_Cube::getImage(int level, int layer) const
Brandon Jones0511e802014-07-14 16:27:26 -0700901{
902 ASSERT(level < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS);
Brandon Jonescef06ff2014-08-05 13:27:48 -0700903 ASSERT(layer < 6);
904 return mImageArray[layer][level];
Brandon Jones0511e802014-07-14 16:27:26 -0700905}
906
Brandon Jonescef06ff2014-08-05 13:27:48 -0700907GLsizei TextureD3D_Cube::getLayerCount(int level) const
Brandon Jones0511e802014-07-14 16:27:26 -0700908{
Brandon Jonescef06ff2014-08-05 13:27:48 -0700909 ASSERT(level < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS);
910 return 6;
Brandon Jones0511e802014-07-14 16:27:26 -0700911}
912
Brandon Jonescef06ff2014-08-05 13:27:48 -0700913GLenum TextureD3D_Cube::getInternalFormat(GLint level, GLint layer) const
Brandon Jones0511e802014-07-14 16:27:26 -0700914{
915 if (level < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS)
Brandon Jonescef06ff2014-08-05 13:27:48 -0700916 return mImageArray[layer][level]->getInternalFormat();
Brandon Jones0511e802014-07-14 16:27:26 -0700917 else
918 return GL_NONE;
919}
920
Brandon Jonescef06ff2014-08-05 13:27:48 -0700921bool TextureD3D_Cube::isDepth(GLint level, GLint layer) const
Brandon Jones0511e802014-07-14 16:27:26 -0700922{
Brandon Jonescef06ff2014-08-05 13:27:48 -0700923 return gl::GetInternalFormatInfo(getInternalFormat(level, layer)).depthBits > 0;
Brandon Jones0511e802014-07-14 16:27:26 -0700924}
925
Brandon Jonescef06ff2014-08-05 13:27:48 -0700926void TextureD3D_Cube::setImage(GLenum target, GLint level, GLsizei width, GLsizei height, GLsizei depth, GLenum internalFormat, GLenum format, GLenum type, const gl::PixelUnpackState &unpack, const void *pixels)
Brandon Jones0511e802014-07-14 16:27:26 -0700927{
Brandon Jonescef06ff2014-08-05 13:27:48 -0700928 ASSERT(depth == 1);
929
930 int faceIndex = gl::TextureCubeMap::targetToLayerIndex(target);
Geoff Lang5d601382014-07-22 15:14:06 -0400931 GLenum sizedInternalFormat = gl::GetSizedInternalFormat(internalFormat, type);
Brandon Jones0511e802014-07-14 16:27:26 -0700932
933 redefineImage(faceIndex, level, sizedInternalFormat, width, height);
934
935 TextureD3D::setImage(unpack, type, pixels, mImageArray[faceIndex][level]);
936}
937
Brandon Jonescef06ff2014-08-05 13:27:48 -0700938void TextureD3D_Cube::setCompressedImage(GLenum target, GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei depth, GLsizei imageSize, const void *pixels)
Brandon Jones0511e802014-07-14 16:27:26 -0700939{
Brandon Jonescef06ff2014-08-05 13:27:48 -0700940 ASSERT(depth == 1);
941
Brandon Jones0511e802014-07-14 16:27:26 -0700942 // compressed formats don't have separate sized internal formats-- we can just use the compressed format directly
Brandon Jonescef06ff2014-08-05 13:27:48 -0700943 int faceIndex = gl::TextureCubeMap::targetToLayerIndex(target);
944
Brandon Jones0511e802014-07-14 16:27:26 -0700945 redefineImage(faceIndex, level, format, width, height);
946
947 TextureD3D::setCompressedImage(imageSize, pixels, mImageArray[faceIndex][level]);
948}
949
Brandon Jonescef06ff2014-08-05 13:27:48 -0700950void TextureD3D_Cube::subImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const gl::PixelUnpackState &unpack, const void *pixels)
Brandon Jones0511e802014-07-14 16:27:26 -0700951{
Brandon Jonescef06ff2014-08-05 13:27:48 -0700952 ASSERT(depth == 1 && zoffset == 0);
953
954 int faceIndex = gl::TextureCubeMap::targetToLayerIndex(target);
955
Brandon Jones0511e802014-07-14 16:27:26 -0700956 if (TextureD3D::subImage(xoffset, yoffset, 0, width, height, 1, format, type, unpack, pixels, mImageArray[faceIndex][level]))
957 {
958 commitRect(faceIndex, level, xoffset, yoffset, width, height);
959 }
960}
961
Brandon Jonescef06ff2014-08-05 13:27:48 -0700962void TextureD3D_Cube::subImageCompressed(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *pixels)
Brandon Jones0511e802014-07-14 16:27:26 -0700963{
Brandon Jonescef06ff2014-08-05 13:27:48 -0700964 ASSERT(depth == 1 && zoffset == 0);
965
966 int faceIndex = gl::TextureCubeMap::targetToLayerIndex(target);
967
Brandon Jones0511e802014-07-14 16:27:26 -0700968 if (TextureD3D::subImageCompressed(xoffset, yoffset, 0, width, height, 1, format, imageSize, pixels, mImageArray[faceIndex][level]))
969 {
970 commitRect(faceIndex, level, xoffset, yoffset, width, height);
971 }
972}
973
974void TextureD3D_Cube::copyImage(GLenum target, GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, gl::Framebuffer *source)
975{
Brandon Jonescef06ff2014-08-05 13:27:48 -0700976 int faceIndex = gl::TextureCubeMap::targetToLayerIndex(target);
Geoff Lang5d601382014-07-22 15:14:06 -0400977 GLenum sizedInternalFormat = gl::GetSizedInternalFormat(format, GL_UNSIGNED_BYTE);
978
Brandon Jones0511e802014-07-14 16:27:26 -0700979 redefineImage(faceIndex, level, sizedInternalFormat, width, height);
980
981 if (!mImageArray[faceIndex][level]->isRenderableFormat())
982 {
983 mImageArray[faceIndex][level]->copy(0, 0, 0, x, y, width, height, source);
984 mDirtyImages = true;
985 }
986 else
987 {
988 ensureRenderTarget();
989 mImageArray[faceIndex][level]->markClean();
990
991 ASSERT(width == height);
992
993 if (width > 0 && isValidFaceLevel(faceIndex, level))
994 {
995 gl::Rectangle sourceRect;
996 sourceRect.x = x;
997 sourceRect.width = width;
998 sourceRect.y = y;
999 sourceRect.height = height;
1000
1001 mRenderer->copyImage(source, sourceRect, format, 0, 0, mTexStorage, target, level);
1002 }
1003 }
1004}
1005
1006void TextureD3D_Cube::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, gl::Framebuffer *source)
1007{
Brandon Jonescef06ff2014-08-05 13:27:48 -07001008 int faceIndex = gl::TextureCubeMap::targetToLayerIndex(target);
Brandon Jones0511e802014-07-14 16:27:26 -07001009
1010 // We can only make our texture storage to a render target if the level we're copying *to* is complete
1011 // and the base level is cube-complete. The base level must be cube complete (common case) because we cannot
1012 // rely on the "getBaseLevel*" methods reliably otherwise.
1013 bool canCreateRenderTarget = isFaceLevelComplete(faceIndex, level) && isCubeComplete();
1014
1015 if (!mImageArray[faceIndex][level]->isRenderableFormat() || (!mTexStorage && !canCreateRenderTarget))
1016 {
1017 mImageArray[faceIndex][level]->copy(0, 0, 0, x, y, width, height, source);
1018 mDirtyImages = true;
1019 }
1020 else
1021 {
1022 ensureRenderTarget();
1023
1024 if (isValidFaceLevel(faceIndex, level))
1025 {
1026 updateStorageFaceLevel(faceIndex, level);
1027
1028 gl::Rectangle sourceRect;
1029 sourceRect.x = x;
1030 sourceRect.width = width;
1031 sourceRect.y = y;
1032 sourceRect.height = height;
1033
Geoff Lang5d601382014-07-22 15:14:06 -04001034 mRenderer->copyImage(source, sourceRect, gl::GetInternalFormatInfo(getBaseLevelInternalFormat()).format,
Brandon Jones0511e802014-07-14 16:27:26 -07001035 xoffset, yoffset, mTexStorage, target, level);
1036 }
1037 }
1038}
1039
Brandon Jonescef06ff2014-08-05 13:27:48 -07001040void TextureD3D_Cube::storage(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth)
Brandon Jones0511e802014-07-14 16:27:26 -07001041{
Brandon Jonescef06ff2014-08-05 13:27:48 -07001042 ASSERT(width == height);
1043 ASSERT(depth == 1);
1044
Brandon Jones0511e802014-07-14 16:27:26 -07001045 for (int level = 0; level < levels; level++)
1046 {
Brandon Jonescef06ff2014-08-05 13:27:48 -07001047 GLsizei mipSize = std::max(1, width >> level);
Brandon Jones0511e802014-07-14 16:27:26 -07001048 for (int faceIndex = 0; faceIndex < 6; faceIndex++)
1049 {
1050 mImageArray[faceIndex][level]->redefine(mRenderer, GL_TEXTURE_CUBE_MAP, internalformat, mipSize, mipSize, 1, true);
1051 }
1052 }
1053
1054 for (int level = levels; level < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
1055 {
1056 for (int faceIndex = 0; faceIndex < 6; faceIndex++)
1057 {
1058 mImageArray[faceIndex][level]->redefine(mRenderer, GL_TEXTURE_CUBE_MAP, GL_NONE, 0, 0, 0, true);
1059 }
1060 }
1061
1062 mImmutable = true;
1063
Brandon Jonescef06ff2014-08-05 13:27:48 -07001064 setCompleteTexStorage(new TextureStorageInterfaceCube(mRenderer, internalformat, IsRenderTargetUsage(mUsage), width, levels));
Brandon Jones0511e802014-07-14 16:27:26 -07001065}
1066
1067bool TextureD3D_Cube::isSamplerComplete(const gl::SamplerState &samplerState) const
1068{
1069 int size = getBaseLevelWidth();
1070
1071 bool mipmapping = IsMipmapFiltered(samplerState);
1072
1073 // TODO(geofflang): use context's texture caps
Brandon Jonescef06ff2014-08-05 13:27:48 -07001074 if (!mRenderer->getRendererTextureCaps().get(getInternalFormat(0, 0)).filterable)
Brandon Jones0511e802014-07-14 16:27:26 -07001075 {
1076 if (samplerState.magFilter != GL_NEAREST ||
1077 (samplerState.minFilter != GL_NEAREST && samplerState.minFilter != GL_NEAREST_MIPMAP_NEAREST))
1078 {
1079 return false;
1080 }
1081 }
1082
1083 // TODO(geofflang): use context's extensions
1084 if (!gl::isPow2(size) && !mRenderer->getRendererExtensions().textureNPOT)
1085 {
1086 if (samplerState.wrapS != GL_CLAMP_TO_EDGE || samplerState.wrapT != GL_CLAMP_TO_EDGE || mipmapping)
1087 {
1088 return false;
1089 }
1090 }
1091
1092 if (!mipmapping)
1093 {
1094 if (!isCubeComplete())
1095 {
1096 return false;
1097 }
1098 }
1099 else
1100 {
1101 if (!isMipmapCubeComplete()) // Also tests for isCubeComplete()
1102 {
1103 return false;
1104 }
1105 }
1106
1107 return true;
1108}
1109
1110// Tests for cube texture completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
1111bool TextureD3D_Cube::isCubeComplete() const
1112{
1113 int baseWidth = getBaseLevelWidth();
1114 int baseHeight = getBaseLevelHeight();
1115 GLenum baseFormat = getBaseLevelInternalFormat();
1116
1117 if (baseWidth <= 0 || baseWidth != baseHeight)
1118 {
1119 return false;
1120 }
1121
1122 for (int faceIndex = 1; faceIndex < 6; faceIndex++)
1123 {
1124 const ImageD3D &faceBaseImage = *mImageArray[faceIndex][0];
1125
1126 if (faceBaseImage.getWidth() != baseWidth ||
1127 faceBaseImage.getHeight() != baseHeight ||
1128 faceBaseImage.getInternalFormat() != baseFormat )
1129 {
1130 return false;
1131 }
1132 }
1133
1134 return true;
1135}
1136
1137void TextureD3D_Cube::generateMipmaps()
1138{
1139 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
1140 int levelCount = mipLevels();
1141 for (int faceIndex = 0; faceIndex < 6; faceIndex++)
1142 {
1143 for (int level = 1; level < levelCount; level++)
1144 {
1145 int faceLevelSize = (std::max(mImageArray[faceIndex][0]->getWidth() >> level, 1));
1146 redefineImage(faceIndex, level, mImageArray[faceIndex][0]->getInternalFormat(), faceLevelSize, faceLevelSize);
1147 }
1148 }
1149
1150 if (mTexStorage && mTexStorage->isRenderTarget())
1151 {
1152 for (int faceIndex = 0; faceIndex < 6; faceIndex++)
1153 {
1154 for (int level = 1; level < levelCount; level++)
1155 {
1156 mTexStorage->generateMipmap(faceIndex, level);
1157
1158 mImageArray[faceIndex][level]->markClean();
1159 }
1160 }
1161 }
1162 else
1163 {
1164 for (int faceIndex = 0; faceIndex < 6; faceIndex++)
1165 {
1166 for (int level = 1; level < levelCount; level++)
1167 {
1168 mRenderer->generateMipmap(mImageArray[faceIndex][level], mImageArray[faceIndex][level - 1]);
1169 }
1170 }
1171 }
1172}
1173
Brandon Jonescef06ff2014-08-05 13:27:48 -07001174unsigned int TextureD3D_Cube::getRenderTargetSerial(GLint level, GLint layer)
Brandon Jones0511e802014-07-14 16:27:26 -07001175{
Brandon Jonescef06ff2014-08-05 13:27:48 -07001176 GLenum target = gl::TextureCubeMap::layerIndexToTarget(layer);
Brandon Jones0511e802014-07-14 16:27:26 -07001177 return (ensureRenderTarget() ? mTexStorage->getRenderTargetSerial(target, level) : 0);
1178}
1179
Brandon Jonescef06ff2014-08-05 13:27:48 -07001180RenderTarget *TextureD3D_Cube::getRenderTarget(GLint level, GLint layer)
Brandon Jones0511e802014-07-14 16:27:26 -07001181{
Brandon Jonescef06ff2014-08-05 13:27:48 -07001182 GLenum target = gl::TextureCubeMap::layerIndexToTarget(layer);
Brandon Jones0511e802014-07-14 16:27:26 -07001183 ASSERT(gl::IsCubemapTextureTarget(target));
1184
1185 // ensure the underlying texture is created
1186 if (!ensureRenderTarget())
1187 {
1188 return NULL;
1189 }
1190
Brandon Jonescef06ff2014-08-05 13:27:48 -07001191 updateStorageFaceLevel(layer, level);
Brandon Jones0511e802014-07-14 16:27:26 -07001192
1193 // ensure this is NOT a depth texture
Brandon Jonescef06ff2014-08-05 13:27:48 -07001194 if (isDepth(level, layer))
Brandon Jones0511e802014-07-14 16:27:26 -07001195 {
1196 return NULL;
1197 }
1198
1199 return mTexStorage->getRenderTarget(target, level);
1200}
1201
Brandon Jonescef06ff2014-08-05 13:27:48 -07001202RenderTarget *TextureD3D_Cube::getDepthStencil(GLint level, GLint layer)
Brandon Jones0511e802014-07-14 16:27:26 -07001203{
Brandon Jonescef06ff2014-08-05 13:27:48 -07001204 GLenum target = gl::TextureCubeMap::layerIndexToTarget(layer);
Brandon Jones0511e802014-07-14 16:27:26 -07001205 ASSERT(gl::IsCubemapTextureTarget(target));
1206
1207 // ensure the underlying texture is created
1208 if (!ensureRenderTarget())
1209 {
1210 return NULL;
1211 }
1212
Brandon Jonescef06ff2014-08-05 13:27:48 -07001213 updateStorageFaceLevel(layer, level);
Brandon Jones0511e802014-07-14 16:27:26 -07001214
1215 // ensure this is a depth texture
Brandon Jonescef06ff2014-08-05 13:27:48 -07001216 if (!isDepth(level, layer))
Brandon Jones0511e802014-07-14 16:27:26 -07001217 {
1218 return NULL;
1219 }
1220
1221 return mTexStorage->getRenderTarget(target, level);
1222}
1223
Brandon Jones0511e802014-07-14 16:27:26 -07001224void TextureD3D_Cube::initializeStorage(bool renderTarget)
1225{
1226 // Only initialize the first time this texture is used as a render target or shader resource
1227 if (mTexStorage)
1228 {
1229 return;
1230 }
1231
1232 // do not attempt to create storage for nonexistant data
1233 if (!isFaceLevelComplete(0, 0))
1234 {
1235 return;
1236 }
1237
1238 bool createRenderTarget = (renderTarget || IsRenderTargetUsage(mUsage));
1239
1240 setCompleteTexStorage(createCompleteStorage(createRenderTarget));
1241 ASSERT(mTexStorage);
1242
1243 // flush image data to the storage
1244 updateStorage();
1245}
1246
1247TextureStorageInterfaceCube *TextureD3D_Cube::createCompleteStorage(bool renderTarget) const
1248{
1249 GLsizei size = getBaseLevelWidth();
1250
1251 ASSERT(size > 0);
1252
1253 // use existing storage level count, when previously specified by TexStorage*D
1254 GLint levels = (mTexStorage ? mTexStorage->getLevelCount() : creationLevels(size, size, 1));
1255
1256 return new TextureStorageInterfaceCube(mRenderer, getBaseLevelInternalFormat(), renderTarget, size, levels);
1257}
1258
1259void TextureD3D_Cube::setCompleteTexStorage(TextureStorageInterfaceCube *newCompleteTexStorage)
1260{
1261 SafeDelete(mTexStorage);
1262 mTexStorage = newCompleteTexStorage;
1263
1264 if (mTexStorage && mTexStorage->isManaged())
1265 {
1266 for (int faceIndex = 0; faceIndex < 6; faceIndex++)
1267 {
1268 for (int level = 0; level < mTexStorage->getLevelCount(); level++)
1269 {
1270 mImageArray[faceIndex][level]->setManagedSurface(mTexStorage, faceIndex, level);
1271 }
1272 }
1273 }
1274
1275 mDirtyImages = true;
1276}
1277
1278void TextureD3D_Cube::updateStorage()
1279{
1280 ASSERT(mTexStorage != NULL);
1281 GLint storageLevels = mTexStorage->getLevelCount();
1282 for (int face = 0; face < 6; face++)
1283 {
1284 for (int level = 0; level < storageLevels; level++)
1285 {
1286 if (mImageArray[face][level]->isDirty() && isFaceLevelComplete(face, level))
1287 {
1288 updateStorageFaceLevel(face, level);
1289 }
1290 }
1291 }
1292}
1293
1294bool TextureD3D_Cube::ensureRenderTarget()
1295{
1296 initializeStorage(true);
1297
1298 if (getBaseLevelWidth() > 0)
1299 {
1300 ASSERT(mTexStorage);
1301 if (!mTexStorage->isRenderTarget())
1302 {
1303 TextureStorageInterfaceCube *newRenderTargetStorage = createCompleteStorage(true);
1304
1305 if (!mRenderer->copyToRenderTarget(newRenderTargetStorage, mTexStorage))
1306 {
1307 delete newRenderTargetStorage;
1308 return gl::error(GL_OUT_OF_MEMORY, false);
1309 }
1310
1311 setCompleteTexStorage(newRenderTargetStorage);
1312 }
1313 }
1314
1315 return (mTexStorage && mTexStorage->isRenderTarget());
1316}
1317
1318TextureStorageInterface *TextureD3D_Cube::getBaseLevelStorage()
1319{
1320 return mTexStorage;
1321}
1322
1323const ImageD3D *TextureD3D_Cube::getBaseLevelImage() const
1324{
1325 // Note: if we are not cube-complete, there is no single base level image that can describe all
1326 // cube faces, so this method is only well-defined for a cube-complete base level.
1327 return mImageArray[0][0];
1328}
1329
1330bool TextureD3D_Cube::isMipmapCubeComplete() const
1331{
1332 if (isImmutable())
1333 {
1334 return true;
1335 }
1336
1337 if (!isCubeComplete())
1338 {
1339 return false;
1340 }
1341
1342 int levelCount = mipLevels();
1343
1344 for (int face = 0; face < 6; face++)
1345 {
1346 for (int level = 1; level < levelCount; level++)
1347 {
1348 if (!isFaceLevelComplete(face, level))
1349 {
1350 return false;
1351 }
1352 }
1353 }
1354
1355 return true;
1356}
1357
1358bool TextureD3D_Cube::isValidFaceLevel(int faceIndex, int level) const
1359{
1360 return (mTexStorage ? (level >= 0 && level < mTexStorage->getLevelCount()) : 0);
1361}
1362
1363
1364bool TextureD3D_Cube::isFaceLevelComplete(int faceIndex, int level) const
1365{
1366 ASSERT(level >= 0 && faceIndex < 6 && level < (int)ArraySize(mImageArray[faceIndex]) && mImageArray[faceIndex][level] != NULL);
1367
1368 if (isImmutable())
1369 {
1370 return true;
1371 }
1372
1373 int baseSize = getBaseLevelWidth();
1374
1375 if (baseSize <= 0)
1376 {
1377 return false;
1378 }
1379
1380 // "isCubeComplete" checks for base level completeness and we must call that
1381 // to determine if any face at level 0 is complete. We omit that check here
1382 // to avoid re-checking cube-completeness for every face at level 0.
1383 if (level == 0)
1384 {
1385 return true;
1386 }
1387
1388 // Check that non-zero levels are consistent with the base level.
1389 const ImageD3D *faceLevelImage = mImageArray[faceIndex][level];
1390
1391 if (faceLevelImage->getInternalFormat() != getBaseLevelInternalFormat())
1392 {
1393 return false;
1394 }
1395
1396 if (faceLevelImage->getWidth() != std::max(1, baseSize >> level))
1397 {
1398 return false;
1399 }
1400
1401 return true;
1402}
1403
1404void TextureD3D_Cube::updateStorageFaceLevel(int faceIndex, int level)
1405{
1406 ASSERT(level >= 0 && faceIndex < 6 && level < (int)ArraySize(mImageArray[faceIndex]) && mImageArray[faceIndex][level] != NULL);
1407 ImageD3D *image = mImageArray[faceIndex][level];
1408
1409 if (image->isDirty())
1410 {
1411 commitRect(faceIndex, level, 0, 0, image->getWidth(), image->getHeight());
1412 }
1413}
1414
1415void TextureD3D_Cube::redefineImage(int faceIndex, GLint level, GLenum internalformat, GLsizei width, GLsizei height)
1416{
1417 // If there currently is a corresponding storage texture image, it has these parameters
1418 const int storageWidth = std::max(1, getBaseLevelWidth() >> level);
1419 const int storageHeight = std::max(1, getBaseLevelHeight() >> level);
1420 const GLenum storageFormat = getBaseLevelInternalFormat();
1421
1422 mImageArray[faceIndex][level]->redefine(mRenderer, GL_TEXTURE_CUBE_MAP, internalformat, width, height, 1, false);
1423
1424 if (mTexStorage)
1425 {
1426 const int storageLevels = mTexStorage->getLevelCount();
1427
1428 if ((level >= storageLevels && storageLevels != 0) ||
1429 width != storageWidth ||
1430 height != storageHeight ||
1431 internalformat != storageFormat) // Discard mismatched storage
1432 {
1433 for (int level = 0; level < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
1434 {
1435 for (int faceIndex = 0; faceIndex < 6; faceIndex++)
1436 {
1437 mImageArray[faceIndex][level]->markDirty();
1438 }
1439 }
1440
1441 SafeDelete(mTexStorage);
1442
1443 mDirtyImages = true;
1444 }
1445 }
1446}
1447
1448void TextureD3D_Cube::commitRect(int faceIndex, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
1449{
1450 if (isValidFaceLevel(faceIndex, level))
1451 {
1452 ImageD3D *image = mImageArray[faceIndex][level];
1453 if (image->copyToStorage(mTexStorage, faceIndex, level, xoffset, yoffset, width, height))
1454 image->markClean();
1455 }
1456}
1457
Brandon Jones78b1acd2014-07-15 15:33:07 -07001458
1459TextureD3D_3D::TextureD3D_3D(Renderer *renderer)
1460 : Texture3DImpl(),
1461 TextureD3D(renderer),
1462 mTexStorage(NULL)
1463{
1464 for (int i = 0; i < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS; ++i)
1465 {
1466 mImageArray[i] = ImageD3D::makeImageD3D(renderer->createImage());
1467 }
1468}
1469
1470TextureD3D_3D::~TextureD3D_3D()
1471{
1472 SafeDelete(mTexStorage);
1473
1474 for (int i = 0; i < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS; ++i)
1475 {
1476 delete mImageArray[i];
1477 }
1478}
1479
1480TextureD3D_3D *TextureD3D_3D::makeTextureD3D_3D(Texture3DImpl *texture)
1481{
1482 ASSERT(HAS_DYNAMIC_TYPE(TextureD3D_3D*, texture));
1483 return static_cast<TextureD3D_3D*>(texture);
1484}
1485
1486TextureStorageInterface *TextureD3D_3D::getNativeTexture()
1487{
1488 // ensure the underlying texture is created
1489 initializeStorage(false);
1490
1491 TextureStorageInterface *storage = getBaseLevelStorage();
1492 if (storage)
1493 {
1494 updateStorage();
1495 }
1496
1497 return storage;
1498}
1499
Brandon Jonescef06ff2014-08-05 13:27:48 -07001500Image *TextureD3D_3D::getImage(int level, int layer) const
Brandon Jones78b1acd2014-07-15 15:33:07 -07001501{
1502 ASSERT(level < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS);
Brandon Jonescef06ff2014-08-05 13:27:48 -07001503 ASSERT(layer == 0);
Brandon Jones78b1acd2014-07-15 15:33:07 -07001504 return mImageArray[level];
1505}
1506
Brandon Jonescef06ff2014-08-05 13:27:48 -07001507GLsizei TextureD3D_3D::getLayerCount(int level) const
Brandon Jones78b1acd2014-07-15 15:33:07 -07001508{
Brandon Jonescef06ff2014-08-05 13:27:48 -07001509 ASSERT(level < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS);
1510 return 1;
Brandon Jones78b1acd2014-07-15 15:33:07 -07001511}
1512
Brandon Jones78b1acd2014-07-15 15:33:07 -07001513GLsizei TextureD3D_3D::getWidth(GLint level) const
1514{
1515 if (level < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS)
1516 return mImageArray[level]->getWidth();
1517 else
1518 return 0;
1519}
1520
1521GLsizei TextureD3D_3D::getHeight(GLint level) const
1522{
1523 if (level < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS)
1524 return mImageArray[level]->getHeight();
1525 else
1526 return 0;
1527}
1528
1529GLsizei TextureD3D_3D::getDepth(GLint level) const
1530{
1531 if (level < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS)
1532 return mImageArray[level]->getDepth();
1533 else
1534 return 0;
1535}
1536
1537GLenum TextureD3D_3D::getInternalFormat(GLint level) const
1538{
1539 if (level < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS)
1540 return mImageArray[level]->getInternalFormat();
1541 else
1542 return GL_NONE;
1543}
1544
1545bool TextureD3D_3D::isDepth(GLint level) const
1546{
Geoff Lang5d601382014-07-22 15:14:06 -04001547 return gl::GetInternalFormatInfo(getInternalFormat(level)).depthBits > 0;
Brandon Jones78b1acd2014-07-15 15:33:07 -07001548}
1549
Brandon Jonescef06ff2014-08-05 13:27:48 -07001550void TextureD3D_3D::setImage(GLenum target, GLint level, GLsizei width, GLsizei height, GLsizei depth, GLenum internalFormat, GLenum format, GLenum type, const gl::PixelUnpackState &unpack, const void *pixels)
Brandon Jones78b1acd2014-07-15 15:33:07 -07001551{
Brandon Jonescef06ff2014-08-05 13:27:48 -07001552 ASSERT(target == GL_TEXTURE_3D);
Geoff Lang5d601382014-07-22 15:14:06 -04001553 GLenum sizedInternalFormat = gl::GetSizedInternalFormat(internalFormat, type);
1554
Brandon Jones78b1acd2014-07-15 15:33:07 -07001555 redefineImage(level, sizedInternalFormat, width, height, depth);
1556
1557 bool fastUnpacked = false;
1558
1559 // Attempt a fast gpu copy of the pixel data to the surface if the app bound an unpack buffer
1560 if (isFastUnpackable(unpack, sizedInternalFormat))
1561 {
1562 // Will try to create RT storage if it does not exist
1563 RenderTarget *destRenderTarget = getRenderTarget(level);
1564 gl::Box destArea(0, 0, 0, getWidth(level), getHeight(level), getDepth(level));
1565
1566 if (destRenderTarget && fastUnpackPixels(unpack, pixels, destArea, sizedInternalFormat, type, destRenderTarget))
1567 {
1568 // Ensure we don't overwrite our newly initialized data
1569 mImageArray[level]->markClean();
1570
1571 fastUnpacked = true;
1572 }
1573 }
1574
1575 if (!fastUnpacked)
1576 {
1577 TextureD3D::setImage(unpack, type, pixels, mImageArray[level]);
1578 }
1579}
1580
Brandon Jonescef06ff2014-08-05 13:27:48 -07001581void TextureD3D_3D::setCompressedImage(GLenum target, GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei depth, GLsizei imageSize, const void *pixels)
Brandon Jones78b1acd2014-07-15 15:33:07 -07001582{
Brandon Jonescef06ff2014-08-05 13:27:48 -07001583 ASSERT(target == GL_TEXTURE_3D);
1584
Brandon Jones78b1acd2014-07-15 15:33:07 -07001585 // compressed formats don't have separate sized internal formats-- we can just use the compressed format directly
1586 redefineImage(level, format, width, height, depth);
1587
1588 TextureD3D::setCompressedImage(imageSize, pixels, mImageArray[level]);
1589}
1590
Brandon Jonescef06ff2014-08-05 13:27:48 -07001591void TextureD3D_3D::subImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const gl::PixelUnpackState &unpack, const void *pixels)
Brandon Jones78b1acd2014-07-15 15:33:07 -07001592{
Brandon Jonescef06ff2014-08-05 13:27:48 -07001593 ASSERT(target == GL_TEXTURE_3D);
1594
Brandon Jones78b1acd2014-07-15 15:33:07 -07001595 bool fastUnpacked = false;
1596
1597 // Attempt a fast gpu copy of the pixel data to the surface if the app bound an unpack buffer
1598 if (isFastUnpackable(unpack, getInternalFormat(level)))
1599 {
1600 RenderTarget *destRenderTarget = getRenderTarget(level);
1601 gl::Box destArea(xoffset, yoffset, zoffset, width, height, depth);
1602
1603 if (destRenderTarget && fastUnpackPixels(unpack, pixels, destArea, getInternalFormat(level), type, destRenderTarget))
1604 {
1605 // Ensure we don't overwrite our newly initialized data
1606 mImageArray[level]->markClean();
1607
1608 fastUnpacked = true;
1609 }
1610 }
1611
1612 if (!fastUnpacked && TextureD3D::subImage(xoffset, yoffset, zoffset, width, height, depth, format, type, unpack, pixels, mImageArray[level]))
1613 {
1614 commitRect(level, xoffset, yoffset, zoffset, width, height, depth);
1615 }
1616}
1617
Brandon Jonescef06ff2014-08-05 13:27:48 -07001618void TextureD3D_3D::subImageCompressed(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *pixels)
Brandon Jones78b1acd2014-07-15 15:33:07 -07001619{
Brandon Jonescef06ff2014-08-05 13:27:48 -07001620 ASSERT(target == GL_TEXTURE_3D);
1621
Brandon Jones78b1acd2014-07-15 15:33:07 -07001622 if (TextureD3D::subImageCompressed(xoffset, yoffset, zoffset, width, height, depth, format, imageSize, pixels, mImageArray[level]))
1623 {
1624 commitRect(level, xoffset, yoffset, zoffset, width, height, depth);
1625 }
1626}
1627
Brandon Jonescef06ff2014-08-05 13:27:48 -07001628void TextureD3D_3D::copyImage(GLenum target, GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, gl::Framebuffer *source)
1629{
1630 UNIMPLEMENTED();
1631}
1632
Brandon Jones78b1acd2014-07-15 15:33:07 -07001633void TextureD3D_3D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, gl::Framebuffer *source)
1634{
Brandon Jonescef06ff2014-08-05 13:27:48 -07001635 ASSERT(target == GL_TEXTURE_3D);
1636
Brandon Jones78b1acd2014-07-15 15:33:07 -07001637 // can only make our texture storage to a render target if level 0 is defined (with a width & height) and
1638 // the current level we're copying to is defined (with appropriate format, width & height)
1639 bool canCreateRenderTarget = isLevelComplete(level) && isLevelComplete(0);
1640
1641 if (!mImageArray[level]->isRenderableFormat() || (!mTexStorage && !canCreateRenderTarget))
1642 {
1643 mImageArray[level]->copy(xoffset, yoffset, zoffset, x, y, width, height, source);
1644 mDirtyImages = true;
1645 }
1646 else
1647 {
1648 ensureRenderTarget();
1649
1650 if (isValidLevel(level))
1651 {
1652 updateStorageLevel(level);
1653
1654 gl::Rectangle sourceRect;
1655 sourceRect.x = x;
1656 sourceRect.width = width;
1657 sourceRect.y = y;
1658 sourceRect.height = height;
1659
1660 mRenderer->copyImage(source, sourceRect,
Geoff Lang5d601382014-07-22 15:14:06 -04001661 gl::GetInternalFormatInfo(getBaseLevelInternalFormat()).format,
Brandon Jones78b1acd2014-07-15 15:33:07 -07001662 xoffset, yoffset, zoffset, mTexStorage, level);
1663 }
1664 }
1665}
1666
Brandon Jonescef06ff2014-08-05 13:27:48 -07001667void TextureD3D_3D::storage(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth)
Brandon Jones78b1acd2014-07-15 15:33:07 -07001668{
Brandon Jonescef06ff2014-08-05 13:27:48 -07001669 ASSERT(target == GL_TEXTURE_3D);
1670
Brandon Jones78b1acd2014-07-15 15:33:07 -07001671 for (int level = 0; level < levels; level++)
1672 {
1673 GLsizei levelWidth = std::max(1, width >> level);
1674 GLsizei levelHeight = std::max(1, height >> level);
1675 GLsizei levelDepth = std::max(1, depth >> level);
1676 mImageArray[level]->redefine(mRenderer, GL_TEXTURE_3D, internalformat, levelWidth, levelHeight, levelDepth, true);
1677 }
1678
1679 for (int level = levels; level < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
1680 {
1681 mImageArray[level]->redefine(mRenderer, GL_TEXTURE_3D, GL_NONE, 0, 0, 0, true);
1682 }
1683
1684 mImmutable = true;
1685
1686 setCompleteTexStorage(new TextureStorageInterface3D(mRenderer, internalformat, IsRenderTargetUsage(mUsage), width, height, depth, levels));
1687}
1688
1689bool TextureD3D_3D::isSamplerComplete(const gl::SamplerState &samplerState) const
1690{
1691 GLsizei width = getBaseLevelWidth();
1692 GLsizei height = getBaseLevelHeight();
1693 GLsizei depth = getBaseLevelDepth();
1694
1695 if (width <= 0 || height <= 0 || depth <= 0)
1696 {
1697 return false;
1698 }
1699
1700 // TODO(geofflang): use context's texture caps
Geoff Lang6cf8e1b2014-07-03 13:03:57 -04001701 if (!mRenderer->getRendererTextureCaps().get(getInternalFormat(0)).filterable)
Brandon Jones78b1acd2014-07-15 15:33:07 -07001702 {
1703 if (samplerState.magFilter != GL_NEAREST ||
1704 (samplerState.minFilter != GL_NEAREST && samplerState.minFilter != GL_NEAREST_MIPMAP_NEAREST))
1705 {
1706 return false;
1707 }
1708 }
1709
1710 if (IsMipmapFiltered(samplerState) && !isMipmapComplete())
1711 {
1712 return false;
1713 }
1714
1715 return true;
1716}
1717
1718bool TextureD3D_3D::isMipmapComplete() const
1719{
1720 int levelCount = mipLevels();
1721
1722 for (int level = 0; level < levelCount; level++)
1723 {
1724 if (!isLevelComplete(level))
1725 {
1726 return false;
1727 }
1728 }
1729
1730 return true;
1731}
1732
1733void TextureD3D_3D::generateMipmaps()
1734{
1735 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
1736 int levelCount = mipLevels();
1737 for (int level = 1; level < levelCount; level++)
1738 {
1739 redefineImage(level, getBaseLevelInternalFormat(),
1740 std::max(getBaseLevelWidth() >> level, 1),
1741 std::max(getBaseLevelHeight() >> level, 1),
1742 std::max(getBaseLevelDepth() >> level, 1));
1743 }
1744
1745 if (mTexStorage && mTexStorage->isRenderTarget())
1746 {
1747 for (int level = 1; level < levelCount; level++)
1748 {
1749 mTexStorage->generateMipmap(level);
1750
1751 mImageArray[level]->markClean();
1752 }
1753 }
1754 else
1755 {
1756 for (int level = 1; level < levelCount; level++)
1757 {
1758 mRenderer->generateMipmap(mImageArray[level], mImageArray[level - 1]);
1759 }
1760 }
1761}
1762
1763unsigned int TextureD3D_3D::getRenderTargetSerial(GLint level, GLint layer)
1764{
1765 return (ensureRenderTarget() ? mTexStorage->getRenderTargetSerial(level, layer) : 0);
1766}
1767
1768RenderTarget *TextureD3D_3D::getRenderTarget(GLint level)
1769{
1770 // ensure the underlying texture is created
1771 if (!ensureRenderTarget())
1772 {
1773 return NULL;
1774 }
1775
1776 updateStorageLevel(level);
1777
1778 // ensure this is NOT a depth texture
1779 if (isDepth(level))
1780 {
1781 return NULL;
1782 }
1783
1784 return mTexStorage->getRenderTarget(level);
1785}
1786
1787RenderTarget *TextureD3D_3D::getRenderTarget(GLint level, GLint layer)
1788{
1789 // ensure the underlying texture is created
1790 if (!ensureRenderTarget())
1791 {
1792 return NULL;
1793 }
1794
1795 updateStorage();
1796
1797 // ensure this is NOT a depth texture
1798 if (isDepth(level))
1799 {
1800 return NULL;
1801 }
1802
1803 return mTexStorage->getRenderTarget(level, layer);
1804}
1805
1806RenderTarget *TextureD3D_3D::getDepthStencil(GLint level, GLint layer)
1807{
1808 // ensure the underlying texture is created
1809 if (!ensureRenderTarget())
1810 {
1811 return NULL;
1812 }
1813
1814 updateStorageLevel(level);
1815
1816 // ensure this is a depth texture
1817 if (!isDepth(level))
1818 {
1819 return NULL;
1820 }
1821
1822 return mTexStorage->getRenderTarget(level, layer);
1823}
1824
1825void TextureD3D_3D::initializeStorage(bool renderTarget)
1826{
1827 // Only initialize the first time this texture is used as a render target or shader resource
1828 if (mTexStorage)
1829 {
1830 return;
1831 }
1832
1833 // do not attempt to create storage for nonexistant data
1834 if (!isLevelComplete(0))
1835 {
1836 return;
1837 }
1838
1839 bool createRenderTarget = (renderTarget || mUsage == GL_FRAMEBUFFER_ATTACHMENT_ANGLE);
1840
1841 setCompleteTexStorage(createCompleteStorage(createRenderTarget));
1842 ASSERT(mTexStorage);
1843
1844 // flush image data to the storage
1845 updateStorage();
1846}
1847
1848TextureStorageInterface3D *TextureD3D_3D::createCompleteStorage(bool renderTarget) const
1849{
1850 GLsizei width = getBaseLevelWidth();
1851 GLsizei height = getBaseLevelHeight();
1852 GLsizei depth = getBaseLevelDepth();
1853
1854 ASSERT(width > 0 && height > 0 && depth > 0);
1855
1856 // use existing storage level count, when previously specified by TexStorage*D
1857 GLint levels = (mTexStorage ? mTexStorage->getLevelCount() : creationLevels(width, height, depth));
1858
1859 return new TextureStorageInterface3D(mRenderer, getBaseLevelInternalFormat(), renderTarget, width, height, depth, levels);
1860}
1861
1862void TextureD3D_3D::setCompleteTexStorage(TextureStorageInterface3D *newCompleteTexStorage)
1863{
1864 SafeDelete(mTexStorage);
1865 mTexStorage = newCompleteTexStorage;
1866 mDirtyImages = true;
1867
1868 // We do not support managed 3D storage, as that is D3D9/ES2-only
1869 ASSERT(!mTexStorage->isManaged());
1870}
1871
1872void TextureD3D_3D::updateStorage()
1873{
1874 ASSERT(mTexStorage != NULL);
1875 GLint storageLevels = mTexStorage->getLevelCount();
1876 for (int level = 0; level < storageLevels; level++)
1877 {
1878 if (mImageArray[level]->isDirty() && isLevelComplete(level))
1879 {
1880 updateStorageLevel(level);
1881 }
1882 }
1883}
1884
1885bool TextureD3D_3D::ensureRenderTarget()
1886{
1887 initializeStorage(true);
1888
1889 if (getBaseLevelWidth() > 0 && getBaseLevelHeight() > 0 && getBaseLevelDepth() > 0)
1890 {
1891 ASSERT(mTexStorage);
1892 if (!mTexStorage->isRenderTarget())
1893 {
1894 TextureStorageInterface3D *newRenderTargetStorage = createCompleteStorage(true);
1895
1896 if (!mRenderer->copyToRenderTarget(newRenderTargetStorage, mTexStorage))
1897 {
1898 delete newRenderTargetStorage;
1899 return gl::error(GL_OUT_OF_MEMORY, false);
1900 }
1901
1902 setCompleteTexStorage(newRenderTargetStorage);
1903 }
1904 }
1905
1906 return (mTexStorage && mTexStorage->isRenderTarget());
1907}
1908
1909TextureStorageInterface *TextureD3D_3D::getBaseLevelStorage()
1910{
1911 return mTexStorage;
1912}
1913
1914const ImageD3D *TextureD3D_3D::getBaseLevelImage() const
1915{
1916 return mImageArray[0];
1917}
1918
1919bool TextureD3D_3D::isValidLevel(int level) const
1920{
1921 return (mTexStorage ? (level >= 0 && level < mTexStorage->getLevelCount()) : 0);
1922}
1923
1924bool TextureD3D_3D::isLevelComplete(int level) const
1925{
1926 ASSERT(level >= 0 && level < (int)ArraySize(mImageArray) && mImageArray[level] != NULL);
1927
1928 if (isImmutable())
1929 {
1930 return true;
1931 }
1932
1933 GLsizei width = getBaseLevelWidth();
1934 GLsizei height = getBaseLevelHeight();
1935 GLsizei depth = getBaseLevelDepth();
1936
1937 if (width <= 0 || height <= 0 || depth <= 0)
1938 {
1939 return false;
1940 }
1941
1942 if (level == 0)
1943 {
1944 return true;
1945 }
1946
1947 ImageD3D *levelImage = mImageArray[level];
1948
1949 if (levelImage->getInternalFormat() != getBaseLevelInternalFormat())
1950 {
1951 return false;
1952 }
1953
1954 if (levelImage->getWidth() != std::max(1, width >> level))
1955 {
1956 return false;
1957 }
1958
1959 if (levelImage->getHeight() != std::max(1, height >> level))
1960 {
1961 return false;
1962 }
1963
1964 if (levelImage->getDepth() != std::max(1, depth >> level))
1965 {
1966 return false;
1967 }
1968
1969 return true;
1970}
1971
1972void TextureD3D_3D::updateStorageLevel(int level)
1973{
1974 ASSERT(level >= 0 && level < (int)ArraySize(mImageArray) && mImageArray[level] != NULL);
1975 ASSERT(isLevelComplete(level));
1976
1977 if (mImageArray[level]->isDirty())
1978 {
1979 commitRect(level, 0, 0, 0, getWidth(level), getHeight(level), getDepth(level));
1980 }
1981}
1982
1983void TextureD3D_3D::redefineImage(GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth)
1984{
1985 // If there currently is a corresponding storage texture image, it has these parameters
1986 const int storageWidth = std::max(1, getBaseLevelWidth() >> level);
1987 const int storageHeight = std::max(1, getBaseLevelHeight() >> level);
1988 const int storageDepth = std::max(1, getBaseLevelDepth() >> level);
1989 const GLenum storageFormat = getBaseLevelInternalFormat();
1990
1991 mImageArray[level]->redefine(mRenderer, GL_TEXTURE_3D, internalformat, width, height, depth, false);
1992
1993 if (mTexStorage)
1994 {
1995 const int storageLevels = mTexStorage->getLevelCount();
1996
1997 if ((level >= storageLevels && storageLevels != 0) ||
1998 width != storageWidth ||
1999 height != storageHeight ||
2000 depth != storageDepth ||
2001 internalformat != storageFormat) // Discard mismatched storage
2002 {
2003 for (int i = 0; i < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
2004 {
2005 mImageArray[i]->markDirty();
2006 }
2007
2008 SafeDelete(mTexStorage);
2009 mDirtyImages = true;
2010 }
2011 }
2012}
2013
2014void TextureD3D_3D::commitRect(GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth)
2015{
2016 if (isValidLevel(level))
2017 {
2018 ImageD3D *image = mImageArray[level];
2019 if (image->copyToStorage(mTexStorage, level, xoffset, yoffset, zoffset, width, height, depth))
2020 {
2021 image->markClean();
2022 }
2023 }
2024}
2025
Brandon Jones142ec422014-07-16 10:31:30 -07002026
2027TextureD3D_2DArray::TextureD3D_2DArray(Renderer *renderer)
2028 : Texture2DArrayImpl(),
2029 TextureD3D(renderer),
2030 mTexStorage(NULL)
2031{
2032 for (int level = 0; level < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS; ++level)
2033 {
2034 mLayerCounts[level] = 0;
2035 mImageArray[level] = NULL;
2036 }
2037}
2038
2039TextureD3D_2DArray::~TextureD3D_2DArray()
2040{
2041 SafeDelete(mTexStorage);
2042
2043 deleteImages();
2044}
2045
2046TextureD3D_2DArray *TextureD3D_2DArray::makeTextureD3D_2DArray(Texture2DArrayImpl *texture)
2047{
2048 ASSERT(HAS_DYNAMIC_TYPE(TextureD3D_2DArray*, texture));
2049 return static_cast<TextureD3D_2DArray*>(texture);
2050}
2051
2052TextureStorageInterface *TextureD3D_2DArray::getNativeTexture()
2053{
2054 // ensure the underlying texture is created
2055 initializeStorage(false);
2056
2057 TextureStorageInterface *storage = getBaseLevelStorage();
2058 if (storage)
2059 {
2060 updateStorage();
2061 }
2062
2063 return storage;
2064}
2065
2066Image *TextureD3D_2DArray::getImage(int level, int layer) const
2067{
2068 ASSERT(level < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS);
2069 ASSERT(layer < mLayerCounts[level]);
2070 return mImageArray[level][layer];
2071}
2072
2073GLsizei TextureD3D_2DArray::getLayerCount(int level) const
2074{
2075 ASSERT(level < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS);
2076 return mLayerCounts[level];
2077}
2078
Brandon Jones142ec422014-07-16 10:31:30 -07002079GLsizei TextureD3D_2DArray::getWidth(GLint level) const
2080{
2081 return (level < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS && mLayerCounts[level] > 0) ? mImageArray[level][0]->getWidth() : 0;
2082}
2083
2084GLsizei TextureD3D_2DArray::getHeight(GLint level) const
2085{
2086 return (level < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS && mLayerCounts[level] > 0) ? mImageArray[level][0]->getHeight() : 0;
2087}
2088
2089GLsizei TextureD3D_2DArray::getLayers(GLint level) const
2090{
2091 return (level < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS) ? mLayerCounts[level] : 0;
2092}
2093
2094GLenum TextureD3D_2DArray::getInternalFormat(GLint level) const
2095{
2096 return (level < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS && mLayerCounts[level] > 0) ? mImageArray[level][0]->getInternalFormat() : GL_NONE;
2097}
2098
2099bool TextureD3D_2DArray::isDepth(GLint level) const
2100{
Geoff Lang5d601382014-07-22 15:14:06 -04002101 return gl::GetInternalFormatInfo(getInternalFormat(level)).depthBits > 0;
Brandon Jones142ec422014-07-16 10:31:30 -07002102}
2103
Brandon Jonescef06ff2014-08-05 13:27:48 -07002104void TextureD3D_2DArray::setImage(GLenum target, GLint level, GLsizei width, GLsizei height, GLsizei depth, GLenum internalFormat, GLenum format, GLenum type, const gl::PixelUnpackState &unpack, const void *pixels)
Brandon Jones142ec422014-07-16 10:31:30 -07002105{
Brandon Jonescef06ff2014-08-05 13:27:48 -07002106 ASSERT(target == GL_TEXTURE_2D_ARRAY);
2107
Geoff Lang5d601382014-07-22 15:14:06 -04002108 GLenum sizedInternalFormat = gl::GetSizedInternalFormat(internalFormat, type);
2109
Brandon Jones142ec422014-07-16 10:31:30 -07002110 redefineImage(level, sizedInternalFormat, width, height, depth);
2111
Geoff Lang5d601382014-07-22 15:14:06 -04002112 const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(sizedInternalFormat);
2113 GLsizei inputDepthPitch = formatInfo.computeDepthPitch(type, width, height, unpack.alignment);
Brandon Jones142ec422014-07-16 10:31:30 -07002114
2115 for (int i = 0; i < depth; i++)
2116 {
2117 const void *layerPixels = pixels ? (reinterpret_cast<const unsigned char*>(pixels) + (inputDepthPitch * i)) : NULL;
2118 TextureD3D::setImage(unpack, type, layerPixels, mImageArray[level][i]);
2119 }
2120}
2121
Brandon Jonescef06ff2014-08-05 13:27:48 -07002122void TextureD3D_2DArray::setCompressedImage(GLenum target, GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei depth, GLsizei imageSize, const void *pixels)
Brandon Jones142ec422014-07-16 10:31:30 -07002123{
Brandon Jonescef06ff2014-08-05 13:27:48 -07002124 ASSERT(target == GL_TEXTURE_2D_ARRAY);
2125
Brandon Jones142ec422014-07-16 10:31:30 -07002126 // compressed formats don't have separate sized internal formats-- we can just use the compressed format directly
2127 redefineImage(level, format, width, height, depth);
2128
Geoff Lang5d601382014-07-22 15:14:06 -04002129 const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(format);
2130 GLsizei inputDepthPitch = formatInfo.computeDepthPitch(GL_UNSIGNED_BYTE, width, height, 1);
Brandon Jones142ec422014-07-16 10:31:30 -07002131
2132 for (int i = 0; i < depth; i++)
2133 {
2134 const void *layerPixels = pixels ? (reinterpret_cast<const unsigned char*>(pixels) + (inputDepthPitch * i)) : NULL;
2135 TextureD3D::setCompressedImage(imageSize, layerPixels, mImageArray[level][i]);
2136 }
2137}
2138
Brandon Jonescef06ff2014-08-05 13:27:48 -07002139void TextureD3D_2DArray::subImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const gl::PixelUnpackState &unpack, const void *pixels)
Brandon Jones142ec422014-07-16 10:31:30 -07002140{
Brandon Jonescef06ff2014-08-05 13:27:48 -07002141 ASSERT(target == GL_TEXTURE_2D_ARRAY);
2142
Geoff Lang5d601382014-07-22 15:14:06 -04002143 const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(getInternalFormat(level));
2144 GLsizei inputDepthPitch = formatInfo.computeDepthPitch(type, width, height, unpack.alignment);
Brandon Jones142ec422014-07-16 10:31:30 -07002145
2146 for (int i = 0; i < depth; i++)
2147 {
2148 int layer = zoffset + i;
2149 const void *layerPixels = pixels ? (reinterpret_cast<const unsigned char*>(pixels) + (inputDepthPitch * i)) : NULL;
2150
2151 if (TextureD3D::subImage(xoffset, yoffset, zoffset, width, height, 1, format, type, unpack, layerPixels, mImageArray[level][layer]))
2152 {
2153 commitRect(level, xoffset, yoffset, layer, width, height);
2154 }
2155 }
2156}
2157
Brandon Jonescef06ff2014-08-05 13:27:48 -07002158void TextureD3D_2DArray::subImageCompressed(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *pixels)
Brandon Jones142ec422014-07-16 10:31:30 -07002159{
Brandon Jonescef06ff2014-08-05 13:27:48 -07002160 ASSERT(target == GL_TEXTURE_2D_ARRAY);
2161
Geoff Lang5d601382014-07-22 15:14:06 -04002162 const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(format);
2163 GLsizei inputDepthPitch = formatInfo.computeDepthPitch(GL_UNSIGNED_BYTE, width, height, 1);
Brandon Jones142ec422014-07-16 10:31:30 -07002164
2165 for (int i = 0; i < depth; i++)
2166 {
2167 int layer = zoffset + i;
2168 const void *layerPixels = pixels ? (reinterpret_cast<const unsigned char*>(pixels) + (inputDepthPitch * i)) : NULL;
2169
2170 if (TextureD3D::subImageCompressed(xoffset, yoffset, zoffset, width, height, 1, format, imageSize, layerPixels, mImageArray[level][layer]))
2171 {
2172 commitRect(level, xoffset, yoffset, layer, width, height);
2173 }
2174 }
2175}
2176
Brandon Jonescef06ff2014-08-05 13:27:48 -07002177void TextureD3D_2DArray::copyImage(GLenum target, GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, gl::Framebuffer *source)
2178{
2179 UNIMPLEMENTED();
2180}
2181
Brandon Jones142ec422014-07-16 10:31:30 -07002182void TextureD3D_2DArray::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, gl::Framebuffer *source)
2183{
Brandon Jonescef06ff2014-08-05 13:27:48 -07002184 ASSERT(target == GL_TEXTURE_2D_ARRAY);
2185
Brandon Jones142ec422014-07-16 10:31:30 -07002186 // can only make our texture storage to a render target if level 0 is defined (with a width & height) and
2187 // the current level we're copying to is defined (with appropriate format, width & height)
2188 bool canCreateRenderTarget = isLevelComplete(level) && isLevelComplete(0);
2189
2190 if (!mImageArray[level][0]->isRenderableFormat() || (!mTexStorage && !canCreateRenderTarget))
2191 {
2192 mImageArray[level][zoffset]->copy(xoffset, yoffset, 0, x, y, width, height, source);
2193 mDirtyImages = true;
2194 }
2195 else
2196 {
2197 ensureRenderTarget();
2198
2199 if (isValidLevel(level))
2200 {
2201 updateStorageLevel(level);
2202
2203 gl::Rectangle sourceRect;
2204 sourceRect.x = x;
2205 sourceRect.width = width;
2206 sourceRect.y = y;
2207 sourceRect.height = height;
2208
Geoff Lang5d601382014-07-22 15:14:06 -04002209 mRenderer->copyImage(source, sourceRect, gl::GetInternalFormatInfo(getInternalFormat(0)).format,
Brandon Jones142ec422014-07-16 10:31:30 -07002210 xoffset, yoffset, zoffset, mTexStorage, level);
2211 }
2212 }
2213}
2214
Brandon Jonescef06ff2014-08-05 13:27:48 -07002215void TextureD3D_2DArray::storage(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth)
Brandon Jones142ec422014-07-16 10:31:30 -07002216{
Brandon Jonescef06ff2014-08-05 13:27:48 -07002217 ASSERT(target == GL_TEXTURE_2D_ARRAY);
2218
Brandon Jones142ec422014-07-16 10:31:30 -07002219 deleteImages();
2220
2221 for (int level = 0; level < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
2222 {
2223 GLsizei levelWidth = std::max(1, width >> level);
2224 GLsizei levelHeight = std::max(1, height >> level);
2225
2226 mLayerCounts[level] = (level < levels ? depth : 0);
2227
2228 if (mLayerCounts[level] > 0)
2229 {
2230 // Create new images for this level
2231 mImageArray[level] = new ImageD3D*[mLayerCounts[level]];
2232
2233 for (int layer = 0; layer < mLayerCounts[level]; layer++)
2234 {
2235 mImageArray[level][layer] = ImageD3D::makeImageD3D(mRenderer->createImage());
2236 mImageArray[level][layer]->redefine(mRenderer, GL_TEXTURE_2D_ARRAY, internalformat, levelWidth,
2237 levelHeight, 1, true);
2238 }
2239 }
2240 }
2241
2242 mImmutable = true;
2243 setCompleteTexStorage(new TextureStorageInterface2DArray(mRenderer, internalformat, IsRenderTargetUsage(mUsage), width, height, depth, levels));
2244}
2245
2246bool TextureD3D_2DArray::isSamplerComplete(const gl::SamplerState &samplerState) const
2247{
2248 GLsizei width = getBaseLevelWidth();
2249 GLsizei height = getBaseLevelHeight();
2250 GLsizei depth = getLayers(0);
2251
2252 if (width <= 0 || height <= 0 || depth <= 0)
2253 {
2254 return false;
2255 }
2256
2257 // TODO(geofflang): use context's texture caps
2258 if (!mRenderer->getRendererTextureCaps().get(getBaseLevelInternalFormat()).filterable)
2259 {
2260 if (samplerState.magFilter != GL_NEAREST ||
2261 (samplerState.minFilter != GL_NEAREST && samplerState.minFilter != GL_NEAREST_MIPMAP_NEAREST))
2262 {
2263 return false;
2264 }
2265 }
2266
2267 if (IsMipmapFiltered(samplerState) && !isMipmapComplete())
2268 {
2269 return false;
2270 }
2271
2272 return true;
2273}
2274
2275bool TextureD3D_2DArray::isMipmapComplete() const
2276{
2277 int levelCount = mipLevels();
2278
2279 for (int level = 1; level < levelCount; level++)
2280 {
2281 if (!isLevelComplete(level))
2282 {
2283 return false;
2284 }
2285 }
2286
2287 return true;
2288}
2289
2290void TextureD3D_2DArray::generateMipmaps()
2291{
2292 int baseWidth = getBaseLevelWidth();
2293 int baseHeight = getBaseLevelHeight();
2294 int baseDepth = getBaseLevelDepth();
2295 GLenum baseFormat = getBaseLevelInternalFormat();
2296
2297 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
2298 int levelCount = mipLevels();
2299 for (int level = 1; level < levelCount; level++)
2300 {
2301 redefineImage(level, baseFormat, std::max(baseWidth >> level, 1), std::max(baseHeight >> level, 1), baseDepth);
2302 }
2303
2304 if (mTexStorage && mTexStorage->isRenderTarget())
2305 {
2306 for (int level = 1; level < levelCount; level++)
2307 {
2308 mTexStorage->generateMipmap(level);
2309
2310 for (int layer = 0; layer < mLayerCounts[level]; layer++)
2311 {
2312 mImageArray[level][layer]->markClean();
2313 }
2314 }
2315 }
2316 else
2317 {
2318 for (int level = 1; level < levelCount; level++)
2319 {
2320 for (int layer = 0; layer < mLayerCounts[level]; layer++)
2321 {
2322 mRenderer->generateMipmap(mImageArray[level][layer], mImageArray[level - 1][layer]);
2323 }
2324 }
2325 }
2326}
2327
2328unsigned int TextureD3D_2DArray::getRenderTargetSerial(GLint level, GLint layer)
2329{
2330 return (ensureRenderTarget() ? mTexStorage->getRenderTargetSerial(level, layer) : 0);
2331}
2332
2333RenderTarget *TextureD3D_2DArray::getRenderTarget(GLint level, GLint layer)
2334{
2335 // ensure the underlying texture is created
2336 if (!ensureRenderTarget())
2337 {
2338 return NULL;
2339 }
2340
2341 updateStorageLevel(level);
2342
2343 // ensure this is NOT a depth texture
2344 if (isDepth(level))
2345 {
2346 return NULL;
2347 }
2348
2349 return mTexStorage->getRenderTarget(level, layer);
2350}
2351
2352RenderTarget *TextureD3D_2DArray::getDepthStencil(GLint level, GLint layer)
2353{
2354 // ensure the underlying texture is created
2355 if (!ensureRenderTarget())
2356 {
2357 return NULL;
2358 }
2359
2360 updateStorageLevel(level);
2361
2362 // ensure this is a depth texture
2363 if (!isDepth(level))
2364 {
2365 return NULL;
2366 }
2367
2368 return mTexStorage->getRenderTarget(level, layer);
2369}
2370
2371void TextureD3D_2DArray::initializeStorage(bool renderTarget)
2372{
2373 // Only initialize the first time this texture is used as a render target or shader resource
2374 if (mTexStorage)
2375 {
2376 return;
2377 }
2378
2379 // do not attempt to create storage for nonexistant data
2380 if (!isLevelComplete(0))
2381 {
2382 return;
2383 }
2384
2385 bool createRenderTarget = (renderTarget || mUsage == GL_FRAMEBUFFER_ATTACHMENT_ANGLE);
2386
2387 setCompleteTexStorage(createCompleteStorage(createRenderTarget));
2388 ASSERT(mTexStorage);
2389
2390 // flush image data to the storage
2391 updateStorage();
2392}
2393
2394TextureStorageInterface2DArray *TextureD3D_2DArray::createCompleteStorage(bool renderTarget) const
2395{
2396 GLsizei width = getBaseLevelWidth();
2397 GLsizei height = getBaseLevelHeight();
2398 GLsizei depth = getLayers(0);
2399
2400 ASSERT(width > 0 && height > 0 && depth > 0);
2401
2402 // use existing storage level count, when previously specified by TexStorage*D
2403 GLint levels = (mTexStorage ? mTexStorage->getLevelCount() : creationLevels(width, height, 1));
2404
2405 return new TextureStorageInterface2DArray(mRenderer, getBaseLevelInternalFormat(), renderTarget, width, height, depth, levels);
2406}
2407
2408void TextureD3D_2DArray::setCompleteTexStorage(TextureStorageInterface2DArray *newCompleteTexStorage)
2409{
2410 SafeDelete(mTexStorage);
2411 mTexStorage = newCompleteTexStorage;
2412 mDirtyImages = true;
2413
2414 // We do not support managed 2D array storage, as managed storage is ES2/D3D9 only
2415 ASSERT(!mTexStorage->isManaged());
2416}
2417
2418void TextureD3D_2DArray::updateStorage()
2419{
2420 ASSERT(mTexStorage != NULL);
2421 GLint storageLevels = mTexStorage->getLevelCount();
2422 for (int level = 0; level < storageLevels; level++)
2423 {
2424 if (isLevelComplete(level))
2425 {
2426 updateStorageLevel(level);
2427 }
2428 }
2429}
2430
2431bool TextureD3D_2DArray::ensureRenderTarget()
2432{
2433 initializeStorage(true);
2434
2435 if (getBaseLevelWidth() > 0 && getBaseLevelHeight() > 0 && getLayers(0) > 0)
2436 {
2437 ASSERT(mTexStorage);
2438 if (!mTexStorage->isRenderTarget())
2439 {
2440 TextureStorageInterface2DArray *newRenderTargetStorage = createCompleteStorage(true);
2441
2442 if (!mRenderer->copyToRenderTarget(newRenderTargetStorage, mTexStorage))
2443 {
2444 delete newRenderTargetStorage;
2445 return gl::error(GL_OUT_OF_MEMORY, false);
2446 }
2447
2448 setCompleteTexStorage(newRenderTargetStorage);
2449 }
2450 }
2451
2452 return (mTexStorage && mTexStorage->isRenderTarget());
2453}
2454
2455const ImageD3D *TextureD3D_2DArray::getBaseLevelImage() const
2456{
2457 return (mLayerCounts[0] > 0 ? mImageArray[0][0] : NULL);
2458}
2459
2460TextureStorageInterface *TextureD3D_2DArray::getBaseLevelStorage()
2461{
2462 return mTexStorage;
2463}
2464
2465bool TextureD3D_2DArray::isValidLevel(int level) const
2466{
2467 return (mTexStorage ? (level >= 0 && level < mTexStorage->getLevelCount()) : 0);
2468}
2469
2470bool TextureD3D_2DArray::isLevelComplete(int level) const
2471{
2472 ASSERT(level >= 0 && level < (int)ArraySize(mImageArray));
2473
2474 if (isImmutable())
2475 {
2476 return true;
2477 }
2478
2479 GLsizei width = getBaseLevelWidth();
2480 GLsizei height = getBaseLevelHeight();
2481 GLsizei layers = getLayers(0);
2482
2483 if (width <= 0 || height <= 0 || layers <= 0)
2484 {
2485 return false;
2486 }
2487
2488 if (level == 0)
2489 {
2490 return true;
2491 }
2492
2493 if (getInternalFormat(level) != getInternalFormat(0))
2494 {
2495 return false;
2496 }
2497
2498 if (getWidth(level) != std::max(1, width >> level))
2499 {
2500 return false;
2501 }
2502
2503 if (getHeight(level) != std::max(1, height >> level))
2504 {
2505 return false;
2506 }
2507
2508 if (getLayers(level) != layers)
2509 {
2510 return false;
2511 }
2512
2513 return true;
2514}
2515
2516void TextureD3D_2DArray::updateStorageLevel(int level)
2517{
2518 ASSERT(level >= 0 && level < (int)ArraySize(mLayerCounts));
2519 ASSERT(isLevelComplete(level));
2520
2521 for (int layer = 0; layer < mLayerCounts[level]; layer++)
2522 {
2523 ASSERT(mImageArray[level] != NULL && mImageArray[level][layer] != NULL);
2524 if (mImageArray[level][layer]->isDirty())
2525 {
2526 commitRect(level, 0, 0, layer, getWidth(level), getHeight(level));
2527 }
2528 }
2529}
2530
2531void TextureD3D_2DArray::deleteImages()
2532{
2533 for (int level = 0; level < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS; ++level)
2534 {
2535 for (int layer = 0; layer < mLayerCounts[level]; ++layer)
2536 {
2537 delete mImageArray[level][layer];
2538 }
2539 delete[] mImageArray[level];
2540 mImageArray[level] = NULL;
2541 mLayerCounts[level] = 0;
2542 }
2543}
2544
2545void TextureD3D_2DArray::redefineImage(GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth)
2546{
2547 // If there currently is a corresponding storage texture image, it has these parameters
2548 const int storageWidth = std::max(1, getBaseLevelWidth() >> level);
2549 const int storageHeight = std::max(1, getBaseLevelHeight() >> level);
2550 const int storageDepth = getLayers(0);
2551 const GLenum storageFormat = getBaseLevelInternalFormat();
2552
2553 for (int layer = 0; layer < mLayerCounts[level]; layer++)
2554 {
2555 delete mImageArray[level][layer];
2556 }
2557 delete[] mImageArray[level];
2558 mImageArray[level] = NULL;
2559 mLayerCounts[level] = depth;
2560
2561 if (depth > 0)
2562 {
2563 mImageArray[level] = new ImageD3D*[depth]();
2564
2565 for (int layer = 0; layer < mLayerCounts[level]; layer++)
2566 {
2567 mImageArray[level][layer] = ImageD3D::makeImageD3D(mRenderer->createImage());
2568 mImageArray[level][layer]->redefine(mRenderer, GL_TEXTURE_2D_ARRAY, internalformat, width, height, 1, false);
2569 }
2570 }
2571
2572 if (mTexStorage)
2573 {
2574 const int storageLevels = mTexStorage->getLevelCount();
2575
2576 if ((level >= storageLevels && storageLevels != 0) ||
2577 width != storageWidth ||
2578 height != storageHeight ||
2579 depth != storageDepth ||
2580 internalformat != storageFormat) // Discard mismatched storage
2581 {
2582 for (int level = 0; level < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
2583 {
2584 for (int layer = 0; layer < mLayerCounts[level]; layer++)
2585 {
2586 mImageArray[level][layer]->markDirty();
2587 }
2588 }
2589
2590 delete mTexStorage;
2591 mTexStorage = NULL;
2592 mDirtyImages = true;
2593 }
2594 }
2595}
2596
2597void TextureD3D_2DArray::commitRect(GLint level, GLint xoffset, GLint yoffset, GLint layerTarget, GLsizei width, GLsizei height)
2598{
2599 if (isValidLevel(level) && layerTarget < getLayers(level))
2600 {
2601 ImageD3D *image = mImageArray[level][layerTarget];
2602 if (image->copyToStorage(mTexStorage, level, xoffset, yoffset, layerTarget, width, height))
2603 {
2604 image->markClean();
2605 }
2606 }
2607}
2608
Brandon Jones78b1acd2014-07-15 15:33:07 -07002609}