// HUH??? #define RAD_TELEMETRY_DISABLED #include "TextureDx11.h" #include "shaderapi/ishaderapi.h" #include "shaderdevicedx11.h" #include "shaderapidx11_global.h" #include "shaderapi/ishaderutil.h" #include "filesystem.h" #include "bitmap/tgawriter.h" // NOTE: This has to be the last file included! #include "tier0/memdbgon.h" // Returns a matching ShaderResourceView format for the depth texture's format. FORCEINLINE DXGI_FORMAT GetDepthSRVFormat( DXGI_FORMAT depthResourceFormat ) { switch ( depthResourceFormat ) { case DXGI_FORMAT_R16_TYPELESS: return DXGI_FORMAT_R16_UNORM; case DXGI_FORMAT_R24G8_TYPELESS: return DXGI_FORMAT_R24_UNORM_X8_TYPELESS; case DXGI_FORMAT_R32_TYPELESS: return DXGI_FORMAT_R32_FLOAT; case DXGI_FORMAT_R32G8X24_TYPELESS: return DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS; default: return DXGI_FORMAT_UNKNOWN; } } // Returns a matching DepthStencilView format for the depth texture's format. FORCEINLINE DXGI_FORMAT GetDepthStencilViewFormat( DXGI_FORMAT depthResourceFormat ) { switch ( depthResourceFormat ) { case DXGI_FORMAT_R16_TYPELESS: return DXGI_FORMAT_D16_UNORM; case DXGI_FORMAT_R24G8_TYPELESS: return DXGI_FORMAT_D24_UNORM_S8_UINT; case DXGI_FORMAT_R32_TYPELESS: return DXGI_FORMAT_D32_FLOAT; case DXGI_FORMAT_R32G8X24_TYPELESS: return DXGI_FORMAT_D32_FLOAT_S8X24_UINT; default: return DXGI_FORMAT_UNKNOWN; } } // We will have to convert srcFormat into what this function // returns for use in D3D11. ImageFormat CTextureDx11::GetClosestSupportedImageFormatForD3D11( ImageFormat srcFormat ) { switch ( srcFormat ) { case IMAGE_FORMAT_BGR888: return IMAGE_FORMAT_BGRA8888; case IMAGE_FORMAT_RGB888: return IMAGE_FORMAT_RGBA8888; default: return srcFormat; } } DXGI_FORMAT GetD3DFormat( ImageFormat format ) { switch ( format ) { // These are not exact but have the same number // of channels and bits-per-channel. case IMAGE_FORMAT_I8: return DXGI_FORMAT_R8_UNORM; case IMAGE_FORMAT_IA88: return DXGI_FORMAT_R8G8_UNORM; case IMAGE_FORMAT_UV88: return DXGI_FORMAT_R8G8_UNORM; case IMAGE_FORMAT_UVWQ8888: case IMAGE_FORMAT_UVLX8888: return DXGI_FORMAT_R8G8B8A8_UNORM; // Depth buffer formats case IMAGE_FORMAT_NV_DST24: return DXGI_FORMAT_R24G8_TYPELESS; case IMAGE_FORMAT_NV_DST16: return DXGI_FORMAT_R16_TYPELESS; // These ones match D3D. case IMAGE_FORMAT_A8: return DXGI_FORMAT_A8_UNORM; case IMAGE_FORMAT_DXT1: case IMAGE_FORMAT_DXT1_ONEBITALPHA: return DXGI_FORMAT_BC1_UNORM; //case IMAGE_FORMAT_DXT1_SRGB: //case IMAGE_FORMAT_DXT1_ONEBITALPHA_SRGB: return DXGI_FORMAT_BC1_UNORM_SRGB; case IMAGE_FORMAT_DXT3: return DXGI_FORMAT_BC2_UNORM; //case IMAGE_FORMAT_DXT3_SRGB: return DXGI_FORMAT_BC2_UNORM_SRGB; case IMAGE_FORMAT_DXT5: return DXGI_FORMAT_BC3_UNORM; //case IMAGE_FORMAT_DXT5_SRGB: return DXGI_FORMAT_BC3_UNORM_SRGB; case IMAGE_FORMAT_BGRA4444: return DXGI_FORMAT_B4G4R4A4_UNORM; case IMAGE_FORMAT_BGRX5551: return DXGI_FORMAT_B5G5R5A1_UNORM; case IMAGE_FORMAT_BGR565: return DXGI_FORMAT_B5G6R5_UNORM; case IMAGE_FORMAT_BGRX8888: return DXGI_FORMAT_B8G8R8X8_UNORM; //case IMAGE_FORMAT_BGRX8888_SRGB: return DXGI_FORMAT_B8G8R8X8_UNORM_SRGB; case IMAGE_FORMAT_ARGB8888: // unsupported? return DXGI_FORMAT_R8G8B8A8_UNORM; case IMAGE_FORMAT_BGRA8888: return DXGI_FORMAT_B8G8R8A8_UNORM; //case IMAGE_FORMAT_BGRA8888_SRGB: return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; case IMAGE_FORMAT_RGBA16161616F: return DXGI_FORMAT_R16G16B16A16_FLOAT; case IMAGE_FORMAT_RGBA16161616: return DXGI_FORMAT_R16G16B16A16_UNORM; case IMAGE_FORMAT_R32F: return DXGI_FORMAT_R32_FLOAT; case IMAGE_FORMAT_RGBA32323232F: return DXGI_FORMAT_R32G32B32A32_FLOAT; case IMAGE_FORMAT_RGBA8888: return DXGI_FORMAT_R8G8B8A8_UNORM; //case IMAGE_FORMAT_RGBA8888_SRGB: return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; default: return DXGI_FORMAT_UNKNOWN; } } ImageFormat CTextureDx11::GetImageFormat( DXGI_FORMAT d3dFormat ) { switch ( d3dFormat ) { case DXGI_FORMAT_UNKNOWN: return IMAGE_FORMAT_UNKNOWN; // These are not exact but have the same number // of channels and bits-per-channel. case DXGI_FORMAT_R8_UNORM: return IMAGE_FORMAT_I8; case DXGI_FORMAT_R8G8_UNORM: return IMAGE_FORMAT_IA88; case DXGI_FORMAT_R8G8B8A8_UNORM: return IMAGE_FORMAT_RGBA8888; case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: return IMAGE_FORMAT_RGBA8888;// _SRGB; case DXGI_FORMAT_A8_UNORM: return IMAGE_FORMAT_A8; case DXGI_FORMAT_BC1_UNORM: return IMAGE_FORMAT_DXT1; case DXGI_FORMAT_BC1_UNORM_SRGB: return IMAGE_FORMAT_DXT1;// _SRGB; case DXGI_FORMAT_BC2_UNORM: return IMAGE_FORMAT_DXT3; case DXGI_FORMAT_BC2_UNORM_SRGB: return IMAGE_FORMAT_DXT3;// _SRGB; case DXGI_FORMAT_BC3_UNORM: return IMAGE_FORMAT_DXT5; case DXGI_FORMAT_BC3_UNORM_SRGB: return IMAGE_FORMAT_DXT5;// _SRGB; case DXGI_FORMAT_B4G4R4A4_UNORM: return IMAGE_FORMAT_BGRA4444; case DXGI_FORMAT_B5G6R5_UNORM: return IMAGE_FORMAT_BGR565; case DXGI_FORMAT_B8G8R8X8_UNORM: return IMAGE_FORMAT_BGRX8888; case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: return IMAGE_FORMAT_BGRX8888;// _SRGB; case DXGI_FORMAT_B8G8R8A8_UNORM: return IMAGE_FORMAT_BGRA8888; case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: return IMAGE_FORMAT_BGRA8888;// _SRGB; case DXGI_FORMAT_R16G16B16A16_FLOAT: return IMAGE_FORMAT_RGBA16161616F; case DXGI_FORMAT_R16G16B16A16_UNORM: return IMAGE_FORMAT_RGBA16161616; case DXGI_FORMAT_R32_FLOAT: return IMAGE_FORMAT_R32F; case DXGI_FORMAT_R32G32B32A32_FLOAT: return IMAGE_FORMAT_RGBA32323232F; default: return IMAGE_FORMAT_UNKNOWN; } } int CTextureDx11::CalcRamBytes() const { int rWidth = m_nWidth; int rHeight = m_nHeight; int rDepth = m_Depth; int nRamBytes = 0; for ( int i = 0; i < m_NumLevels; i++ ) { nRamBytes += ImageLoader::GetMemRequired( rWidth, rHeight, rDepth, m_Format, false ); if ( rWidth == 1 && rHeight == 1 && rDepth == 1 ) { break; } rDepth >>= 1; rWidth >>= 1; rHeight >>= 1; if ( rWidth < 1 ) { rWidth = 1; } if ( rHeight < 1 ) { rHeight = 1; } if ( rDepth < 1 ) { rDepth = 1; } } return nRamBytes; } // Texture2D is used for regular 2D textures, cubemaps, and 2D texture arrays // TODO: Support 1D and 3D textures? ID3D11Resource *CTextureDx11::CreateD3DTexture( int width, int height, int nDepth, ImageFormat dstFormat, int numLevels, int nCreationFlags ) { if ( nDepth <= 0 ) nDepth = 1; bool isCubeMap = ( nCreationFlags & TEXTURE_CREATE_CUBEMAP ) != 0; if ( isCubeMap ) nDepth = 6; bool bIsRenderTarget = ( nCreationFlags & TEXTURE_CREATE_RENDERTARGET ) != 0; //bool bManaged = ( nCreationFlags & TEXTURE_CREATE_MANAGED ) != 0; bool bIsDepthBuffer = ( nCreationFlags & TEXTURE_CREATE_DEPTHBUFFER ) != 0; bool isDynamic = ( nCreationFlags & TEXTURE_CREATE_DYNAMIC ) != 0; bool bAutoMipMap = ( nCreationFlags & TEXTURE_CREATE_AUTOMIPMAP ) != 0; //bool bVertexTexture = ( nCreationFlags & TEXTURE_CREATE_VERTEXTEXTURE ) != 0; //bool bAllowNonFilterable = ( nCreationFlags & TEXTURE_CREATE_UNFILTERABLE_OK ) != 0; //bool bVolumeTexture = ( nDepth > 1 ); //bool bIsFallback = ( nCreationFlags & TEXTURE_CREATE_FALLBACK ) != 0; //bool bNoD3DBits = ( nCreationFlags & TEXTURE_CREATE_NOD3DMEMORY ) != 0; // NOTE: This function shouldn't be used for creating depth buffers! //Assert( !bIsDepthBuffer ); dstFormat = GetClosestSupportedImageFormatForD3D11( dstFormat ); m_Format = dstFormat; DXGI_FORMAT d3dFormat = DXGI_FORMAT_UNKNOWN; d3dFormat = GetD3DFormat( dstFormat ); if ( d3dFormat == DXGI_FORMAT_UNKNOWN ) { Warning( "ShaderAPIDX11::CreateD3DTexture: Invalid image format %i!\n", (int)dstFormat ); Assert( 0 ); return 0; } m_D3DFormat = d3dFormat; D3D11_USAGE usage = D3D11_USAGE_DEFAULT; if ( isDynamic ) { usage = D3D11_USAGE_DYNAMIC; } UINT miscFlags = 0; if ( bAutoMipMap ) { miscFlags |= D3D11_RESOURCE_MISC_GENERATE_MIPS; } if ( isCubeMap ) { miscFlags |= D3D11_RESOURCE_MISC_TEXTURECUBE; } UINT bindFlags = D3D11_BIND_SHADER_RESOURCE; if ( bIsRenderTarget ) { bindFlags |= D3D11_BIND_RENDER_TARGET; } if ( bIsDepthBuffer ) { bindFlags |= D3D11_BIND_DEPTH_STENCIL; } UINT cpuAccessFlags = 0; if ( isDynamic ) { cpuAccessFlags = D3D11_CPU_ACCESS_WRITE; } ID3D11Resource *pBaseTexture = NULL; HRESULT hr = S_OK; // TODO: Support 1D and 3D textures // (2D textures are good for regular textures, cubemaps, and texture arrays) D3D11_TEXTURE2D_DESC desc; ZeroMemory( &desc, sizeof( D3D11_TEXTURE2D_DESC ) ); desc.ArraySize = nDepth; desc.Format = d3dFormat; desc.Width = width; desc.Height = height; desc.MipLevels = numLevels; desc.MiscFlags = miscFlags; desc.BindFlags = bindFlags; desc.CPUAccessFlags = cpuAccessFlags; desc.Usage = usage; desc.SampleDesc.Count = 1; desc.SampleDesc.Quality = 0; ID3D11Texture2D *pTex2D = NULL; hr = D3D11Device()->CreateTexture2D( &desc, NULL, &pTex2D ); pBaseTexture = pTex2D; if ( FAILED( hr ) ) { switch ( hr ) { case E_OUTOFMEMORY: Warning( "TextureDx11::CreateD3DTexture: E_OUTOFMEMORY\n" ); break; default: break; } return 0; } //s_TextureCount++; return pBaseTexture; } void DestroyD3DTexture( ID3D11Resource *pD3DTex ) { if ( pD3DTex ) { pD3DTex->Release(); Assert( ref == 0 ); //s_TextureCount--; } } //----------------------------------- // Dx11 implementation of a texture //----------------------------------- CTextureDx11::CTextureDx11() { m_pLockedFace = NULL; m_nLockedFaceSize = 0; m_pLockedTexture = NULL; m_LockedSubresource = 0; m_bLocked = false; m_nFlags = 0; m_Count = 0; m_CountIndex = 0; m_nTimesBoundMax = 0; m_nTimesBoundThisFrame = 0; m_Anisotropy = 0; m_Format = IMAGE_FORMAT_RGBA8888; m_MinFilter = D3D11_FILTER_TYPE_LINEAR; m_MagFilter = D3D11_FILTER_TYPE_LINEAR; m_MipFilter = D3D11_FILTER_TYPE_POINT; m_bIsAnisotropic = false; m_UTexWrap = D3D11_TEXTURE_ADDRESS_CLAMP; m_VTexWrap = D3D11_TEXTURE_ADDRESS_CLAMP; m_WTexWrap = D3D11_TEXTURE_ADDRESS_CLAMP; m_Filter = D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT; m_pTexture = NULL; m_ppTexture = NULL; m_pSamplerState = NULL; m_pView = NULL; m_ppView = NULL; m_pDepthStencilView = NULL; m_pRenderTargetView = NULL; m_pRamImage = NULL; m_ppRamImage = NULL; } inline int CalcMipLevels( int w, int h ) { return /*ceil( log2( max( w, h ) ) ) +*/ 1; // we dont support mips yet } void CTextureDx11::SetupTexture2D( int width, int height, int depth, int count, int i, int flags, int numCopies, int numMipLevels, ImageFormat dstImageFormat ) { if ( dstImageFormat == IMAGE_FORMAT_I8 ) { //DebuggerBreak(); } //Log( "Making texture2D\n" ); bool bIsRenderTarget = ( flags & TEXTURE_CREATE_RENDERTARGET ) != 0; bool bIsDepthBuffer = ( flags & TEXTURE_CREATE_DEPTHBUFFER ) != 0; if ( bIsDepthBuffer ) { //Log( "Making depth buffer\n" ); SetupDepthTexture( dstImageFormat, width, height, "depth", true ); return; } else if ( bIsRenderTarget ) { //Log( "Making render target\n" ); SetupBackBuffer( width, height, "rendertarget", NULL, dstImageFormat ); return; } //dstImageFormat = IMAGE_FORMAT_RGBA8888; //--------------------------------------------------------------------------------- if ( depth == 0 ) depth = 1; bool bIsCubeMap = ( flags & TEXTURE_CREATE_CUBEMAP ) != 0; if ( bIsCubeMap ) depth = 6; bool bIsDynamic = ( flags & TEXTURE_CREATE_DYNAMIC ) != 0; //bool bAutoMipMap = ( flags & TEXTURE_CREATE_AUTOMIPMAP ) != 0; bool bIsManaged = ( flags & TEXTURE_CREATE_MANAGED ) != 0; // Can't be both managed + dynamic. Dynamic is an optimization, but // if it's not managed, then we gotta do special client-specific stuff // So, managed wins out! if ( bIsManaged ) bIsDynamic = false; /*if ( bAutoMipMap && numMipLevels == 0 && !bIsDynamic ) { numMipLevels = CalcMipLevels( width, height ); } else if ( bIsDynamic )*/ { // Dynamic textures can't have mipmaps numMipLevels = 1; } m_iTextureType = TEXTURE_STANDARD; m_iTextureDimensions = TEXTURE_2D; unsigned short usSetFlags = 0; usSetFlags |= ( flags & TEXTURE_CREATE_VERTEXTEXTURE ) ? CTextureDx11::IS_VERTEX_TEXTURE : 0; m_nFlags = CTextureDx11::IS_ALLOCATED; m_nWidth = width; m_nHeight = height; m_Depth = depth; m_Count = count; m_CountIndex = i; m_CreationFlags = flags; m_nFlags |= usSetFlags; m_NumLevels = numMipLevels; ID3D11Resource *pD3DTex; // Set the initial texture state if ( numCopies <= 1 ) { m_NumCopies = 1; pD3DTex = CreateD3DTexture( width, height, depth, dstImageFormat, numMipLevels, flags ); SetTexture( pD3DTex ); if ( bIsDynamic ) { int nRamBytes = CalcRamBytes(); m_pRamImage = new unsigned char[nRamBytes]; memset( m_pRamImage, 0, nRamBytes ); } } else { m_NumCopies = numCopies; m_ppTexture = new ID3D11Resource * [numCopies]; if ( bIsDynamic ) m_ppRamImage = new unsigned char * [numCopies]; for ( int k = 0; k < numCopies; k++ ) { pD3DTex = CreateD3DTexture( width, height, depth, dstImageFormat, numMipLevels, flags ); SetTexture( k, pD3DTex ); if ( bIsDynamic ) { int nRamBytes = CalcRamBytes(); m_ppRamImage[k] = new unsigned char[nRamBytes]; memset( m_ppRamImage[k], 0, nRamBytes ); } } } m_CurrentCopy = 0; if ( m_NumCopies == 1 ) pD3DTex = m_pTexture; else pD3DTex = m_ppTexture[m_CurrentCopy]; m_UTexWrap = D3D11_TEXTURE_ADDRESS_CLAMP; m_VTexWrap = D3D11_TEXTURE_ADDRESS_CLAMP; m_WTexWrap = D3D11_TEXTURE_ADDRESS_CLAMP; m_MipFilter = ( numMipLevels != 1 ) ? D3D11_FILTER_TYPE_LINEAR : D3D11_FILTER_TYPE_POINT; m_MinFilter = m_MagFilter = D3D11_FILTER_TYPE_LINEAR; m_SwitchNeeded = false; AdjustD3DFilter(); MakeView(); } void CTextureDx11::SetupDepthTexture( ImageFormat depthFormat, int width, int height, const char *pDebugName, bool bTexture ) { m_nFlags = CTextureDx11::IS_ALLOCATED; if ( bTexture ) m_nFlags |= CTextureDx11::IS_DEPTH_STENCIL_TEXTURE; else m_nFlags |= CTextureDx11::IS_DEPTH_STENCIL; m_iTextureType = TEXTURE_DEPTHSTENCIL; m_iTextureDimensions = TEXTURE_3D; m_nWidth = width; m_nHeight = height; m_Depth = 1; m_Count = 1; m_CountIndex = 0; m_CreationFlags = TEXTURE_CREATE_DEPTHBUFFER; m_MinFilter = D3D11_FILTER_TYPE_LINEAR; m_MagFilter = D3D11_FILTER_TYPE_LINEAR; m_MipFilter = D3D11_FILTER_TYPE_POINT; m_NumLevels = 1; m_NumCopies = 1; m_CurrentCopy = 0; m_pTexture = CreateD3DTexture( width, height, m_Depth, depthFormat, m_NumLevels, m_CreationFlags ); AdjustD3DFilter(); MakeDepthStencilView(); if ( bTexture ) { MakeView(); } } void CTextureDx11::SetupBackBuffer( int width, int height, const char *pDebugName, ID3D11Texture2D *pBackBuffer, ImageFormat format ) { m_nFlags = CTextureDx11::IS_ALLOCATED; m_iTextureType = TEXTURE_RENDERTARGET; m_iTextureDimensions = TEXTURE_2D; m_nWidth = width; m_nHeight = height; m_Depth = 1; m_Count = 1; m_CountIndex = 0; m_CreationFlags = TEXTURE_CREATE_RENDERTARGET; m_MinFilter = D3D11_FILTER_TYPE_LINEAR; m_MagFilter = D3D11_FILTER_TYPE_LINEAR; m_MipFilter = D3D11_FILTER_TYPE_POINT; m_NumLevels = 1; m_NumCopies = 1; m_CurrentCopy = 0; m_Format = GetClosestSupportedImageFormatForD3D11( format ); m_D3DFormat = GetD3DFormat( m_Format ); if ( pBackBuffer ) m_pTexture = pBackBuffer; else m_pTexture = CreateD3DTexture( width, height, m_Depth, format, m_NumLevels, m_CreationFlags ); AdjustD3DFilter(); MakeRenderTargetView(); MakeView(); } void CTextureDx11::SetMinFilter( ShaderTexFilterMode_t texFilterMode ) { switch ( texFilterMode ) { case SHADER_TEXFILTERMODE_LINEAR: case SHADER_TEXFILTERMODE_LINEAR_MIPMAP_NEAREST: m_MinFilter = D3D11_FILTER_TYPE_LINEAR; m_MipFilter = D3D11_FILTER_TYPE_POINT; m_bIsAnisotropic = false; break; case SHADER_TEXFILTERMODE_LINEAR_MIPMAP_LINEAR: m_MinFilter = D3D11_FILTER_TYPE_LINEAR; m_MipFilter = D3D11_FILTER_TYPE_LINEAR; m_bIsAnisotropic = false; break; case SHADER_TEXFILTERMODE_NEAREST: case SHADER_TEXFILTERMODE_NEAREST_MIPMAP_NEAREST: m_MinFilter = D3D11_FILTER_TYPE_POINT; m_MipFilter = D3D11_FILTER_TYPE_POINT; m_bIsAnisotropic = false; break; case SHADER_TEXFILTERMODE_NEAREST_MIPMAP_LINEAR: m_MinFilter = D3D11_FILTER_TYPE_POINT; m_MipFilter = D3D11_FILTER_TYPE_LINEAR; m_bIsAnisotropic = false; break; case SHADER_TEXFILTERMODE_ANISOTROPIC: m_bIsAnisotropic = true; break; } AdjustD3DFilter(); } void CTextureDx11::SetMagFilter( ShaderTexFilterMode_t texFilterMode ) { switch ( texFilterMode ) { case SHADER_TEXFILTERMODE_LINEAR: m_MagFilter = D3D11_FILTER_TYPE_LINEAR; break; case SHADER_TEXFILTERMODE_NEAREST: m_MagFilter = D3D11_FILTER_TYPE_POINT; break; } AdjustD3DFilter(); } void CTextureDx11::SetWrap( ShaderTexCoordComponent_t coord, ShaderTexWrapMode_t wrapMode ) { D3D11_TEXTURE_ADDRESS_MODE address; switch ( wrapMode ) { case SHADER_TEXWRAPMODE_CLAMP: address = D3D11_TEXTURE_ADDRESS_CLAMP; break; case SHADER_TEXWRAPMODE_REPEAT: address = D3D11_TEXTURE_ADDRESS_WRAP; break; case SHADER_TEXWRAPMODE_BORDER: address = D3D11_TEXTURE_ADDRESS_BORDER; break; default: address = D3D11_TEXTURE_ADDRESS_CLAMP; Warning( "CTextureDx11::SetWrap: unknown wrapMode %i\n", wrapMode ); break; } switch ( coord ) { case SHADER_TEXCOORD_S: m_UTexWrap = address; break; case SHADER_TEXCOORD_T: m_VTexWrap = address; break; case SHADER_TEXCOORD_U: m_WTexWrap = address; break; default: Warning( "CTextureDx11::SetWrap: unknown coord %i\n", coord ); break; } AdjustSamplerState(); } void CTextureDx11::AdjustD3DFilter() { // Set to anisotropic if the texture was already set to an anisotropic filter, // or we are using at least bilinear filtering and have an anisotropic level > 1 if ( m_bIsAnisotropic || ( m_MinFilter == D3D11_FILTER_TYPE_LINEAR && m_MagFilter == D3D11_FILTER_TYPE_LINEAR && m_Anisotropy > 1 ) ) { m_Filter = D3D11_FILTER_ANISOTROPIC; } else { m_Filter = D3D11_ENCODE_BASIC_FILTER( m_MinFilter, m_MagFilter, m_MipFilter, D3D11_FILTER_REDUCTION_TYPE_STANDARD ); } AdjustSamplerState(); } void CTextureDx11::SetAnisotropicLevel( int level ) { m_Anisotropy = level; AdjustD3DFilter(); } void CTextureDx11::AdjustSamplerState() { if ( m_pSamplerState ) m_pSamplerState->Release(); m_pSamplerState = NULL; D3D11_SAMPLER_DESC desc; ZeroMemory( &desc, sizeof( D3D11_SAMPLER_DESC ) ); desc.AddressU = m_UTexWrap; desc.AddressV = m_VTexWrap; desc.AddressW = m_WTexWrap; desc.Filter = m_Filter; desc.MaxAnisotropy = m_Anisotropy; desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS; desc.MinLOD = 0; desc.MaxLOD = D3D11_FLOAT32_MAX; HRESULT hr = D3D11Device()->CreateSamplerState( &desc, &m_pSamplerState ); if ( FAILED( hr ) ) { Warning( "CTextureDx11: Couldn't create sampler state!\n" ); } } void CTextureDx11::MakeRenderTargetView() { if ( m_iTextureType != TEXTURE_RENDERTARGET ) { return; } D3D11_RENDER_TARGET_VIEW_DESC desc; ZeroMemory( &desc, sizeof( D3D11_RENDER_TARGET_VIEW_DESC ) ); desc.Format = m_D3DFormat; desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; if ( m_pRenderTargetView ) { m_pRenderTargetView->Release(); Assert( ref == 0 ); } HRESULT hr = D3D11Device()->CreateRenderTargetView( m_pTexture, &desc, &m_pRenderTargetView ); if ( FAILED( hr ) ) { Warning( "Failed to make D3D render target view!\n" ); } } void CTextureDx11::MakeDepthStencilView() { if ( m_iTextureType != TEXTURE_DEPTHSTENCIL ) { return; } D3D11_DEPTH_STENCIL_VIEW_DESC desc; ZeroMemory( &desc, sizeof( D3D11_DEPTH_STENCIL_VIEW_DESC ) ); desc.Format = GetDepthStencilViewFormat( m_D3DFormat ); desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; desc.Flags = 0; if ( m_pDepthStencilView ) { m_pDepthStencilView->Release(); Assert( ref == 0 ); } HRESULT hr = D3D11Device()->CreateDepthStencilView( m_pTexture, &desc, &m_pDepthStencilView ); if ( FAILED( hr ) ) { Warning( "Failed to make D3D depth stencil view!\n" ); } } void CTextureDx11::MakeView() { DXGI_FORMAT format; if ( m_iTextureType == TEXTURE_DEPTHSTENCIL ) { format = GetDepthSRVFormat( m_D3DFormat ); } else { format = m_D3DFormat; } D3D11_SRV_DIMENSION dim; if ( m_Depth == 6 && ( ( m_CreationFlags & TEXTURE_CREATE_CUBEMAP ) != 0 ) ) { dim = D3D11_SRV_DIMENSION_TEXTURECUBE; } else if ( m_Depth > 1 ) { dim = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; } else { dim = D3D11_SRV_DIMENSION_TEXTURE2D; } CD3D11_SHADER_RESOURCE_VIEW_DESC desc( dim, format ); if ( m_NumCopies > 1 ) { if ( m_ppView ) { delete[] m_ppView; } m_ppView = new ID3D11ShaderResourceView * [m_NumCopies]; for ( int copy = 0; copy < m_NumCopies; copy++ ) { HRESULT hr = D3D11Device()->CreateShaderResourceView( m_ppTexture[copy], &desc, &m_ppView[copy] ); if ( FAILED( hr ) ) { Warning( "Unable to create shader resource view for texture copy %i!\n", copy ); } } } else { if ( m_pView ) { m_pView->Release(); } m_pView = NULL; HRESULT hr = D3D11Device()->CreateShaderResourceView( m_pTexture, &desc, &m_pView ); if ( FAILED( hr ) ) { Warning( "Unable to create D3D11 Texture view!\n" ); } } } static int s_CubemapBlits = 0; void CTextureDx11::BlitSurfaceBits( CTextureDx11::TextureLoadInfo_t &info, int xOffset, int yOffset, int srcStride ) { if ( m_CreationFlags & TEXTURE_CREATE_DYNAMIC ) { // Dynamic textures need to be updated using Lock() and Unlock() Warning( "TextureDX11: Tried to call BlitSurfaceBits() on a dynamic texture!\n" ); return; } int width = info.m_nWidth; int height = info.m_nHeight; if ( m_Format == IMAGE_FORMAT_DXT3 || //m_Format == IMAGE_FORMAT_DXT3_SRGB || m_Format == IMAGE_FORMAT_DXT5 || //m_Format == IMAGE_FORMAT_DXT5_SRGB || m_Format == IMAGE_FORMAT_DXT1 || //m_Format == IMAGE_FORMAT_DXT1_SRGB || m_Format == IMAGE_FORMAT_DXT1_ONEBITALPHA )//|| m_Format == IMAGE_FORMAT_DXT1_ONEBITALPHA_SRGB ) { width = ALIGN_VALUE( width, 4 ); height = ALIGN_VALUE( height, 4 ); } CD3D11_BOX box; box.left = xOffset; box.right = xOffset + width; box.top = yOffset; box.bottom = yOffset + height; box.front = info.m_nZOffset; box.back = info.m_nZOffset + 1; int mem = ImageLoader::GetMemRequired( width, height, info.m_nZOffset, m_Format, false ); unsigned char *pNewImage = new unsigned char[mem]; int dstStride = ImageLoader::SizeInBytes( m_Format ); int pitch = dstStride * width; if ( m_Format == IMAGE_FORMAT_DXT3 || //m_Format == IMAGE_FORMAT_DXT3_SRGB || m_Format == IMAGE_FORMAT_DXT5 )//|| m_Format == IMAGE_FORMAT_DXT5_SRGB ) { pitch = 16 * ( width / 4 ); } else if ( m_Format == IMAGE_FORMAT_DXT1 || //m_Format == IMAGE_FORMAT_DXT1_SRGB || m_Format == IMAGE_FORMAT_DXT1_ONEBITALPHA )//|| m_Format == IMAGE_FORMAT_DXT1_ONEBITALPHA_SRGB ) { pitch = 8 * ( width / 4 ); } bool ret = ShaderUtil()->ConvertImageFormat( info.m_pSrcData, info.m_SrcFormat, pNewImage, m_Format, width, height, srcStride ); if ( !ret ) { Warning( "Couldn't convert texture for uploading to D3D!\n" ); return; } #if 0 if ( m_CreationFlags & TEXTURE_CREATE_CUBEMAP ) { char cmfile[100]; sprintf( cmfile, "C:\\Users\\Brian\\Desktop\\SourceDX11Debug\\cubemap-%i-face-%i.tga", s_CubemapBlits, info.m_CubeFaceID ); TGAWriter::WriteTGAFile( cmfile, info.m_nWidth, info.m_nHeight, m_Format, pNewImage, dstStride * info.m_nWidth ); if ( info.m_CubeFaceID == 4 ) { s_CubemapBlits++; } } #endif UINT subresource = D3D11CalcSubresource( info.m_nLevel, info.m_CubeFaceID, m_NumLevels ); D3D11DeviceContext()->UpdateSubresource( info.m_pTexture, subresource, &box, pNewImage, pitch, 0 ); delete[] pNewImage; // If the texture was created with auto mipmap and we have just filled in the base mip, // make D3D generate the remaining mips. If the texture was not created with auto mipmap, // or we have just filled in a non-base mip, we assume the user is filling in the mips. if ( ( ( m_CreationFlags & TEXTURE_CREATE_AUTOMIPMAP ) != 0 ) && m_NumLevels > 1 && info.m_nLevel == 0 ) { D3D11DeviceContext()->GenerateMips( info.m_pView ); } } void CTextureDx11::BlitTextureBits( CTextureDx11::TextureLoadInfo_t &info, int xOffset, int yOffset, int srcStride ) { // TODO: Volume texture? //Assert( info.m_nZOffset == 0 ); if ( info.m_nZOffset != 0 ) { return; } BlitSurfaceBits( info, xOffset, yOffset, srcStride ); } void CTextureDx11::LoadTexImage( CTextureDx11::TextureLoadInfo_t &info, int xOffset, int yOffset, int srcStride ) { MEM_ALLOC_CREDIT(); Assert( info.m_pSrcData ); Assert( info.m_pTexture ); // Copy in the bits... BlitTextureBits( info, xOffset, yOffset, srcStride ); } void CTextureDx11::LoadTextureFromVTF(CTextureDx11::TextureLoadInfo_t& info, IVTFTexture* pVTF, int iVTFFrame) { BlitTextureBits(info, 0, 0, 0); } void CTextureDx11::Delete() { int nDeallocated = 0; if ( m_NumCopies == 1 ) { if ( m_pView ) { m_pView->Release(); Assert( refCount == 0 ); m_pView = 0; nDeallocated++; } if ( m_pTexture ) { DestroyD3DTexture( m_pTexture ); m_pTexture = 0; nDeallocated = 1; } if ( m_pRamImage ) { delete[] m_pRamImage; m_pRamImage = NULL; } } else { if ( m_ppView ) { for ( int i = 0; i < m_NumCopies; i++ ) { if ( m_ppView[i] ) { m_ppView[i]->Release(); Assert( refCount == 0 ); nDeallocated++; } } delete[] m_ppView; m_ppView = NULL; } if ( m_ppTexture ) { // Multiple copy texture for ( int j = 0; j < m_NumCopies; j++ ) { if ( m_ppTexture[j] ) { DestroyD3DTexture( m_ppTexture[j] ); m_ppTexture[j] = 0; ++nDeallocated; } } delete[] m_ppTexture; m_ppTexture = NULL; } if ( m_ppRamImage ) { delete[] m_ppRamImage; m_ppRamImage = NULL; } } if ( m_iTextureType == TEXTURE_RENDERTARGET ) { if ( m_pRenderTargetView ) { m_pRenderTargetView->Release(); Assert( refCount == 0 ); m_pRenderTargetView = 0; nDeallocated++; } } else if ( m_iTextureType == TEXTURE_DEPTHSTENCIL ) { if ( m_pDepthStencilView ) { m_pDepthStencilView->Release(); Assert( refCount == 0 ); m_pDepthStencilView = 0; nDeallocated++; } } if ( m_pSamplerState ) { m_pSamplerState->Release(); m_pSamplerState = 0; nDeallocated++; } m_NumCopies = 0; m_nFlags = 0; } bool CTextureDx11::Lock( int copy, int level, int cubeFaceID, int xOffset, int yOffset, int width, int height, bool bDiscard, CPixelWriter &writer ) { Assert( !m_bLocked ); Assert( level == 0 ); // dynamic textures can't have mipmaps if ( m_bLocked ) { return false; } if ( m_NumCopies == 1 ) { m_pLockedTexture = m_pTexture; m_pLockedFace = m_pRamImage; } else { m_pLockedTexture = m_ppTexture[copy]; m_pLockedFace = m_ppRamImage[copy]; } int bytesPerXel = ImageLoader::SizeInBytes( m_Format ); int bytesPerRow = bytesPerXel * m_nWidth; int bytesPerFace = bytesPerXel * m_nWidth * m_nHeight; // Where do we want to start modifying? int nMemOffset = bytesPerFace * cubeFaceID; nMemOffset += yOffset * bytesPerRow; nMemOffset += xOffset * bytesPerXel; // Offset into the ram image unsigned char *pModifyBegin = m_pLockedFace + nMemOffset; m_nLockedFaceSize = bytesPerFace; m_LockedSubresource = D3D11CalcSubresource( 0, cubeFaceID, 1 ); ZeroMemory( &m_MappedData, sizeof( D3D11_MAPPED_SUBRESOURCE ) ); HRESULT hr = D3D11DeviceContext()->Map( m_pLockedTexture, m_LockedSubresource, D3D11_MAP_WRITE_DISCARD, 0, &m_MappedData ); if ( FAILED( hr ) ) { return false; } writer.SetPixelMemory( m_Format, pModifyBegin, bytesPerRow ); m_bLocked = true; return true; } void CTextureDx11::Unlock( int copy, int level, int cubeFaceID ) { Assert( m_bLocked ); if ( !m_bLocked ) { return; } memcpy( m_MappedData.pData, m_pLockedFace, m_nLockedFaceSize ); D3D11DeviceContext()->Unmap( m_pLockedTexture, m_LockedSubresource ); m_pLockedFace = NULL; m_nLockedFaceSize = 0; m_pLockedTexture = NULL; m_LockedSubresource = 0; m_bLocked = false; }