michael@0: #include "precompiled.h" michael@0: // michael@0: // Copyright (c) 2012 The ANGLE Project Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: // michael@0: michael@0: // Image11.h: Implements the rx::Image11 class, which acts as the interface to michael@0: // the actual underlying resources of a Texture michael@0: michael@0: #include "libGLESv2/renderer/Renderer11.h" michael@0: #include "libGLESv2/renderer/Image11.h" michael@0: #include "libGLESv2/renderer/TextureStorage11.h" michael@0: #include "libGLESv2/Framebuffer.h" michael@0: #include "libGLESv2/Renderbuffer.h" michael@0: michael@0: #include "libGLESv2/main.h" michael@0: #include "libGLESv2/utilities.h" michael@0: #include "libGLESv2/renderer/renderer11_utils.h" michael@0: #include "libGLESv2/renderer/generatemip.h" michael@0: michael@0: namespace rx michael@0: { michael@0: michael@0: Image11::Image11() michael@0: { michael@0: mStagingTexture = NULL; michael@0: mRenderer = NULL; michael@0: mDXGIFormat = DXGI_FORMAT_UNKNOWN; michael@0: } michael@0: michael@0: Image11::~Image11() michael@0: { michael@0: if (mStagingTexture) michael@0: { michael@0: mStagingTexture->Release(); michael@0: } michael@0: } michael@0: michael@0: Image11 *Image11::makeImage11(Image *img) michael@0: { michael@0: ASSERT(HAS_DYNAMIC_TYPE(rx::Image11*, img)); michael@0: return static_cast(img); michael@0: } michael@0: michael@0: void Image11::generateMipmap(Image11 *dest, Image11 *src) michael@0: { michael@0: ASSERT(src->getDXGIFormat() == dest->getDXGIFormat()); michael@0: ASSERT(src->getWidth() == 1 || src->getWidth() / 2 == dest->getWidth()); michael@0: ASSERT(src->getHeight() == 1 || src->getHeight() / 2 == dest->getHeight()); michael@0: michael@0: D3D11_MAPPED_SUBRESOURCE destMapped, srcMapped; michael@0: dest->map(&destMapped); michael@0: src->map(&srcMapped); michael@0: michael@0: const unsigned char *sourceData = reinterpret_cast(srcMapped.pData); michael@0: unsigned char *destData = reinterpret_cast(destMapped.pData); michael@0: michael@0: if (sourceData && destData) michael@0: { michael@0: switch (src->getDXGIFormat()) michael@0: { michael@0: case DXGI_FORMAT_R8G8B8A8_UNORM: michael@0: case DXGI_FORMAT_B8G8R8A8_UNORM: michael@0: GenerateMip(src->getWidth(), src->getHeight(), sourceData, srcMapped.RowPitch, destData, destMapped.RowPitch); michael@0: break; michael@0: case DXGI_FORMAT_A8_UNORM: michael@0: GenerateMip(src->getWidth(), src->getHeight(), sourceData, srcMapped.RowPitch, destData, destMapped.RowPitch); michael@0: break; michael@0: case DXGI_FORMAT_R8_UNORM: michael@0: GenerateMip(src->getWidth(), src->getHeight(), sourceData, srcMapped.RowPitch, destData, destMapped.RowPitch); michael@0: break; michael@0: case DXGI_FORMAT_R32G32B32A32_FLOAT: michael@0: GenerateMip(src->getWidth(), src->getHeight(), sourceData, srcMapped.RowPitch, destData, destMapped.RowPitch); michael@0: break; michael@0: case DXGI_FORMAT_R32G32B32_FLOAT: michael@0: GenerateMip(src->getWidth(), src->getHeight(), sourceData, srcMapped.RowPitch, destData, destMapped.RowPitch); michael@0: break; michael@0: case DXGI_FORMAT_R16G16B16A16_FLOAT: michael@0: GenerateMip(src->getWidth(), src->getHeight(), sourceData, srcMapped.RowPitch, destData, destMapped.RowPitch); michael@0: break; michael@0: case DXGI_FORMAT_R8G8_UNORM: michael@0: GenerateMip(src->getWidth(), src->getHeight(), sourceData, srcMapped.RowPitch, destData, destMapped.RowPitch); michael@0: break; michael@0: case DXGI_FORMAT_R16_FLOAT: michael@0: GenerateMip(src->getWidth(), src->getHeight(), sourceData, srcMapped.RowPitch, destData, destMapped.RowPitch); michael@0: break; michael@0: case DXGI_FORMAT_R16G16_FLOAT: michael@0: GenerateMip(src->getWidth(), src->getHeight(), sourceData, srcMapped.RowPitch, destData, destMapped.RowPitch); michael@0: break; michael@0: case DXGI_FORMAT_R32_FLOAT: michael@0: GenerateMip(src->getWidth(), src->getHeight(), sourceData, srcMapped.RowPitch, destData, destMapped.RowPitch); michael@0: break; michael@0: case DXGI_FORMAT_R32G32_FLOAT: michael@0: GenerateMip(src->getWidth(), src->getHeight(), sourceData, srcMapped.RowPitch, destData, destMapped.RowPitch); michael@0: break; michael@0: default: michael@0: UNREACHABLE(); michael@0: break; michael@0: } michael@0: michael@0: dest->unmap(); michael@0: src->unmap(); michael@0: } michael@0: michael@0: dest->markDirty(); michael@0: } michael@0: michael@0: bool Image11::isDirty() const michael@0: { michael@0: return (mStagingTexture && mDirty); michael@0: } michael@0: michael@0: bool Image11::updateSurface(TextureStorageInterface2D *storage, int level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height) michael@0: { michael@0: TextureStorage11_2D *storage11 = TextureStorage11_2D::makeTextureStorage11_2D(storage->getStorageInstance()); michael@0: return storage11->updateSubresourceLevel(getStagingTexture(), getStagingSubresource(), level, 0, xoffset, yoffset, width, height); michael@0: } michael@0: michael@0: bool Image11::updateSurface(TextureStorageInterfaceCube *storage, int face, int level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height) michael@0: { michael@0: TextureStorage11_Cube *storage11 = TextureStorage11_Cube::makeTextureStorage11_Cube(storage->getStorageInstance()); michael@0: return storage11->updateSubresourceLevel(getStagingTexture(), getStagingSubresource(), level, face, xoffset, yoffset, width, height); michael@0: } michael@0: michael@0: bool Image11::redefine(Renderer *renderer, GLint internalformat, GLsizei width, GLsizei height, bool forceRelease) michael@0: { michael@0: if (mWidth != width || michael@0: mHeight != height || michael@0: mInternalFormat != internalformat || michael@0: forceRelease) michael@0: { michael@0: mRenderer = Renderer11::makeRenderer11(renderer); michael@0: michael@0: mWidth = width; michael@0: mHeight = height; michael@0: mInternalFormat = internalformat; michael@0: // compute the d3d format that will be used michael@0: mDXGIFormat = gl_d3d11::ConvertTextureFormat(internalformat); michael@0: mActualFormat = d3d11_gl::ConvertTextureInternalFormat(mDXGIFormat); michael@0: michael@0: if (mStagingTexture) michael@0: { michael@0: mStagingTexture->Release(); michael@0: mStagingTexture = NULL; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: bool Image11::isRenderableFormat() const michael@0: { michael@0: return TextureStorage11::IsTextureFormatRenderable(mDXGIFormat); michael@0: } michael@0: michael@0: DXGI_FORMAT Image11::getDXGIFormat() const michael@0: { michael@0: // this should only happen if the image hasn't been redefined first michael@0: // which would be a bug by the caller michael@0: ASSERT(mDXGIFormat != DXGI_FORMAT_UNKNOWN); michael@0: michael@0: return mDXGIFormat; michael@0: } michael@0: michael@0: // Store the pixel rectangle designated by xoffset,yoffset,width,height with pixels stored as format/type at input michael@0: // into the target pixel rectangle. michael@0: void Image11::loadData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, michael@0: GLint unpackAlignment, const void *input) michael@0: { michael@0: D3D11_MAPPED_SUBRESOURCE mappedImage; michael@0: HRESULT result = map(&mappedImage); michael@0: if (FAILED(result)) michael@0: { michael@0: ERR("Could not map image for loading."); michael@0: return; michael@0: } michael@0: michael@0: GLsizei inputPitch = gl::ComputePitch(width, mInternalFormat, unpackAlignment); michael@0: size_t pixelSize = d3d11::ComputePixelSizeBits(mDXGIFormat) / 8; michael@0: void* offsetMappedData = (void*)((BYTE *)mappedImage.pData + (yoffset * mappedImage.RowPitch + xoffset * pixelSize)); michael@0: michael@0: switch (mInternalFormat) michael@0: { michael@0: case GL_ALPHA8_EXT: michael@0: loadAlphaDataToNative(width, height, inputPitch, input, mappedImage.RowPitch, offsetMappedData); michael@0: break; michael@0: case GL_LUMINANCE8_EXT: michael@0: loadLuminanceDataToNativeOrBGRA(width, height, inputPitch, input, mappedImage.RowPitch, offsetMappedData, false); michael@0: break; michael@0: case GL_ALPHA32F_EXT: michael@0: loadAlphaFloatDataToRGBA(width, height, inputPitch, input, mappedImage.RowPitch, offsetMappedData); michael@0: break; michael@0: case GL_LUMINANCE32F_EXT: michael@0: loadLuminanceFloatDataToRGBA(width, height, inputPitch, input, mappedImage.RowPitch, offsetMappedData); michael@0: break; michael@0: case GL_ALPHA16F_EXT: michael@0: loadAlphaHalfFloatDataToRGBA(width, height, inputPitch, input, mappedImage.RowPitch, offsetMappedData); michael@0: break; michael@0: case GL_LUMINANCE16F_EXT: michael@0: loadLuminanceHalfFloatDataToRGBA(width, height, inputPitch, input, mappedImage.RowPitch, offsetMappedData); michael@0: break; michael@0: case GL_LUMINANCE8_ALPHA8_EXT: michael@0: loadLuminanceAlphaDataToNativeOrBGRA(width, height, inputPitch, input, mappedImage.RowPitch, offsetMappedData, false); michael@0: break; michael@0: case GL_LUMINANCE_ALPHA32F_EXT: michael@0: loadLuminanceAlphaFloatDataToRGBA(width, height, inputPitch, input, mappedImage.RowPitch, offsetMappedData); michael@0: break; michael@0: case GL_LUMINANCE_ALPHA16F_EXT: michael@0: loadLuminanceAlphaHalfFloatDataToRGBA(width, height, inputPitch, input, mappedImage.RowPitch, offsetMappedData); michael@0: break; michael@0: case GL_RGB8_OES: michael@0: loadRGBUByteDataToRGBA(width, height, inputPitch, input, mappedImage.RowPitch, offsetMappedData); michael@0: break; michael@0: case GL_RGB565: michael@0: loadRGB565DataToRGBA(width, height, inputPitch, input, mappedImage.RowPitch, offsetMappedData); michael@0: break; michael@0: case GL_RGBA8_OES: michael@0: loadRGBAUByteDataToNative(width, height, inputPitch, input, mappedImage.RowPitch, offsetMappedData); michael@0: break; michael@0: case GL_RGBA4: michael@0: loadRGBA4444DataToRGBA(width, height, inputPitch, input, mappedImage.RowPitch, offsetMappedData); michael@0: break; michael@0: case GL_RGB5_A1: michael@0: loadRGBA5551DataToRGBA(width, height, inputPitch, input, mappedImage.RowPitch, offsetMappedData); michael@0: break; michael@0: case GL_BGRA8_EXT: michael@0: loadBGRADataToBGRA(width, height, inputPitch, input, mappedImage.RowPitch, offsetMappedData); michael@0: break; michael@0: case GL_RGB32F_EXT: michael@0: loadRGBFloatDataToRGBA(width, height, inputPitch, input, mappedImage.RowPitch, offsetMappedData); michael@0: break; michael@0: case GL_RGB16F_EXT: michael@0: loadRGBHalfFloatDataToRGBA(width, height, inputPitch, input, mappedImage.RowPitch, offsetMappedData); michael@0: break; michael@0: case GL_RGBA32F_EXT: michael@0: loadRGBAFloatDataToRGBA(width, height, inputPitch, input, mappedImage.RowPitch, offsetMappedData); michael@0: break; michael@0: case GL_RGBA16F_EXT: michael@0: loadRGBAHalfFloatDataToRGBA(width, height, inputPitch, input, mappedImage.RowPitch, offsetMappedData); michael@0: break; michael@0: default: UNREACHABLE(); michael@0: } michael@0: michael@0: unmap(); michael@0: } michael@0: michael@0: void Image11::loadCompressedData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, michael@0: const void *input) michael@0: { michael@0: ASSERT(xoffset % 4 == 0); michael@0: ASSERT(yoffset % 4 == 0); michael@0: michael@0: D3D11_MAPPED_SUBRESOURCE mappedImage; michael@0: HRESULT result = map(&mappedImage); michael@0: if (FAILED(result)) michael@0: { michael@0: ERR("Could not map image for loading."); michael@0: return; michael@0: } michael@0: michael@0: // Size computation assumes a 4x4 block compressed texture format michael@0: size_t blockSize = d3d11::ComputeBlockSizeBits(mDXGIFormat) / 8; michael@0: void* offsetMappedData = (void*)((BYTE *)mappedImage.pData + ((yoffset / 4) * mappedImage.RowPitch + (xoffset / 4) * blockSize)); michael@0: michael@0: GLsizei inputSize = gl::ComputeCompressedSize(width, height, mInternalFormat); michael@0: GLsizei inputPitch = gl::ComputeCompressedPitch(width, mInternalFormat); michael@0: int rows = inputSize / inputPitch; michael@0: for (int i = 0; i < rows; ++i) michael@0: { michael@0: memcpy((void*)((BYTE*)offsetMappedData + i * mappedImage.RowPitch), (void*)((BYTE*)input + i * inputPitch), inputPitch); michael@0: } michael@0: michael@0: unmap(); michael@0: } michael@0: michael@0: void Image11::copy(GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, gl::Framebuffer *source) michael@0: { michael@0: gl::Renderbuffer *colorbuffer = source->getReadColorbuffer(); michael@0: michael@0: if (colorbuffer && colorbuffer->getActualFormat() == (GLuint)mActualFormat) michael@0: { michael@0: // No conversion needed-- use copyback fastpath michael@0: ID3D11Texture2D *colorBufferTexture = NULL; michael@0: unsigned int subresourceIndex = 0; michael@0: michael@0: if (mRenderer->getRenderTargetResource(colorbuffer, &subresourceIndex, &colorBufferTexture)) michael@0: { michael@0: D3D11_TEXTURE2D_DESC textureDesc; michael@0: colorBufferTexture->GetDesc(&textureDesc); michael@0: michael@0: ID3D11Device *device = mRenderer->getDevice(); michael@0: ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); michael@0: michael@0: ID3D11Texture2D* srcTex = NULL; michael@0: if (textureDesc.SampleDesc.Count > 1) michael@0: { michael@0: D3D11_TEXTURE2D_DESC resolveDesc; michael@0: resolveDesc.Width = textureDesc.Width; michael@0: resolveDesc.Height = textureDesc.Height; michael@0: resolveDesc.MipLevels = 1; michael@0: resolveDesc.ArraySize = 1; michael@0: resolveDesc.Format = textureDesc.Format; michael@0: resolveDesc.SampleDesc.Count = 1; michael@0: resolveDesc.SampleDesc.Quality = 0; michael@0: resolveDesc.Usage = D3D11_USAGE_DEFAULT; michael@0: resolveDesc.BindFlags = 0; michael@0: resolveDesc.CPUAccessFlags = 0; michael@0: resolveDesc.MiscFlags = 0; michael@0: michael@0: HRESULT result = device->CreateTexture2D(&resolveDesc, NULL, &srcTex); michael@0: if (FAILED(result)) michael@0: { michael@0: ERR("Failed to create resolve texture for Image11::copy, HRESULT: 0x%X.", result); michael@0: return; michael@0: } michael@0: michael@0: deviceContext->ResolveSubresource(srcTex, 0, colorBufferTexture, subresourceIndex, textureDesc.Format); michael@0: subresourceIndex = 0; michael@0: } michael@0: else michael@0: { michael@0: srcTex = colorBufferTexture; michael@0: srcTex->AddRef(); michael@0: } michael@0: michael@0: D3D11_BOX srcBox; michael@0: srcBox.left = x; michael@0: srcBox.right = x + width; michael@0: srcBox.top = y; michael@0: srcBox.bottom = y + height; michael@0: srcBox.front = 0; michael@0: srcBox.back = 1; michael@0: michael@0: deviceContext->CopySubresourceRegion(mStagingTexture, 0, xoffset, yoffset, 0, srcTex, subresourceIndex, &srcBox); michael@0: michael@0: srcTex->Release(); michael@0: colorBufferTexture->Release(); michael@0: } michael@0: } michael@0: else michael@0: { michael@0: // This format requires conversion, so we must copy the texture to staging and manually convert via readPixels michael@0: D3D11_MAPPED_SUBRESOURCE mappedImage; michael@0: HRESULT result = map(&mappedImage); michael@0: michael@0: // determine the offset coordinate into the destination buffer michael@0: GLsizei rowOffset = gl::ComputePixelSize(mActualFormat) * xoffset; michael@0: void *dataOffset = static_cast(mappedImage.pData) + mappedImage.RowPitch * yoffset + rowOffset; michael@0: michael@0: mRenderer->readPixels(source, x, y, width, height, gl::ExtractFormat(mInternalFormat), michael@0: gl::ExtractType(mInternalFormat), mappedImage.RowPitch, false, 4, dataOffset); michael@0: michael@0: unmap(); michael@0: } michael@0: } michael@0: michael@0: ID3D11Texture2D *Image11::getStagingTexture() michael@0: { michael@0: createStagingTexture(); michael@0: michael@0: return mStagingTexture; michael@0: } michael@0: michael@0: unsigned int Image11::getStagingSubresource() michael@0: { michael@0: createStagingTexture(); michael@0: michael@0: return mStagingSubresource; michael@0: } michael@0: michael@0: void Image11::createStagingTexture() michael@0: { michael@0: if (mStagingTexture) michael@0: { michael@0: return; michael@0: } michael@0: michael@0: ID3D11Texture2D *newTexture = NULL; michael@0: int lodOffset = 1; michael@0: const DXGI_FORMAT dxgiFormat = getDXGIFormat(); michael@0: ASSERT(!d3d11::IsDepthStencilFormat(dxgiFormat)); // We should never get here for depth textures michael@0: michael@0: if (mWidth != 0 && mHeight != 0) michael@0: { michael@0: GLsizei width = mWidth; michael@0: GLsizei height = mHeight; michael@0: michael@0: // adjust size if needed for compressed textures michael@0: gl::MakeValidSize(false, d3d11::IsCompressed(dxgiFormat), &width, &height, &lodOffset); michael@0: ID3D11Device *device = mRenderer->getDevice(); michael@0: michael@0: D3D11_TEXTURE2D_DESC desc; michael@0: desc.Width = width; michael@0: desc.Height = height; michael@0: desc.MipLevels = lodOffset + 1; michael@0: desc.ArraySize = 1; michael@0: desc.Format = dxgiFormat; michael@0: desc.SampleDesc.Count = 1; michael@0: desc.SampleDesc.Quality = 0; michael@0: desc.Usage = D3D11_USAGE_STAGING; michael@0: desc.BindFlags = 0; michael@0: desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; michael@0: desc.MiscFlags = 0; michael@0: michael@0: HRESULT result = device->CreateTexture2D(&desc, NULL, &newTexture); michael@0: michael@0: if (FAILED(result)) michael@0: { michael@0: ASSERT(result == E_OUTOFMEMORY); michael@0: ERR("Creating image failed."); michael@0: return gl::error(GL_OUT_OF_MEMORY); michael@0: } michael@0: } michael@0: michael@0: mStagingTexture = newTexture; michael@0: mStagingSubresource = D3D11CalcSubresource(lodOffset, 0, lodOffset + 1); michael@0: mDirty = false; michael@0: } michael@0: michael@0: HRESULT Image11::map(D3D11_MAPPED_SUBRESOURCE *map) michael@0: { michael@0: createStagingTexture(); michael@0: michael@0: HRESULT result = E_FAIL; michael@0: michael@0: if (mStagingTexture) michael@0: { michael@0: ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); michael@0: result = deviceContext->Map(mStagingTexture, mStagingSubresource, D3D11_MAP_WRITE, 0, map); michael@0: michael@0: // this can fail if the device is removed (from TDR) michael@0: if (d3d11::isDeviceLostError(result)) michael@0: { michael@0: mRenderer->notifyDeviceLost(); michael@0: } michael@0: else if (SUCCEEDED(result)) michael@0: { michael@0: mDirty = true; michael@0: } michael@0: } michael@0: michael@0: return result; michael@0: } michael@0: michael@0: void Image11::unmap() michael@0: { michael@0: if (mStagingTexture) michael@0: { michael@0: ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); michael@0: deviceContext->Unmap(mStagingTexture, mStagingSubresource); michael@0: } michael@0: } michael@0: michael@0: }