1// 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. 5// 6 7// Image11.h: Implements the rx::Image11 class, which acts as the interface to 8// the actual underlying resources of a Texture 9 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" 18 19#include "common/utilities.h" 20 21namespace rx 22{ 23 24Image11::Image11() 25{ 26 mStagingTexture = NULL; 27 mRenderer = NULL; 28 mDXGIFormat = DXGI_FORMAT_UNKNOWN; 29 mRecoverFromStorage = false; 30 mAssociatedStorage = NULL; 31 mAssociatedStorageLevel = 0; 32 mAssociatedStorageLayerTarget = 0; 33 mRecoveredFromStorageCount = 0; 34} 35 36Image11::~Image11() 37{ 38 disassociateStorage(); 39 releaseStagingTexture(); 40} 41 42Image11 *Image11::makeImage11(Image *img) 43{ 44 ASSERT(HAS_DYNAMIC_TYPE(rx::Image11*, img)); 45 return static_cast<rx::Image11*>(img); 46} 47 48void Image11::generateMipmap(Image11 *dest, Image11 *src) 49{ 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()); 53 54 const d3d11::DXGIFormat &dxgiFormatInfo = d3d11::GetDXGIFormatInfo(src->getDXGIFormat()); 55 ASSERT(dxgiFormatInfo.mipGenerationFunction != NULL); 56 57 D3D11_MAPPED_SUBRESOURCE destMapped; 58 HRESULT destMapResult = dest->map(D3D11_MAP_WRITE, &destMapped); 59 if (FAILED(destMapResult)) 60 { 61 ERR("Failed to map destination image for mip map generation. HRESULT:0x%X", destMapResult); 62 return; 63 } 64 65 D3D11_MAPPED_SUBRESOURCE srcMapped; 66 HRESULT srcMapResult = src->map(D3D11_MAP_READ, &srcMapped); 67 if (FAILED(srcMapResult)) 68 { 69 ERR("Failed to map source image for mip map generation. HRESULT:0x%X", srcMapResult); 70 71 dest->unmap(); 72 return; 73 } 74 75 const uint8_t *sourceData = reinterpret_cast<const uint8_t*>(srcMapped.pData); 76 uint8_t *destData = reinterpret_cast<uint8_t*>(destMapped.pData); 77 78 dxgiFormatInfo.mipGenerationFunction(src->getWidth(), src->getHeight(), src->getDepth(), 79 sourceData, srcMapped.RowPitch, srcMapped.DepthPitch, 80 destData, destMapped.RowPitch, destMapped.DepthPitch); 81 82 dest->unmap(); 83 src->unmap(); 84 85 dest->markDirty(); 86} 87 88bool Image11::isDirty() const 89{ 90 // If mDirty is true 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)) 95 { 96 return false; 97 } 98 99 return mDirty; 100} 101 102bool Image11::copyToStorage2D(TextureStorage *storage, int level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height) 103{ 104 TextureStorage11_2D *storage11 = TextureStorage11_2D::makeTextureStorage11_2D(storage); 105 return copyToStorageImpl(storage11, level, 0, xoffset, yoffset, width, height); 106} 107 108bool Image11::copyToStorageCube(TextureStorage *storage, int face, int level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height) 109{ 110 TextureStorage11_Cube *storage11 = TextureStorage11_Cube::makeTextureStorage11_Cube(storage); 111 return copyToStorageImpl(storage11, level, face, xoffset, yoffset, width, height); 112} 113 114bool Image11::copyToStorage3D(TextureStorage *storage, int level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth) 115{ 116 TextureStorage11_3D *storage11 = TextureStorage11_3D::makeTextureStorage11_3D(storage); 117 return copyToStorageImpl(storage11, level, 0, xoffset, yoffset, width, height); 118} 119 120bool Image11::copyToStorage2DArray(TextureStorage *storage, int level, GLint xoffset, GLint yoffset, GLint arrayLayer, GLsizei width, GLsizei height) 121{ 122 TextureStorage11_2DArray *storage11 = TextureStorage11_2DArray::makeTextureStorage11_2DArray(storage); 123 return copyToStorageImpl(storage11, level, arrayLayer, xoffset, yoffset, width, height); 124} 125 126bool Image11::copyToStorageImpl(TextureStorage11 *storage11, int level, int layerTarget, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height) 127{ 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); 133 134 if (attemptToReleaseStagingTexture) 135 { 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); 138 } 139 140 bool updateSubresourceSuccess = storage11->updateSubresourceLevel(getStagingTexture(), getStagingSubresource(), level, layerTarget, xoffset, yoffset, 0, width, height, 1); 141 142 // Once the image data has been copied into the Storage, we can release it locally. 143 if (attemptToReleaseStagingTexture && updateSubresourceSuccess) 144 { 145 storage11->associateImage(this, level, layerTarget); 146 releaseStagingTexture(); 147 mRecoverFromStorage = true; 148 mAssociatedStorage = storage11; 149 mAssociatedStorageLevel = level; 150 mAssociatedStorageLayerTarget = layerTarget; 151 } 152 153 return updateSubresourceSuccess; 154} 155 156bool Image11::isAssociatedStorageValid(TextureStorage11* textureStorage) const 157{ 158 return (mAssociatedStorage == textureStorage); 159} 160 161bool Image11::recoverFromAssociatedStorage() 162{ 163 if (mRecoverFromStorage) 164 { 165 createStagingTexture(); 166 167 bool textureStorageCorrect = mAssociatedStorage->isAssociatedImageValid(mAssociatedStorageLevel, mAssociatedStorageLayerTarget, this); 168 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); 172 173 if (textureStorageCorrect) 174 { 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; 178 } 179 180 // Reset all the recovery parameters, even if the texture storage association is broken. 181 disassociateStorage(); 182 183 return textureStorageCorrect; 184 } 185 186 return false; 187} 188 189void Image11::disassociateStorage() 190{ 191 if (mRecoverFromStorage) 192 { 193 // Make the texturestorage release the Image11 too 194 mAssociatedStorage->disassociateImage(mAssociatedStorageLevel, mAssociatedStorageLayerTarget, this); 195 196 mRecoverFromStorage = false; 197 mAssociatedStorage = NULL; 198 mAssociatedStorageLevel = 0; 199 mAssociatedStorageLayerTarget = 0; 200 } 201} 202 203bool Image11::redefine(Renderer *renderer, GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, bool forceRelease) 204{ 205 if (mWidth != width || 206 mHeight != height || 207 mInternalFormat != internalformat || 208 forceRelease) 209 { 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; 214 215 mRenderer = Renderer11::makeRenderer11(renderer); 216 217 mWidth = width; 218 mHeight = height; 219 mDepth = depth; 220 mInternalFormat = internalformat; 221 mTarget = target; 222 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); 229 230 SafeRelease(mStagingTexture); 231 mDirty = (formatInfo.dataInitializerFunction != NULL); 232 233 return true; 234 } 235 236 return false; 237} 238 239DXGI_FORMAT Image11::getDXGIFormat() const 240{ 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); 244 245 return mDXGIFormat; 246} 247 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. 250void Image11::loadData(GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, 251 GLint unpackAlignment, GLenum type, const void *input) 252{ 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); 256 257 const d3d11::DXGIFormat &dxgiFormatInfo = d3d11::GetDXGIFormatInfo(mDXGIFormat); 258 GLuint outputPixelSize = dxgiFormatInfo.pixelBytes; 259 260 const d3d11::TextureFormat &d3dFormatInfo = d3d11::GetTextureFormatInfo(mInternalFormat); 261 LoadImageFunction loadFunction = d3dFormatInfo.loadFunctions.at(type); 262 263 D3D11_MAPPED_SUBRESOURCE mappedImage; 264 HRESULT result = map(D3D11_MAP_WRITE, &mappedImage); 265 if (FAILED(result)) 266 { 267 ERR("Could not map image for loading."); 268 return; 269 } 270 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); 275 276 unmap(); 277} 278 279void Image11::loadCompressedData(GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, 280 const void *input) 281{ 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); 285 286 const d3d11::DXGIFormat &dxgiFormatInfo = d3d11::GetDXGIFormatInfo(mDXGIFormat); 287 GLuint outputPixelSize = dxgiFormatInfo.pixelBytes; 288 GLuint outputBlockWidth = dxgiFormatInfo.blockWidth; 289 GLuint outputBlockHeight = dxgiFormatInfo.blockHeight; 290 291 ASSERT(xoffset % outputBlockWidth == 0); 292 ASSERT(yoffset % outputBlockHeight == 0); 293 294 const d3d11::TextureFormat &d3dFormatInfo = d3d11::GetTextureFormatInfo(mInternalFormat); 295 LoadImageFunction loadFunction = d3dFormatInfo.loadFunctions.at(GL_UNSIGNED_BYTE); 296 297 D3D11_MAPPED_SUBRESOURCE mappedImage; 298 HRESULT result = map(D3D11_MAP_WRITE, &mappedImage); 299 if (FAILED(result)) 300 { 301 ERR("Could not map image for loading."); 302 return; 303 } 304 305 uint8_t* offsetMappedData = reinterpret_cast<uint8_t*>(mappedImage.pData) + ((yoffset / outputBlockHeight) * mappedImage.RowPitch + 306 (xoffset / outputBlockWidth) * outputPixelSize + 307 zoffset * mappedImage.DepthPitch); 308 309 loadFunction(width, height, depth, 310 reinterpret_cast<const uint8_t*>(input), inputRowPitch, inputDepthPitch, 311 offsetMappedData, mappedImage.RowPitch, mappedImage.DepthPitch); 312 313 unmap(); 314} 315 316void Image11::copy(GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, gl::Framebuffer *source) 317{ 318 gl::FramebufferAttachment *colorbuffer = source->getReadColorbuffer(); 319 320 if (colorbuffer && colorbuffer->getActualFormat() == mActualFormat) 321 { 322 // No conversion needed-- use copyback fastpath 323 ID3D11Texture2D *colorBufferTexture = NULL; 324 unsigned int subresourceIndex = 0; 325 326 if (mRenderer->getRenderTargetResource(colorbuffer, &subresourceIndex, &colorBufferTexture)) 327 { 328 D3D11_TEXTURE2D_DESC textureDesc; 329 colorBufferTexture->GetDesc(&textureDesc); 330 331 ID3D11Device *device = mRenderer->getDevice(); 332 ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); 333 334 ID3D11Texture2D* srcTex = NULL; 335 if (textureDesc.SampleDesc.Count > 1) 336 { 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; 349 350 HRESULT result = device->CreateTexture2D(&resolveDesc, NULL, &srcTex); 351 if (FAILED(result)) 352 { 353 ERR("Failed to create resolve texture for Image11::copy, HRESULT: 0x%X.", result); 354 return; 355 } 356 357 deviceContext->ResolveSubresource(srcTex, 0, colorBufferTexture, subresourceIndex, textureDesc.Format); 358 subresourceIndex = 0; 359 } 360 else 361 { 362 srcTex = colorBufferTexture; 363 srcTex->AddRef(); 364 } 365 366 D3D11_BOX srcBox; 367 srcBox.left = x; 368 srcBox.right = x + width; 369 srcBox.top = y; 370 srcBox.bottom = y + height; 371 srcBox.front = 0; 372 srcBox.back = 1; 373 374 deviceContext->CopySubresourceRegion(mStagingTexture, 0, xoffset, yoffset, zoffset, srcTex, subresourceIndex, &srcBox); 375 376 SafeRelease(srcTex); 377 SafeRelease(colorBufferTexture); 378 } 379 } 380 else 381 { 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); 385 if (FAILED(result)) 386 { 387 ERR("Failed to map texture for Image11::copy, HRESULT: 0x%X.", result); 388 return; 389 } 390 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; 394 395 const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(mInternalFormat); 396 397 mRenderer->readPixels(source, x, y, width, height, formatInfo.format, formatInfo.type, mappedImage.RowPitch, gl::PixelPackState(), dataOffset); 398 399 unmap(); 400 } 401} 402 403ID3D11Resource *Image11::getStagingTexture() 404{ 405 createStagingTexture(); 406 407 return mStagingTexture; 408} 409 410void Image11::releaseStagingTexture() 411{ 412 SafeRelease(mStagingTexture); 413} 414 415unsigned int Image11::getStagingSubresource() 416{ 417 createStagingTexture(); 418 419 return mStagingSubresource; 420} 421 422void Image11::createStagingTexture() 423{ 424 if (mStagingTexture) 425 { 426 return; 427 } 428 429 const DXGI_FORMAT dxgiFormat = getDXGIFormat(); 430 431 if (mWidth > 0 && mHeight > 0 && mDepth > 0) 432 { 433 ID3D11Device *device = mRenderer->getDevice(); 434 HRESULT result; 435 436 int lodOffset = 1; 437 GLsizei width = mWidth; 438 GLsizei height = mHeight; 439 440 // adjust size if needed for compressed textures 441 d3d11::MakeValidSize(false, dxgiFormat, &width, &height, &lodOffset); 442 443 if (mTarget == GL_TEXTURE_3D) 444 { 445 ID3D11Texture3D *newTexture = NULL; 446 447 D3D11_TEXTURE3D_DESC desc; 448 desc.Width = width; 449 desc.Height = height; 450 desc.Depth = mDepth; 451 desc.MipLevels = lodOffset + 1; 452 desc.Format = dxgiFormat; 453 desc.Usage = D3D11_USAGE_STAGING; 454 desc.BindFlags = 0; 455 desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE; 456 desc.MiscFlags = 0; 457 458 if (d3d11::GetTextureFormatInfo(mInternalFormat).dataInitializerFunction != NULL) 459 { 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); 464 465 result = device->CreateTexture3D(&desc, initialData.data(), &newTexture); 466 } 467 else 468 { 469 result = device->CreateTexture3D(&desc, NULL, &newTexture); 470 } 471 472 if (FAILED(result)) 473 { 474 ASSERT(result == E_OUTOFMEMORY); 475 ERR("Creating image failed."); 476 return gl::error(GL_OUT_OF_MEMORY); 477 } 478 479 mStagingTexture = newTexture; 480 mStagingSubresource = D3D11CalcSubresource(lodOffset, 0, lodOffset + 1); 481 } 482 else if (mTarget == GL_TEXTURE_2D || mTarget == GL_TEXTURE_2D_ARRAY || mTarget == GL_TEXTURE_CUBE_MAP) 483 { 484 ID3D11Texture2D *newTexture = NULL; 485 486 D3D11_TEXTURE2D_DESC desc; 487 desc.Width = width; 488 desc.Height = height; 489 desc.MipLevels = lodOffset + 1; 490 desc.ArraySize = 1; 491 desc.Format = dxgiFormat; 492 desc.SampleDesc.Count = 1; 493 desc.SampleDesc.Quality = 0; 494 desc.Usage = D3D11_USAGE_STAGING; 495 desc.BindFlags = 0; 496 desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE; 497 desc.MiscFlags = 0; 498 499 if (d3d11::GetTextureFormatInfo(mInternalFormat).dataInitializerFunction != NULL) 500 { 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); 505 506 result = device->CreateTexture2D(&desc, initialData.data(), &newTexture); 507 } 508 else 509 { 510 result = device->CreateTexture2D(&desc, NULL, &newTexture); 511 } 512 513 if (FAILED(result)) 514 { 515 ASSERT(result == E_OUTOFMEMORY); 516 ERR("Creating image failed."); 517 return gl::error(GL_OUT_OF_MEMORY); 518 } 519 520 mStagingTexture = newTexture; 521 mStagingSubresource = D3D11CalcSubresource(lodOffset, 0, lodOffset + 1); 522 } 523 else 524 { 525 UNREACHABLE(); 526 } 527 } 528 529 mDirty = false; 530} 531 532HRESULT Image11::map(D3D11_MAP mapType, D3D11_MAPPED_SUBRESOURCE *map) 533{ 534 createStagingTexture(); 535 536 // We must recover from the TextureStorage if necessary, even for D3D11_MAP_WRITE. 537 recoverFromAssociatedStorage(); 538 539 HRESULT result = E_FAIL; 540 541 if (mStagingTexture) 542 { 543 ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); 544 result = deviceContext->Map(mStagingTexture, mStagingSubresource, mapType, 0, map); 545 546 // this can fail if the device is removed (from TDR) 547 if (d3d11::isDeviceLostError(result)) 548 { 549 mRenderer->notifyDeviceLost(); 550 } 551 else if (SUCCEEDED(result)) 552 { 553 mDirty = true; 554 } 555 } 556 557 return result; 558} 559 560void Image11::unmap() 561{ 562 if (mStagingTexture) 563 { 564 ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); 565 deviceContext->Unmap(mStagingTexture, mStagingSubresource); 566 } 567} 568 569} 570