2 // Copyright (c) 2012 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.
7 // Image11.h: Implements the rx::Image11 class, which acts as the interface to
8 // the actual underlying resources of a Texture
10 #include "libGLESv2/renderer/d3d/d3d11/Renderer11.h"
11 #include "libGLESv2/renderer/d3d/d3d11/Image11.h"
12 #include "libGLESv2/renderer/d3d/d3d11/TextureStorage11.h"
13 #include "libGLESv2/renderer/d3d/d3d11/formatutils11.h"
14 #include "libGLESv2/renderer/d3d/d3d11/renderer11_utils.h"
15 #include "libGLESv2/Framebuffer.h"
16 #include "libGLESv2/FramebufferAttachment.h"
17 #include "libGLESv2/main.h"
19 #include "common/utilities.h"
26 mStagingTexture = NULL;
28 mDXGIFormat = DXGI_FORMAT_UNKNOWN;
29 mRecoverFromStorage = false;
30 mAssociatedStorage = NULL;
31 mAssociatedStorageLevel = 0;
32 mAssociatedStorageLayerTarget = 0;
33 mRecoveredFromStorageCount = 0;
38 disassociateStorage();
39 releaseStagingTexture();
42 Image11 *Image11::makeImage11(Image *img)
44 ASSERT(HAS_DYNAMIC_TYPE(rx::Image11*, img));
45 return static_cast<rx::Image11*>(img);
48 void Image11::generateMipmap(Image11 *dest, Image11 *src)
50 ASSERT(src->getDXGIFormat() == dest->getDXGIFormat());
51 ASSERT(src->getWidth() == 1 || src->getWidth() / 2 == dest->getWidth());
52 ASSERT(src->getHeight() == 1 || src->getHeight() / 2 == dest->getHeight());
54 const d3d11::DXGIFormat &dxgiFormatInfo = d3d11::GetDXGIFormatInfo(src->getDXGIFormat());
55 ASSERT(dxgiFormatInfo.mipGenerationFunction != NULL);
57 D3D11_MAPPED_SUBRESOURCE destMapped;
58 HRESULT destMapResult = dest->map(D3D11_MAP_WRITE, &destMapped);
59 if (FAILED(destMapResult))
61 ERR("Failed to map destination image for mip map generation. HRESULT:0x%X", destMapResult);
65 D3D11_MAPPED_SUBRESOURCE srcMapped;
66 HRESULT srcMapResult = src->map(D3D11_MAP_READ, &srcMapped);
67 if (FAILED(srcMapResult))
69 ERR("Failed to map source image for mip map generation. HRESULT:0x%X", srcMapResult);
75 const uint8_t *sourceData = reinterpret_cast<const uint8_t*>(srcMapped.pData);
76 uint8_t *destData = reinterpret_cast<uint8_t*>(destMapped.pData);
78 dxgiFormatInfo.mipGenerationFunction(src->getWidth(), src->getHeight(), src->getDepth(),
79 sourceData, srcMapped.RowPitch, srcMapped.DepthPitch,
80 destData, destMapped.RowPitch, destMapped.DepthPitch);
88 bool Image11::isDirty() const
91 // AND mStagingTexture doesn't exist AND mStagingTexture doesn't need to be recovered from TextureStorage
92 // AND the texture doesn't require init data (i.e. a blank new texture will suffice)
93 // then isDirty should still return false.
94 if (mDirty && !mStagingTexture && !mRecoverFromStorage && !(d3d11::GetTextureFormatInfo(mInternalFormat).dataInitializerFunction != NULL))
102 bool Image11::copyToStorage2D(TextureStorage *storage, int level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
104 TextureStorage11_2D *storage11 = TextureStorage11_2D::makeTextureStorage11_2D(storage);
105 return copyToStorageImpl(storage11, level, 0, xoffset, yoffset, width, height);
108 bool Image11::copyToStorageCube(TextureStorage *storage, int face, int level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
110 TextureStorage11_Cube *storage11 = TextureStorage11_Cube::makeTextureStorage11_Cube(storage);
111 return copyToStorageImpl(storage11, level, face, xoffset, yoffset, width, height);
114 bool Image11::copyToStorage3D(TextureStorage *storage, int level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth)
116 TextureStorage11_3D *storage11 = TextureStorage11_3D::makeTextureStorage11_3D(storage);
117 return copyToStorageImpl(storage11, level, 0, xoffset, yoffset, width, height);
120 bool Image11::copyToStorage2DArray(TextureStorage *storage, int level, GLint xoffset, GLint yoffset, GLint arrayLayer, GLsizei width, GLsizei height)
122 TextureStorage11_2DArray *storage11 = TextureStorage11_2DArray::makeTextureStorage11_2DArray(storage);
123 return copyToStorageImpl(storage11, level, arrayLayer, xoffset, yoffset, width, height);
126 bool Image11::copyToStorageImpl(TextureStorage11 *storage11, int level, int layerTarget, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
128 // If an app's behavior results in an Image11 copying its data to/from to a TextureStorage multiple times,
129 // then we should just keep the staging texture around to prevent the copying from impacting perf.
130 // We allow the Image11 to copy its data to/from TextureStorage once.
131 // This accounts for an app making a late call to glGenerateMipmap.
132 bool attemptToReleaseStagingTexture = (mRecoveredFromStorageCount < 2);
134 if (attemptToReleaseStagingTexture)
136 // If another image is relying on this Storage for its data, then we must let it recover its data before we overwrite it.
137 storage11->releaseAssociatedImage(level, layerTarget, this);
140 bool updateSubresourceSuccess = storage11->updateSubresourceLevel(getStagingTexture(), getStagingSubresource(), level, layerTarget, xoffset, yoffset, 0, width, height, 1);
142 // Once the image data has been copied into the Storage, we can release it locally.
143 if (attemptToReleaseStagingTexture && updateSubresourceSuccess)
145 storage11->associateImage(this, level, layerTarget);
146 releaseStagingTexture();
147 mRecoverFromStorage = true;
148 mAssociatedStorage = storage11;
149 mAssociatedStorageLevel = level;
150 mAssociatedStorageLayerTarget = layerTarget;
153 return updateSubresourceSuccess;
156 bool Image11::isAssociatedStorageValid(TextureStorage11* textureStorage) const
158 return (mAssociatedStorage == textureStorage);
161 bool Image11::recoverFromAssociatedStorage()
163 if (mRecoverFromStorage)
165 createStagingTexture();
167 bool textureStorageCorrect = mAssociatedStorage->isAssociatedImageValid(mAssociatedStorageLevel, mAssociatedStorageLayerTarget, this);
169 // This means that the cached TextureStorage has been modified after this Image11 released its copy of its data.
170 // This should not have happened. The TextureStorage should have told this Image11 to recover its data before it was overwritten.
171 ASSERT(textureStorageCorrect);
173 if (textureStorageCorrect)
175 // CopySubResource from the Storage to the Staging texture
176 mAssociatedStorage->copySubresourceLevel(mStagingTexture, mStagingSubresource, mAssociatedStorageLevel, mAssociatedStorageLayerTarget, 0, 0, 0, mWidth, mHeight, mDepth);
177 mRecoveredFromStorageCount += 1;
180 // Reset all the recovery parameters, even if the texture storage association is broken.
181 disassociateStorage();
183 return textureStorageCorrect;
189 void Image11::disassociateStorage()
191 if (mRecoverFromStorage)
193 // Make the texturestorage release the Image11 too
194 mAssociatedStorage->disassociateImage(mAssociatedStorageLevel, mAssociatedStorageLayerTarget, this);
196 mRecoverFromStorage = false;
197 mAssociatedStorage = NULL;
198 mAssociatedStorageLevel = 0;
199 mAssociatedStorageLayerTarget = 0;
203 bool Image11::redefine(Renderer *renderer, GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, bool forceRelease)
205 if (mWidth != width ||
207 mInternalFormat != internalformat ||
210 // End the association with the TextureStorage, since that data will be out of date.
211 // Also reset mRecoveredFromStorageCount since this Image is getting completely redefined.
212 disassociateStorage();
213 mRecoveredFromStorageCount = 0;
215 mRenderer = Renderer11::makeRenderer11(renderer);
220 mInternalFormat = internalformat;
223 // compute the d3d format that will be used
224 const d3d11::TextureFormat &formatInfo = d3d11::GetTextureFormatInfo(internalformat);
225 const d3d11::DXGIFormat &dxgiFormatInfo = d3d11::GetDXGIFormatInfo(formatInfo.texFormat);
226 mDXGIFormat = formatInfo.texFormat;
227 mActualFormat = dxgiFormatInfo.internalFormat;
228 mRenderable = (formatInfo.rtvFormat != DXGI_FORMAT_UNKNOWN);
230 SafeRelease(mStagingTexture);
231 mDirty = (formatInfo.dataInitializerFunction != NULL);
239 DXGI_FORMAT Image11::getDXGIFormat() const
241 // this should only happen if the image hasn't been redefined first
242 // which would be a bug by the caller
243 ASSERT(mDXGIFormat != DXGI_FORMAT_UNKNOWN);
248 // Store the pixel rectangle designated by xoffset,yoffset,width,height with pixels stored as format/type at input
249 // into the target pixel rectangle.
250 void Image11::loadData(GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth,
251 GLint unpackAlignment, GLenum type, const void *input)
253 const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(mInternalFormat);
254 GLsizei inputRowPitch = formatInfo.computeRowPitch(type, width, unpackAlignment);
255 GLsizei inputDepthPitch = formatInfo.computeDepthPitch(type, width, height, unpackAlignment);
257 const d3d11::DXGIFormat &dxgiFormatInfo = d3d11::GetDXGIFormatInfo(mDXGIFormat);
258 GLuint outputPixelSize = dxgiFormatInfo.pixelBytes;
260 const d3d11::TextureFormat &d3dFormatInfo = d3d11::GetTextureFormatInfo(mInternalFormat);
261 LoadImageFunction loadFunction = d3dFormatInfo.loadFunctions.at(type);
263 D3D11_MAPPED_SUBRESOURCE mappedImage;
264 HRESULT result = map(D3D11_MAP_WRITE, &mappedImage);
267 ERR("Could not map image for loading.");
271 uint8_t* offsetMappedData = (reinterpret_cast<uint8_t*>(mappedImage.pData) + (yoffset * mappedImage.RowPitch + xoffset * outputPixelSize + zoffset * mappedImage.DepthPitch));
272 loadFunction(width, height, depth,
273 reinterpret_cast<const uint8_t*>(input), inputRowPitch, inputDepthPitch,
274 offsetMappedData, mappedImage.RowPitch, mappedImage.DepthPitch);
279 void Image11::loadCompressedData(GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth,
282 const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(mInternalFormat);
283 GLsizei inputRowPitch = formatInfo.computeRowPitch(GL_UNSIGNED_BYTE, width, 1);
284 GLsizei inputDepthPitch = formatInfo.computeDepthPitch(GL_UNSIGNED_BYTE, width, height, 1);
286 const d3d11::DXGIFormat &dxgiFormatInfo = d3d11::GetDXGIFormatInfo(mDXGIFormat);
287 GLuint outputPixelSize = dxgiFormatInfo.pixelBytes;
288 GLuint outputBlockWidth = dxgiFormatInfo.blockWidth;
289 GLuint outputBlockHeight = dxgiFormatInfo.blockHeight;
291 ASSERT(xoffset % outputBlockWidth == 0);
292 ASSERT(yoffset % outputBlockHeight == 0);
294 const d3d11::TextureFormat &d3dFormatInfo = d3d11::GetTextureFormatInfo(mInternalFormat);
295 LoadImageFunction loadFunction = d3dFormatInfo.loadFunctions.at(GL_UNSIGNED_BYTE);
297 D3D11_MAPPED_SUBRESOURCE mappedImage;
298 HRESULT result = map(D3D11_MAP_WRITE, &mappedImage);
301 ERR("Could not map image for loading.");
305 uint8_t* offsetMappedData = reinterpret_cast<uint8_t*>(mappedImage.pData) + ((yoffset / outputBlockHeight) * mappedImage.RowPitch +
306 (xoffset / outputBlockWidth) * outputPixelSize +
307 zoffset * mappedImage.DepthPitch);
309 loadFunction(width, height, depth,
310 reinterpret_cast<const uint8_t*>(input), inputRowPitch, inputDepthPitch,
311 offsetMappedData, mappedImage.RowPitch, mappedImage.DepthPitch);
316 void Image11::copy(GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, gl::Framebuffer *source)
318 gl::FramebufferAttachment *colorbuffer = source->getReadColorbuffer();
320 if (colorbuffer && colorbuffer->getActualFormat() == mActualFormat)
322 // No conversion needed-- use copyback fastpath
323 ID3D11Texture2D *colorBufferTexture = NULL;
324 unsigned int subresourceIndex = 0;
326 if (mRenderer->getRenderTargetResource(colorbuffer, &subresourceIndex, &colorBufferTexture))
328 D3D11_TEXTURE2D_DESC textureDesc;
329 colorBufferTexture->GetDesc(&textureDesc);
331 ID3D11Device *device = mRenderer->getDevice();
332 ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext();
334 ID3D11Texture2D* srcTex = NULL;
335 if (textureDesc.SampleDesc.Count > 1)
337 D3D11_TEXTURE2D_DESC resolveDesc;
338 resolveDesc.Width = textureDesc.Width;
339 resolveDesc.Height = textureDesc.Height;
340 resolveDesc.MipLevels = 1;
341 resolveDesc.ArraySize = 1;
342 resolveDesc.Format = textureDesc.Format;
343 resolveDesc.SampleDesc.Count = 1;
344 resolveDesc.SampleDesc.Quality = 0;
345 resolveDesc.Usage = D3D11_USAGE_DEFAULT;
346 resolveDesc.BindFlags = 0;
347 resolveDesc.CPUAccessFlags = 0;
348 resolveDesc.MiscFlags = 0;
350 HRESULT result = device->CreateTexture2D(&resolveDesc, NULL, &srcTex);
353 ERR("Failed to create resolve texture for Image11::copy, HRESULT: 0x%X.", result);
357 deviceContext->ResolveSubresource(srcTex, 0, colorBufferTexture, subresourceIndex, textureDesc.Format);
358 subresourceIndex = 0;
362 srcTex = colorBufferTexture;
368 srcBox.right = x + width;
370 srcBox.bottom = y + height;
374 deviceContext->CopySubresourceRegion(mStagingTexture, 0, xoffset, yoffset, zoffset, srcTex, subresourceIndex, &srcBox);
377 SafeRelease(colorBufferTexture);
382 // This format requires conversion, so we must copy the texture to staging and manually convert via readPixels
383 D3D11_MAPPED_SUBRESOURCE mappedImage;
384 HRESULT result = map(D3D11_MAP_WRITE, &mappedImage);
387 ERR("Failed to map texture for Image11::copy, HRESULT: 0x%X.", result);
391 // determine the offset coordinate into the destination buffer
392 GLsizei rowOffset = gl::GetInternalFormatInfo(mActualFormat).pixelBytes * xoffset;
393 uint8_t *dataOffset = static_cast<uint8_t*>(mappedImage.pData) + mappedImage.RowPitch * yoffset + rowOffset + zoffset * mappedImage.DepthPitch;
395 const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(mInternalFormat);
397 mRenderer->readPixels(source, x, y, width, height, formatInfo.format, formatInfo.type, mappedImage.RowPitch, gl::PixelPackState(), dataOffset);
403 ID3D11Resource *Image11::getStagingTexture()
405 createStagingTexture();
407 return mStagingTexture;
410 void Image11::releaseStagingTexture()
412 SafeRelease(mStagingTexture);
415 unsigned int Image11::getStagingSubresource()
417 createStagingTexture();
419 return mStagingSubresource;
422 void Image11::createStagingTexture()
429 const DXGI_FORMAT dxgiFormat = getDXGIFormat();
431 if (mWidth > 0 && mHeight > 0 && mDepth > 0)
433 ID3D11Device *device = mRenderer->getDevice();
437 GLsizei width = mWidth;
438 GLsizei height = mHeight;
440 // adjust size if needed for compressed textures
441 d3d11::MakeValidSize(false, dxgiFormat, &width, &height, &lodOffset);
443 if (mTarget == GL_TEXTURE_3D)
445 ID3D11Texture3D *newTexture = NULL;
447 D3D11_TEXTURE3D_DESC desc;
449 desc.Height = height;
451 desc.MipLevels = lodOffset + 1;
452 desc.Format = dxgiFormat;
453 desc.Usage = D3D11_USAGE_STAGING;
455 desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
458 if (d3d11::GetTextureFormatInfo(mInternalFormat).dataInitializerFunction != NULL)
460 std::vector<D3D11_SUBRESOURCE_DATA> initialData;
461 std::vector< std::vector<BYTE> > textureData;
462 d3d11::GenerateInitialTextureData(mInternalFormat, width, height, mDepth,
463 lodOffset + 1, &initialData, &textureData);
465 result = device->CreateTexture3D(&desc, initialData.data(), &newTexture);
469 result = device->CreateTexture3D(&desc, NULL, &newTexture);
474 ASSERT(result == E_OUTOFMEMORY);
475 ERR("Creating image failed.");
476 return gl::error(GL_OUT_OF_MEMORY);
479 mStagingTexture = newTexture;
480 mStagingSubresource = D3D11CalcSubresource(lodOffset, 0, lodOffset + 1);
482 else if (mTarget == GL_TEXTURE_2D || mTarget == GL_TEXTURE_2D_ARRAY || mTarget == GL_TEXTURE_CUBE_MAP)
484 ID3D11Texture2D *newTexture = NULL;
486 D3D11_TEXTURE2D_DESC desc;
488 desc.Height = height;
489 desc.MipLevels = lodOffset + 1;
491 desc.Format = dxgiFormat;
492 desc.SampleDesc.Count = 1;
493 desc.SampleDesc.Quality = 0;
494 desc.Usage = D3D11_USAGE_STAGING;
496 desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
499 if (d3d11::GetTextureFormatInfo(mInternalFormat).dataInitializerFunction != NULL)
501 std::vector<D3D11_SUBRESOURCE_DATA> initialData;
502 std::vector< std::vector<BYTE> > textureData;
503 d3d11::GenerateInitialTextureData(mInternalFormat, width, height, 1,
504 lodOffset + 1, &initialData, &textureData);
506 result = device->CreateTexture2D(&desc, initialData.data(), &newTexture);
510 result = device->CreateTexture2D(&desc, NULL, &newTexture);
515 ASSERT(result == E_OUTOFMEMORY);
516 ERR("Creating image failed.");
517 return gl::error(GL_OUT_OF_MEMORY);
520 mStagingTexture = newTexture;
521 mStagingSubresource = D3D11CalcSubresource(lodOffset, 0, lodOffset + 1);
532 HRESULT Image11::map(D3D11_MAP mapType, D3D11_MAPPED_SUBRESOURCE *map)
534 createStagingTexture();
536 // We must recover from the TextureStorage if necessary, even for D3D11_MAP_WRITE.
537 recoverFromAssociatedStorage();
539 HRESULT result = E_FAIL;
543 ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext();
544 result = deviceContext->Map(mStagingTexture, mStagingSubresource, mapType, 0, map);
546 // this can fail if the device is removed (from TDR)
547 if (d3d11::isDeviceLostError(result))
549 mRenderer->notifyDeviceLost();
551 else if (SUCCEEDED(result))
560 void Image11::unmap()
564 ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext();
565 deviceContext->Unmap(mStagingTexture, mStagingSubresource);