// // Copyright 2012 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // TextureStorage9.cpp: Implements the abstract rx::TextureStorage9 class and its concrete derived // classes TextureStorage9_2D and TextureStorage9_Cube, which act as the interface to the // D3D9 texture. #include "libANGLE/renderer/d3d/d3d9/TextureStorage9.h" #include "common/utilities.h" #include "libANGLE/Context.h" #include "libANGLE/Texture.h" #include "libANGLE/formatutils.h" #include "libANGLE/renderer/d3d/EGLImageD3D.h" #include "libANGLE/renderer/d3d/TextureD3D.h" #include "libANGLE/renderer/d3d/d3d9/RenderTarget9.h" #include "libANGLE/renderer/d3d/d3d9/Renderer9.h" #include "libANGLE/renderer/d3d/d3d9/SwapChain9.h" #include "libANGLE/renderer/d3d/d3d9/formatutils9.h" #include "libANGLE/renderer/d3d/d3d9/renderer9_utils.h" namespace rx { TextureStorage9::TextureStorage9(Renderer9 *renderer, DWORD usage, const std::string &label) : TextureStorage(label), mTopLevel(0), mMipLevels(0), mTextureWidth(0), mTextureHeight(0), mInternalFormat(GL_NONE), mTextureFormat(D3DFMT_UNKNOWN), mRenderer(renderer), mD3DUsage(usage), mD3DPool(mRenderer->getTexturePool(usage)) {} TextureStorage9::~TextureStorage9() {} DWORD TextureStorage9::GetTextureUsage(GLenum internalformat, bool renderTarget) { DWORD d3dusage = 0; const gl::InternalFormat &formatInfo = gl::GetSizedInternalFormatInfo(internalformat); const d3d9::TextureFormat &d3dFormatInfo = d3d9::GetTextureFormatInfo(internalformat); if (formatInfo.depthBits > 0 || formatInfo.stencilBits > 0) { d3dusage |= D3DUSAGE_DEPTHSTENCIL; } else if (renderTarget && (d3dFormatInfo.renderFormat != D3DFMT_UNKNOWN)) { d3dusage |= D3DUSAGE_RENDERTARGET; } return d3dusage; } bool TextureStorage9::isRenderTarget() const { return (mD3DUsage & (D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL)) != 0; } bool TextureStorage9::isManaged() const { return (mD3DPool == D3DPOOL_MANAGED); } bool TextureStorage9::supportsNativeMipmapFunction() const { return false; } D3DPOOL TextureStorage9::getPool() const { return mD3DPool; } DWORD TextureStorage9::getUsage() const { return mD3DUsage; } int TextureStorage9::getTopLevel() const { return mTopLevel; } int TextureStorage9::getLevelCount() const { return static_cast(mMipLevels) - mTopLevel; } angle::Result TextureStorage9::setData(const gl::Context *context, const gl::ImageIndex &index, ImageD3D *image, const gl::Box *destBox, GLenum type, const gl::PixelUnpackState &unpack, const uint8_t *pixelData) { ANGLE_HR_UNREACHABLE(GetImplAs(context)); return angle::Result::Stop; } TextureStorage9_2D::TextureStorage9_2D(Renderer9 *renderer, SwapChain9 *swapchain, const std::string &label) : TextureStorage9(renderer, D3DUSAGE_RENDERTARGET, label) { IDirect3DTexture9 *surfaceTexture = swapchain->getOffscreenTexture(); mTexture = surfaceTexture; mMipLevels = surfaceTexture->GetLevelCount(); mInternalFormat = swapchain->getRenderTargetInternalFormat(); D3DSURFACE_DESC surfaceDesc; surfaceTexture->GetLevelDesc(0, &surfaceDesc); mTextureWidth = surfaceDesc.Width; mTextureHeight = surfaceDesc.Height; mTextureFormat = surfaceDesc.Format; mRenderTargets.resize(mMipLevels, nullptr); } TextureStorage9_2D::TextureStorage9_2D(Renderer9 *renderer, GLenum internalformat, bool renderTarget, GLsizei width, GLsizei height, int levels, const std::string &label) : TextureStorage9(renderer, GetTextureUsage(internalformat, renderTarget), label) { mTexture = nullptr; mInternalFormat = internalformat; const d3d9::TextureFormat &d3dFormatInfo = d3d9::GetTextureFormatInfo(internalformat); mTextureFormat = d3dFormatInfo.texFormat; d3d9::MakeValidSize(false, d3dFormatInfo.texFormat, &width, &height, &mTopLevel); mTextureWidth = width; mTextureHeight = height; mMipLevels = mTopLevel + levels; mRenderTargets.resize(levels, nullptr); } TextureStorage9_2D::~TextureStorage9_2D() { SafeRelease(mTexture); for (RenderTargetD3D *renderTarget : mRenderTargets) { SafeDelete(renderTarget); } } // Increments refcount on surface. // caller must Release() the returned surface angle::Result TextureStorage9_2D::getSurfaceLevel(const gl::Context *context, gl::TextureTarget target, int level, bool dirty, IDirect3DSurface9 **outSurface) { ASSERT(target == gl::TextureTarget::_2D); IDirect3DBaseTexture9 *baseTexture = nullptr; ANGLE_TRY(getBaseTexture(context, &baseTexture)); IDirect3DTexture9 *texture = static_cast(baseTexture); HRESULT result = texture->GetSurfaceLevel(level + mTopLevel, outSurface); ANGLE_TRY_HR(GetImplAs(context), result, "Failed to get the surface from a texture"); // With managed textures the driver needs to be informed of updates to the lower mipmap levels if (level + mTopLevel != 0 && isManaged() && dirty) { texture->AddDirtyRect(nullptr); } return angle::Result::Continue; } angle::Result TextureStorage9_2D::findRenderTarget(const gl::Context *context, const gl::ImageIndex &index, GLsizei samples, RenderTargetD3D **outRT) const { ASSERT(index.getLevelIndex() < getLevelCount()); ASSERT(outRT); *outRT = mRenderTargets[index.getLevelIndex()]; return angle::Result::Continue; } angle::Result TextureStorage9_2D::getRenderTarget(const gl::Context *context, const gl::ImageIndex &index, GLsizei samples, RenderTargetD3D **outRT) { ASSERT(index.getLevelIndex() < getLevelCount()); if (!mRenderTargets[index.getLevelIndex()] && isRenderTarget()) { IDirect3DBaseTexture9 *baseTexture = nullptr; ANGLE_TRY(getBaseTexture(context, &baseTexture)); IDirect3DSurface9 *surface = nullptr; ANGLE_TRY(getSurfaceLevel(context, gl::TextureTarget::_2D, index.getLevelIndex(), false, &surface)); size_t textureMipLevel = mTopLevel + index.getLevelIndex(); size_t mipWidth = std::max(mTextureWidth >> textureMipLevel, 1u); size_t mipHeight = std::max(mTextureHeight >> textureMipLevel, 1u); baseTexture->AddRef(); mRenderTargets[index.getLevelIndex()] = new TextureRenderTarget9( baseTexture, textureMipLevel, surface, mInternalFormat, static_cast(mipWidth), static_cast(mipHeight), 1, 0); } ASSERT(outRT); *outRT = mRenderTargets[index.getLevelIndex()]; return angle::Result::Continue; } angle::Result TextureStorage9_2D::generateMipmap(const gl::Context *context, const gl::ImageIndex &sourceIndex, const gl::ImageIndex &destIndex) { angle::ComPtr upper = nullptr; ANGLE_TRY(getSurfaceLevel(context, gl::TextureTarget::_2D, sourceIndex.getLevelIndex(), false, &upper)); angle::ComPtr lower = nullptr; ANGLE_TRY( getSurfaceLevel(context, gl::TextureTarget::_2D, destIndex.getLevelIndex(), true, &lower)); ASSERT(upper && lower); return mRenderer->boxFilter(GetImplAs(context), upper.Get(), lower.Get()); } angle::Result TextureStorage9_2D::getBaseTexture(const gl::Context *context, IDirect3DBaseTexture9 **outTexture) { // if the width or height is not positive this should be treated as an incomplete texture // we handle that here by skipping the d3d texture creation if (mTexture == nullptr && mTextureWidth > 0 && mTextureHeight > 0) { ASSERT(mMipLevels > 0); IDirect3DDevice9 *device = mRenderer->getDevice(); HRESULT result = device->CreateTexture(static_cast(mTextureWidth), static_cast(mTextureHeight), static_cast(mMipLevels), getUsage(), mTextureFormat, getPool(), &mTexture, nullptr); ANGLE_TRY_HR(GetImplAs(context), result, "Failed to create 2D storage texture"); } *outTexture = mTexture; return angle::Result::Continue; } angle::Result TextureStorage9_2D::copyToStorage(const gl::Context *context, TextureStorage *destStorage) { ASSERT(destStorage); TextureStorage9_2D *dest9 = GetAs(destStorage); int levels = getLevelCount(); for (int i = 0; i < levels; ++i) { angle::ComPtr srcSurf = nullptr; ANGLE_TRY(getSurfaceLevel(context, gl::TextureTarget::_2D, i, false, &srcSurf)); angle::ComPtr dstSurf = nullptr; ANGLE_TRY(dest9->getSurfaceLevel(context, gl::TextureTarget::_2D, i, true, &dstSurf)); ANGLE_TRY( mRenderer->copyToRenderTarget(context, dstSurf.Get(), srcSurf.Get(), isManaged())); } return angle::Result::Continue; } TextureStorage9_EGLImage::TextureStorage9_EGLImage(Renderer9 *renderer, EGLImageD3D *image, RenderTarget9 *renderTarget9, const std::string &label) : TextureStorage9(renderer, D3DUSAGE_RENDERTARGET, label), mImage(image) { mInternalFormat = renderTarget9->getInternalFormat(); mTextureFormat = renderTarget9->getD3DFormat(); mTextureWidth = renderTarget9->getWidth(); mTextureHeight = renderTarget9->getHeight(); mTopLevel = static_cast(renderTarget9->getTextureLevel()); mMipLevels = mTopLevel + 1; } TextureStorage9_EGLImage::~TextureStorage9_EGLImage() {} angle::Result TextureStorage9_EGLImage::getSurfaceLevel(const gl::Context *context, gl::TextureTarget target, int level, bool, IDirect3DSurface9 **outSurface) { ASSERT(target == gl::TextureTarget::_2D); ASSERT(level == 0); RenderTargetD3D *renderTargetD3D = nullptr; ANGLE_TRY(mImage->getRenderTarget(context, &renderTargetD3D)); RenderTarget9 *renderTarget9 = GetAs(renderTargetD3D); *outSurface = renderTarget9->getSurface(); return angle::Result::Continue; } angle::Result TextureStorage9_EGLImage::findRenderTarget(const gl::Context *context, const gl::ImageIndex &index, GLsizei samples, RenderTargetD3D **outRT) const { // Since the render target of a EGL image will be updated when orphaning, trying to find a cache // of it can be rarely useful. ANGLE_HR_UNREACHABLE(GetImplAs(context)); return angle::Result::Stop; } angle::Result TextureStorage9_EGLImage::getRenderTarget(const gl::Context *context, const gl::ImageIndex &index, GLsizei samples, RenderTargetD3D **outRT) { ASSERT(!index.hasLayer()); ASSERT(index.getLevelIndex() == 0); ASSERT(samples == 0); return mImage->getRenderTarget(context, outRT); } angle::Result TextureStorage9_EGLImage::getBaseTexture(const gl::Context *context, IDirect3DBaseTexture9 **outTexture) { RenderTargetD3D *renderTargetD3D = nullptr; ANGLE_TRY(mImage->getRenderTarget(context, &renderTargetD3D)); RenderTarget9 *renderTarget9 = GetAs(renderTargetD3D); *outTexture = renderTarget9->getTexture(); ASSERT(*outTexture != nullptr); return angle::Result::Continue; } angle::Result TextureStorage9_EGLImage::generateMipmap(const gl::Context *context, const gl::ImageIndex &, const gl::ImageIndex &) { ANGLE_HR_UNREACHABLE(GetImplAs(context)); return angle::Result::Stop; } angle::Result TextureStorage9_EGLImage::copyToStorage(const gl::Context *context, TextureStorage *destStorage) { ASSERT(destStorage); ASSERT(getLevelCount() == 1); TextureStorage9 *dest9 = GetAs(destStorage); IDirect3DBaseTexture9 *destBaseTexture9 = nullptr; ANGLE_TRY(dest9->getBaseTexture(context, &destBaseTexture9)); IDirect3DTexture9 *destTexture9 = static_cast(destBaseTexture9); angle::ComPtr destSurface = nullptr; HRESULT result = destTexture9->GetSurfaceLevel(destStorage->getTopLevel(), &destSurface); ANGLE_TRY_HR(GetImplAs(context), result, "Failed to get the surface from a texture"); RenderTargetD3D *sourceRenderTarget = nullptr; ANGLE_TRY(mImage->getRenderTarget(context, &sourceRenderTarget)); RenderTarget9 *sourceRenderTarget9 = GetAs(sourceRenderTarget); ANGLE_TRY(mRenderer->copyToRenderTarget(context, destSurface.Get(), sourceRenderTarget9->getSurface(), isManaged())); if (destStorage->getTopLevel() != 0) { destTexture9->AddDirtyRect(nullptr); } return angle::Result::Continue; } TextureStorage9_Cube::TextureStorage9_Cube(Renderer9 *renderer, GLenum internalformat, bool renderTarget, int size, int levels, bool hintLevelZeroOnly, const std::string &label) : TextureStorage9(renderer, GetTextureUsage(internalformat, renderTarget), label) { mTexture = nullptr; for (size_t i = 0; i < gl::kCubeFaceCount; ++i) { mRenderTarget[i] = nullptr; } mInternalFormat = internalformat; const d3d9::TextureFormat &d3dFormatInfo = d3d9::GetTextureFormatInfo(internalformat); mTextureFormat = d3dFormatInfo.texFormat; int height = size; d3d9::MakeValidSize(false, d3dFormatInfo.texFormat, &size, &height, &mTopLevel); mTextureWidth = size; mTextureHeight = size; mMipLevels = mTopLevel + levels; } TextureStorage9_Cube::~TextureStorage9_Cube() { SafeRelease(mTexture); for (size_t i = 0; i < gl::kCubeFaceCount; ++i) { SafeDelete(mRenderTarget[i]); } } // Increments refcount on surface. // caller must Release() the returned surface angle::Result TextureStorage9_Cube::getSurfaceLevel(const gl::Context *context, gl::TextureTarget target, int level, bool dirty, IDirect3DSurface9 **outSurface) { IDirect3DBaseTexture9 *baseTexture = nullptr; ANGLE_TRY(getBaseTexture(context, &baseTexture)); IDirect3DCubeTexture9 *texture = static_cast(baseTexture); D3DCUBEMAP_FACES face = gl_d3d9::ConvertCubeFace(target); HRESULT result = texture->GetCubeMapSurface(face, level, outSurface); ANGLE_TRY_HR(GetImplAs(context), result, "Failed to get the surface from a texture"); // With managed textures the driver needs to be informed of updates to the lower mipmap levels if (level != 0 && isManaged() && dirty) { texture->AddDirtyRect(face, nullptr); } return angle::Result::Continue; } angle::Result TextureStorage9_Cube::findRenderTarget(const gl::Context *context, const gl::ImageIndex &index, GLsizei samples, RenderTargetD3D **outRT) const { ASSERT(outRT); ASSERT(index.getLevelIndex() == 0); ASSERT(samples == 0); ASSERT(index.getType() == gl::TextureType::CubeMap && gl::IsCubeMapFaceTarget(index.getTarget())); const size_t renderTargetIndex = index.cubeMapFaceIndex(); *outRT = mRenderTarget[renderTargetIndex]; return angle::Result::Continue; } angle::Result TextureStorage9_Cube::getRenderTarget(const gl::Context *context, const gl::ImageIndex &index, GLsizei samples, RenderTargetD3D **outRT) { ASSERT(outRT); ASSERT(index.getLevelIndex() == 0); ASSERT(samples == 0); ASSERT(index.getType() == gl::TextureType::CubeMap && gl::IsCubeMapFaceTarget(index.getTarget())); const size_t renderTargetIndex = index.cubeMapFaceIndex(); if (mRenderTarget[renderTargetIndex] == nullptr && isRenderTarget()) { IDirect3DBaseTexture9 *baseTexture = nullptr; ANGLE_TRY(getBaseTexture(context, &baseTexture)); IDirect3DSurface9 *surface = nullptr; ANGLE_TRY(getSurfaceLevel(context, index.getTarget(), mTopLevel + index.getLevelIndex(), false, &surface)); baseTexture->AddRef(); mRenderTarget[renderTargetIndex] = new TextureRenderTarget9( baseTexture, mTopLevel + index.getLevelIndex(), surface, mInternalFormat, static_cast(mTextureWidth), static_cast(mTextureHeight), 1, 0); } *outRT = mRenderTarget[renderTargetIndex]; return angle::Result::Continue; } angle::Result TextureStorage9_Cube::generateMipmap(const gl::Context *context, const gl::ImageIndex &sourceIndex, const gl::ImageIndex &destIndex) { angle::ComPtr upper = nullptr; ANGLE_TRY(getSurfaceLevel(context, sourceIndex.getTarget(), sourceIndex.getLevelIndex(), false, &upper)); angle::ComPtr lower = nullptr; ANGLE_TRY( getSurfaceLevel(context, destIndex.getTarget(), destIndex.getLevelIndex(), true, &lower)); ASSERT(upper && lower); return mRenderer->boxFilter(GetImplAs(context), upper.Get(), lower.Get()); } angle::Result TextureStorage9_Cube::getBaseTexture(const gl::Context *context, IDirect3DBaseTexture9 **outTexture) { // if the size is not positive this should be treated as an incomplete texture // we handle that here by skipping the d3d texture creation if (mTexture == nullptr && mTextureWidth > 0 && mTextureHeight > 0) { ASSERT(mMipLevels > 0); ASSERT(mTextureWidth == mTextureHeight); IDirect3DDevice9 *device = mRenderer->getDevice(); HRESULT result = device->CreateCubeTexture( static_cast(mTextureWidth), static_cast(mMipLevels), getUsage(), mTextureFormat, getPool(), &mTexture, nullptr); ANGLE_TRY_HR(GetImplAs(context), result, "Failed to create cube storage texture"); } *outTexture = mTexture; return angle::Result::Continue; } angle::Result TextureStorage9_Cube::copyToStorage(const gl::Context *context, TextureStorage *destStorage) { ASSERT(destStorage); TextureStorage9_Cube *dest9 = GetAs(destStorage); int levels = getLevelCount(); for (gl::TextureTarget face : gl::AllCubeFaceTextureTargets()) { for (int i = 0; i < levels; i++) { angle::ComPtr srcSurf = nullptr; ANGLE_TRY(getSurfaceLevel(context, face, i, false, &srcSurf)); angle::ComPtr dstSurf = nullptr; ANGLE_TRY(dest9->getSurfaceLevel(context, face, i, true, &dstSurf)); ANGLE_TRY( mRenderer->copyToRenderTarget(context, dstSurf.Get(), srcSurf.Get(), isManaged())); } } return angle::Result::Continue; } } // namespace rx