| /*============================================================================== |
| Copyright(c) 2017 Intel Corporation |
| |
| Permission is hereby granted, free of charge, to any person obtaining a |
| copy of this software and associated documentation files(the "Software"), |
| to deal in the Software without restriction, including without limitation |
| the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| and / or sell copies of the Software, and to permit persons to whom the |
| Software is furnished to do so, subject to the following conditions: |
| |
| The above copyright notice and this permission notice shall be included |
| in all copies or substantial portions of the Software. |
| |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
| OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR |
| OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
| ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
| OTHER DEALINGS IN THE SOFTWARE. |
| ============================================================================*/ |
| |
| #include "Internal/Common/GmmLibInc.h" |
| |
| ///////////////////////////////////////////////////////////////////////////////////// |
| /// Allocates the 2D mip layout for surface state programming. |
| /// |
| /// @param[in] pTexInfo: ptr to ::GMM_TEXTURE_INFO, |
| /// @param[in] pRestrictions: ptr to surface alignment and size restrictions |
| /// |
| /// @return ::GMM_STATUS |
| ///////////////////////////////////////////////////////////////////////////////////// |
| GMM_STATUS GMM_STDCALL GmmLib::GmmGen7TextureCalc::FillTex2D(GMM_TEXTURE_INFO * pTexInfo, |
| __GMM_BUFFER_TYPE *pRestrictions) |
| { |
| uint32_t Width, Height, BitsPerPixel; |
| uint32_t HAlign, VAlign; |
| uint32_t CompressHeight, CompressWidth, CompressDepth; |
| uint32_t AlignedWidth, BlockHeight, ExpandedArraySize, Pitch; |
| uint8_t Compress = 0; |
| GMM_STATUS Status; |
| |
| __GMM_ASSERTPTR(pTexInfo, GMM_ERROR); |
| __GMM_ASSERTPTR(pRestrictions, GMM_ERROR); |
| |
| GMM_DPF_ENTER; |
| |
| const GMM_PLATFORM_INFO *pPlatform = GMM_OVERRIDE_PLATFORM_INFO(pTexInfo); |
| |
| BitsPerPixel = pTexInfo->BitsPerPixel; |
| Height = pTexInfo->BaseHeight; |
| Width = GFX_ULONG_CAST(pTexInfo->BaseWidth); |
| |
| pTexInfo->MSAA.NumSamples = GFX_MAX(pTexInfo->MSAA.NumSamples, 1); |
| |
| ExpandedArraySize = |
| GFX_MAX(pTexInfo->ArraySize, 1) * |
| ((pTexInfo->Type == RESOURCE_CUBE) ? 6 : 1) * // Cubemaps simply 6-element, 2D arrays. |
| ((pTexInfo->Flags.Gpu.Depth || pTexInfo->Flags.Gpu.SeparateStencil) ? |
| 1 : |
| pTexInfo->MSAA.NumSamples); // Gen7 MSAA (non-Depth/Stencil) RT samples stored as array planes. |
| |
| // |
| // Check for color separation |
| // |
| if(pTexInfo->Flags.Gpu.ColorSeparation || pTexInfo->Flags.Gpu.ColorSeparationRGBX) |
| { |
| bool csRestrictionsMet = (((ExpandedArraySize <= 2) && |
| (ExpandedArraySize == pTexInfo->ArraySize) && |
| ((pTexInfo->Format == GMM_FORMAT_R8G8B8A8_UNORM) || |
| (pTexInfo->Format == GMM_FORMAT_R8G8B8A8_UNORM_SRGB) || |
| (pTexInfo->Format == GMM_FORMAT_B8G8R8A8_UNORM) || |
| (pTexInfo->Format == GMM_FORMAT_B8G8R8A8_UNORM_SRGB) || |
| (pTexInfo->Format == GMM_FORMAT_B8G8R8X8_UNORM) || |
| (pTexInfo->Format == GMM_FORMAT_B8G8R8X8_UNORM_SRGB)) && |
| ((pTexInfo->Flags.Gpu.ColorSeparation && (Width % 16) == 0) || |
| (pTexInfo->Flags.Gpu.ColorSeparationRGBX && (Width % 12) == 0)))); |
| |
| if(csRestrictionsMet) |
| { |
| ExpandedArraySize = GMM_COLOR_SEPARATION_ARRAY_SIZE; |
| } |
| else |
| { |
| pTexInfo->Flags.Gpu.ColorSeparation = 0; |
| pTexInfo->Flags.Gpu.ColorSeparationRGBX = 0; |
| } |
| } |
| |
| HAlign = pTexInfo->Alignment.HAlign; |
| VAlign = pTexInfo->Alignment.VAlign; |
| GetCompressionBlockDimensions(pTexInfo->Format, &CompressWidth, &CompressHeight, &CompressDepth); |
| |
| Compress = GmmIsCompressed(pTexInfo->Format); |
| |
| ///////////////////////////////// |
| // Calculate Block Surface Height |
| ///////////////////////////////// |
| |
| // Adjust total height for arrayed 2D textures |
| if(ExpandedArraySize > 1) |
| { |
| uint32_t Height0, Height1; |
| |
| Height0 = __GMM_EXPAND_HEIGHT(this, Height, VAlign, pTexInfo); |
| |
| // If not ARYSPC_LOD0-eligible... |
| if((pTexInfo->MaxLod > 0) || |
| pTexInfo->Flags.Gpu.Depth || // Depth/HiZ/Stencil buffers not ARYSPC_LOD0-compatible. |
| pTexInfo->Flags.Gpu.HiZ || |
| pTexInfo->Flags.Gpu.SeparateStencil) |
| { |
| Height1 = __GMM_EXPAND_HEIGHT(this, Height >> 1, VAlign, pTexInfo); |
| |
| // QPitch = (h0 + h1 + 12j) * pitch |
| BlockHeight = Height0 + Height1 + 12 * VAlign; |
| } |
| else // SURFACE_STATE: Surface Array Spacing: ARYSPC_LOD0 |
| { |
| // QPitch = h0 * pitch |
| BlockHeight = Height0; |
| pTexInfo->Alignment.ArraySpacingSingleLod = true; |
| } |
| |
| if(Compress) |
| { |
| BlockHeight /= CompressHeight; |
| } |
| else if(pTexInfo->Flags.Gpu.SeparateStencil) |
| { |
| BlockHeight /= 2; |
| } |
| |
| // Compute total array height |
| BlockHeight *= ExpandedArraySize; |
| } |
| else |
| { |
| BlockHeight = Get2DMipMapHeight(pTexInfo); |
| } |
| |
| |
| /////////////////////////////////// |
| // Calculate Pitch |
| /////////////////////////////////// |
| |
| AlignedWidth = __GMM_EXPAND_WIDTH(this, Width, HAlign, pTexInfo); |
| |
| // Calculate special pitch case of small dimensions where LOD1 + LOD2 widths are greater |
| // than LOD0. e.g. dimensions 4x4 and MinPitch == 1 |
| if(pTexInfo->MaxLod >= 2) |
| { |
| uint32_t AlignedWidthLod1, AlignedWidthLod2; |
| |
| AlignedWidthLod1 = __GMM_EXPAND_WIDTH(this, Width >> 1, HAlign, pTexInfo); |
| AlignedWidthLod2 = __GMM_EXPAND_WIDTH(this, Width >> 2, HAlign, pTexInfo); |
| |
| AlignedWidth = GFX_MAX(AlignedWidth, AlignedWidthLod1 + AlignedWidthLod2); |
| } |
| |
| if(Compress) |
| { |
| AlignedWidth /= CompressWidth; |
| } |
| else if(pTexInfo->Flags.Gpu.SeparateStencil) |
| { |
| AlignedWidth *= 2; |
| } |
| else if(pTexInfo->Flags.Gpu.ColorSeparation) |
| { |
| AlignedWidth *= pTexInfo->ArraySize; |
| __GMM_ASSERT(0 == (AlignedWidth % GMM_COLOR_SEPARATION_WIDTH_DIVISION)); |
| AlignedWidth /= GMM_COLOR_SEPARATION_WIDTH_DIVISION; |
| } |
| else if(pTexInfo->Flags.Gpu.ColorSeparationRGBX) |
| { |
| AlignedWidth *= pTexInfo->ArraySize; |
| __GMM_ASSERT(0 == (AlignedWidth % GMM_COLOR_SEPARATION_RGBX_WIDTH_DIVISION)); |
| AlignedWidth /= GMM_COLOR_SEPARATION_RGBX_WIDTH_DIVISION; |
| } |
| |
| // Default pitch |
| Pitch = AlignedWidth * BitsPerPixel >> 3; |
| |
| // Make sure the pitch satisfy linear min pitch requirment |
| Pitch = GFX_MAX(Pitch, pRestrictions->MinPitch); |
| |
| // Make sure pitch satisfy alignment restriction |
| Pitch = GFX_ALIGN(Pitch, pRestrictions->PitchAlignment); |
| |
| |
| //////////////////// |
| // Adjust for Tiling |
| //////////////////// |
| if(GMM_IS_TILED(pPlatform->TileInfo[pTexInfo->TileMode])) |
| { |
| Pitch = GFX_ALIGN(Pitch, pPlatform->TileInfo[pTexInfo->TileMode].LogicalTileWidth); |
| BlockHeight = GFX_ALIGN(BlockHeight, pPlatform->TileInfo[pTexInfo->TileMode].LogicalTileHeight); |
| |
| // If Tiled Resource or Undefined64KBSwizzle resource, align to 64KB tile size |
| if((pTexInfo->Flags.Gpu.TiledResource || pTexInfo->Flags.Info.Undefined64KBSwizzle) && |
| (pTexInfo->Flags.Info.TiledY)) |
| { |
| uint32_t ColFactor = 0, RowFactor = 0; |
| uint32_t TRTileWidth = 0, TRTileHeight = 0; |
| |
| GmmGetD3DToHwTileConversion(pTexInfo, &ColFactor, &RowFactor); |
| TRTileWidth = pPlatform->TileInfo[pTexInfo->TileMode].LogicalTileWidth * ColFactor; |
| TRTileHeight = pPlatform->TileInfo[pTexInfo->TileMode].LogicalTileHeight * RowFactor; |
| |
| Pitch = GFX_ALIGN(Pitch, TRTileWidth); |
| BlockHeight = GFX_ALIGN(BlockHeight, TRTileHeight); |
| } |
| } |
| |
| GMM_ASSERTDPF(pTexInfo->Flags.Info.LayoutBelow || !pTexInfo->Flags.Info.LayoutRight, "MIPLAYOUT_RIGHT not supported after Gen6!"); |
| pTexInfo->Flags.Info.LayoutBelow = 1; |
| pTexInfo->Flags.Info.LayoutRight = 0; |
| |
| // If a texture is YUV packed, 96, or 48 bpp then one row plus 16 bytes of |
| // padding needs to be added. Since this will create a none pitch aligned |
| // surface the padding is aligned to the next row |
| if(GmmIsYUVPacked(pTexInfo->Format) || |
| (pTexInfo->BitsPerPixel == GMM_BITS(96)) || |
| (pTexInfo->BitsPerPixel == GMM_BITS(48))) |
| { |
| BlockHeight += GMM_SCANLINES(1) + GFX_CEIL_DIV(GMM_BYTES(16), Pitch); |
| } |
| |
| // Align height to even row to cover for HW over - fetch |
| BlockHeight = GFX_ALIGN(BlockHeight, __GMM_EVEN_ROW); |
| |
| if((Status = // <-- Note assignment. |
| FillTexPitchAndSize( |
| pTexInfo, Pitch, BlockHeight, pRestrictions)) == GMM_SUCCESS) |
| { |
| Fill2DTexOffsetAddress(pTexInfo); |
| |
| // Init to no-packed mips. It'll be initialized when app calls to get packed |
| // mips. Calculate packed mips here if there's a chance apps won't call to |
| // get packed mips. |
| pTexInfo->Alignment.PackedMipStartLod = GMM_TILED_RESOURCE_NO_PACKED_MIPS; |
| } |
| |
| GMM_DPF_EXIT; |
| return (Status); |
| } |
| |
| |
| ///////////////////////////////////////////////////////////////////////////////////// |
| /// Calculates the mip offset of given LOD in 2D mip layout |
| /// |
| /// @param[in] pTexInfo: ptr to ::GMM_TEXTURE_INFO, |
| /// |
| /// @return offset value in bytes |
| ///////////////////////////////////////////////////////////////////////////////////// |
| GMM_GFX_SIZE_T GmmLib::GmmGen7TextureCalc::Get2DTexOffsetAddressPerMip(GMM_TEXTURE_INFO *pTexInfo, |
| uint32_t MipLevel) |
| { |
| GMM_GFX_SIZE_T MipOffset; |
| uint32_t AlignedMipHeight, i, MipHeight, OffsetHeight; |
| uint32_t HAlign, VAlign; |
| uint32_t CompressHeight, CompressWidth, CompressDepth; |
| uint8_t Compress; |
| |
| __GMM_ASSERTPTR(pTexInfo, GMM_ERROR); |
| GMM_DPF_ENTER; |
| |
| const GMM_PLATFORM_INFO *pPlatform = GMM_OVERRIDE_PLATFORM_INFO(pTexInfo); |
| |
| HAlign = pTexInfo->Alignment.HAlign; |
| VAlign = pTexInfo->Alignment.VAlign; |
| |
| GetCompressionBlockDimensions(pTexInfo->Format, &CompressWidth, &CompressHeight, &CompressDepth); |
| |
| Compress = GmmIsCompressed(pTexInfo->Format); |
| |
| MipHeight = pTexInfo->BaseHeight; |
| OffsetHeight = 0; |
| |
| // Mips 0 and 1 are on the left edge... |
| if(MipLevel < 2) |
| { |
| MipOffset = 0; |
| } |
| else // Mip2 and beyond are to the right of Mip1... |
| { |
| uint32_t Mip1Width = GFX_ULONG_CAST(pTexInfo->BaseWidth) >> 1; |
| |
| Mip1Width = __GMM_EXPAND_WIDTH(this, Mip1Width, HAlign, pTexInfo); |
| |
| if(Compress) |
| { |
| Mip1Width /= CompressWidth; |
| |
| if((pGmmGlobalContext->GetWaTable().WaAstcCorruptionForOddCompressedBlockSizeX || pTexInfo->Flags.Wa.CHVAstcSkipVirtualMips) && pPlatform->FormatTable[pTexInfo->Format].ASTC && CompressWidth == 5) |
| { |
| uint32_t Width1 = (pTexInfo->BaseWidth == 1) ? 1 : (GFX_ULONG_CAST(pTexInfo->BaseWidth) >> 1); |
| uint32_t Modulo10 = Width1 % 10; |
| if(Modulo10 >= 1 && Modulo10 <= CompressWidth) |
| { |
| Mip1Width += 3; |
| } |
| } |
| } |
| else if(pTexInfo->Flags.Gpu.SeparateStencil) |
| { |
| Mip1Width *= 2; |
| } |
| |
| MipOffset = (GMM_GFX_SIZE_T)Mip1Width * pTexInfo->BitsPerPixel >> 3; |
| } |
| |
| for(i = 1; i <= MipLevel; i++) |
| { |
| AlignedMipHeight = GFX_ULONG_CAST(__GMM_EXPAND_HEIGHT(this, MipHeight, VAlign, pTexInfo)); |
| |
| if(Compress) |
| { |
| AlignedMipHeight /= CompressHeight; |
| } |
| else if(pTexInfo->Flags.Gpu.SeparateStencil) |
| { |
| AlignedMipHeight /= 2; |
| } |
| |
| OffsetHeight += ((i != 2) ? AlignedMipHeight : 0); |
| |
| MipHeight >>= 1; |
| } |
| |
| MipOffset += OffsetHeight * GFX_ULONG_CAST(pTexInfo->Pitch); |
| |
| GMM_DPF_EXIT; |
| return (MipOffset); |
| } |
| |
| |
| ///////////////////////////////////////////////////////////////////////////////////// |
| /// Calculates height of the 2D mip layout |
| /// |
| /// @param[in] pTexInfo: ptr to ::GMM_TEXTURE_INFO, |
| /// |
| /// @return Height of 2D mip layout |
| ///////////////////////////////////////////////////////////////////////////////////// |
| uint32_t GmmLib::GmmGen7TextureCalc::Get2DMipMapHeight(GMM_TEXTURE_INFO *pTexInfo) |
| { |
| uint32_t Height, BlockHeight, NumLevels; // Final height for 2D surface |
| uint32_t HeightLines, HeightLinesLevel0, HeightLinesLevel1, HeightLinesLevel2; |
| uint32_t VAlign, CompressHeight, CompressWidth, CompressDepth; |
| uint32_t i; |
| uint8_t Compress; |
| |
| GMM_DPF_ENTER; |
| |
| const GMM_PLATFORM_INFO *pPlatform = GMM_OVERRIDE_PLATFORM_INFO(pTexInfo); |
| |
| // Mip 0 height is needed later |
| Height = pTexInfo->BaseHeight; |
| Compress = GmmIsCompressed(pTexInfo->Format); |
| NumLevels = pTexInfo->MaxLod; |
| HeightLines = Height; |
| VAlign = pTexInfo->Alignment.VAlign; |
| GetCompressionBlockDimensions(pTexInfo->Format, &CompressWidth, &CompressHeight, &CompressDepth); |
| |
| HeightLinesLevel0 = __GMM_EXPAND_HEIGHT(this, HeightLines, VAlign, pTexInfo); |
| |
| if(Compress) |
| { |
| HeightLinesLevel0 /= CompressHeight; |
| } |
| else if(pTexInfo->Flags.Gpu.SeparateStencil) |
| { |
| HeightLinesLevel0 /= 2; |
| } |
| |
| // Start out with mip0 |
| BlockHeight = HeightLinesLevel0; |
| |
| // Height of mip1 and height of all others mips(2,3,4,5,,) needed later |
| HeightLinesLevel1 = HeightLinesLevel2 = 0; |
| for(i = 1; i <= NumLevels; i++) |
| { |
| uint32_t AlignedHeightLines; |
| |
| HeightLines >>= 1; |
| |
| AlignedHeightLines = __GMM_EXPAND_HEIGHT(this, HeightLines, VAlign, pTexInfo); |
| |
| if(Compress) |
| { |
| AlignedHeightLines /= CompressHeight; |
| } |
| else if(pTexInfo->Flags.Gpu.SeparateStencil) |
| { |
| AlignedHeightLines /= 2; |
| } |
| |
| if(i == 1) |
| { |
| HeightLinesLevel1 = AlignedHeightLines; |
| } |
| else |
| { |
| HeightLinesLevel2 += AlignedHeightLines; |
| } |
| } |
| |
| // If mip1 height covers all others then that is all we need |
| if(HeightLinesLevel1 >= HeightLinesLevel2) |
| { |
| BlockHeight += GFX_ALIGN_NP2(HeightLinesLevel1, VAlign); |
| } |
| else |
| { |
| BlockHeight += GFX_ALIGN_NP2(HeightLinesLevel2, VAlign); |
| } |
| |
| GMM_DPF_EXIT; |
| return (BlockHeight); |
| } |
| |
| |
| ///////////////////////////////////////////////////////////////////////////////////// |
| /// Calculates the address offset for each mip map of 2D texture and store them |
| /// into the GMM_TEXTURE_INFO for surf state programming. |
| /// |
| /// @param[in] pTexInfo: pointer to ::GMM_TEXTURE_INFO |
| /// |
| ///////////////////////////////////////////////////////////////////////////////////// |
| void GmmLib::GmmGen7TextureCalc::Fill2DTexOffsetAddress(GMM_TEXTURE_INFO *pTexInfo) |
| { |
| uint32_t i; |
| |
| __GMM_ASSERTPTR(pTexInfo, VOIDRETURN); |
| |
| GMM_DPF_ENTER; |
| |
| // QPitch: Array Element-to-Element, or Cube Face-to-Face Pitch... |
| // ------------------------------------------------------------------------- |
| // Note: Gen7 MSAA RT samples stored as contiguous array planes-- |
| // e.g. MSAA-4X'ed array elements A and B stored: A0 A1 A2 A3 B0 B1 B2 B3. |
| // However, for GMM's purposes QPitch is still the distance between |
| // elements--not the distance between samples. |
| // ------------------------------------------------------------------------- |
| if((pTexInfo->ArraySize <= 1) && |
| (pTexInfo->Type != RESOURCE_CUBE) && |
| !(pTexInfo->Flags.Gpu.ColorSeparation || |
| pTexInfo->Flags.Gpu.ColorSeparationRGBX)) |
| { |
| pTexInfo->OffsetInfo.Texture2DOffsetInfo.ArrayQPitchRender = 0; |
| pTexInfo->OffsetInfo.Texture2DOffsetInfo.ArrayQPitchLock = 0; |
| } |
| else |
| { |
| uint32_t Height, Height0, Height1, ArrayQPitch, VAlign; |
| |
| Height = pTexInfo->BaseHeight; |
| |
| VAlign = pTexInfo->Alignment.VAlign; |
| |
| Height0 = __GMM_EXPAND_HEIGHT(this, Height, VAlign, pTexInfo); |
| Height1 = __GMM_EXPAND_HEIGHT(this, Height >> 1, VAlign, pTexInfo); |
| |
| if(!pTexInfo->Alignment.ArraySpacingSingleLod) |
| { |
| // QPitch = (h0 + h1 + 12j) * pitch |
| ArrayQPitch = Height0 + Height1 + 12 * VAlign; |
| } |
| else // SURFACE_STATE: Surface Array Spacing: ARYSPC_LOD0 |
| { |
| // QPitch = h0 * pitch |
| ArrayQPitch = Height0; |
| } |
| |
| if(GmmIsCompressed(pTexInfo->Format)) |
| { |
| uint32_t CompressHeight, CompressWidth, CompressDepth; |
| |
| GetCompressionBlockDimensions(pTexInfo->Format, &CompressWidth, &CompressHeight, &CompressDepth); |
| |
| ArrayQPitch /= CompressHeight; |
| } |
| else if(pTexInfo->Flags.Gpu.SeparateStencil) |
| { |
| ArrayQPitch /= 2; |
| } |
| |
| if((pTexInfo->MSAA.NumSamples > 1) && !(pTexInfo->Flags.Gpu.Depth || pTexInfo->Flags.Gpu.SeparateStencil)) |
| { |
| // Gen7 MSAA (non-Depth/Stencil) RT samples stored as array planes; |
| // QPitch still element-to-element, not sample-to-sample. |
| ArrayQPitch *= pTexInfo->MSAA.NumSamples; |
| } |
| |
| pTexInfo->OffsetInfo.Texture2DOffsetInfo.ArrayQPitchRender = ArrayQPitch * pTexInfo->Pitch; |
| pTexInfo->OffsetInfo.Texture2DOffsetInfo.ArrayQPitchLock = ArrayQPitch * pTexInfo->Pitch; |
| } |
| |
| for(i = 0; i <= pTexInfo->MaxLod; i++) |
| { |
| pTexInfo->OffsetInfo.Texture2DOffsetInfo.Offset[i] = Get2DTexOffsetAddressPerMip(pTexInfo, i); |
| } |
| |
| GMM_DPF_EXIT; |
| } |
| |
| |
| ///////////////////////////////////////////////////////////////////////////////////// |
| /// Calculates total height of an arrayed 3D mip layout |
| /// |
| /// @param[in] pTexInfo: ptr to ::GMM_TEXTURE_INFO, |
| /// |
| /// @return height of arrayed 3D mip layout |
| ///////////////////////////////////////////////////////////////////////////////////// |
| uint32_t GmmLib::GmmGen7TextureCalc::GetTotal3DHeight(GMM_TEXTURE_INFO *pTexInfo) |
| { |
| uint32_t AlignedHeight, BlockHeight, Depth; |
| uint8_t Compressed; |
| uint32_t i, MipsInThisRow, MipLevel, MipRows; |
| uint32_t Total3DHeight = 0, UnitAlignHeight; |
| uint32_t CompressHeight, CompressWidth, CompressDepth; |
| GMM_TEXTURE_CALC *pTextureCalc; |
| |
| GMM_DPF_ENTER; |
| |
| pTextureCalc = GMM_OVERRIDE_TEXTURE_CALC(pTexInfo); |
| |
| BlockHeight = pTexInfo->BaseHeight; |
| Depth = pTexInfo->Depth; |
| MipLevel = pTexInfo->MaxLod; |
| UnitAlignHeight = pTexInfo->Alignment.VAlign; |
| |
| Compressed = GmmIsCompressed(pTexInfo->Format); |
| pTextureCalc->GetCompressionBlockDimensions(pTexInfo->Format, &CompressWidth, &CompressHeight, &CompressDepth); |
| |
| // All mip0s of all planes are stacked together, then mip1s and so on... |
| for(i = 0; i <= MipLevel; i++) |
| { |
| BlockHeight = GFX_MAX(BlockHeight, UnitAlignHeight); |
| AlignedHeight = GFX_ALIGN(BlockHeight, UnitAlignHeight); |
| |
| if(Compressed) |
| { |
| AlignedHeight /= CompressHeight; |
| } |
| else if(pTexInfo->Flags.Gpu.SeparateStencil) |
| { |
| AlignedHeight /= 2; |
| } |
| else if(pTexInfo->Flags.Gpu.CCS) |
| { |
| if(pTexInfo->Flags.Gpu.__NonMsaaTileYCcs) |
| { |
| AlignedHeight /= 32; |
| } |
| else if(pTexInfo->Flags.Gpu.__NonMsaaTileXCcs) |
| { |
| AlignedHeight /= 16; |
| } |
| } |
| |
| // See how many mip can fit in one row |
| MipsInThisRow = GFX_2_TO_POWER_OF(i); |
| |
| // calculate if the mips will spill over to multiple rows |
| MipRows = GFX_CEIL_DIV(GFX_MAX(1, Depth >> i), MipsInThisRow); |
| |
| Total3DHeight += MipRows * AlignedHeight; |
| |
| // next level height |
| BlockHeight >>= 1; |
| } |
| |
| GMM_DPF_EXIT; |
| return Total3DHeight; |
| } |
| |
| |
| ///////////////////////////////////////////////////////////////////////////////////// |
| /// Calculates the address offset for each mip map of 3D texture and store them |
| /// into the GMM_TEXTURE_INFO for surf state programming. |
| /// |
| /// @param[in] pTexInfo: ptr to ::GMM_TEXTURE_INFO, |
| /// |
| ///////////////////////////////////////////////////////////////////////////////////// |
| void GmmLib::GmmGen7TextureCalc::Fill3DTexOffsetAddress(GMM_TEXTURE_INFO *pTexInfo) |
| { |
| uint32_t AlignedMipHeight, AlignedMipWidth; |
| uint32_t i, Depth; |
| uint32_t MipsInThisRow, MipLevel, MipRows; |
| uint32_t MipHeight, MipWidth; |
| uint32_t UnitAlignHeight, UnitAlignWidth; |
| uint32_t CompressHeight, CompressWidth, CompressDepth; |
| uint32_t OffsetMipRows = 0; |
| GMM_GFX_SIZE_T OffsetValue; |
| uint8_t Compress; |
| GMM_TEXTURE_CALC *pTextureCalc; |
| |
| __GMM_ASSERT(pTexInfo); |
| |
| pTextureCalc = GMM_OVERRIDE_TEXTURE_CALC(pTexInfo); |
| |
| // Assign directly to unaligned MipMap dimension variables |
| // There isn't a need to save original dimensions |
| MipWidth = GFX_ULONG_CAST(pTexInfo->BaseWidth); |
| MipHeight = pTexInfo->BaseHeight; |
| |
| // Align before we compress |
| UnitAlignWidth = pTexInfo->Alignment.HAlign; |
| UnitAlignHeight = pTexInfo->Alignment.VAlign; |
| Compress = GmmIsCompressed(pTexInfo->Format); |
| pTextureCalc->GetCompressionBlockDimensions(pTexInfo->Format, &CompressWidth, &CompressHeight, &CompressDepth); |
| |
| // Aligned MipMap Dimensions |
| AlignedMipWidth = GFX_ALIGN(MipWidth, UnitAlignWidth); |
| AlignedMipHeight = GFX_ALIGN(MipHeight, UnitAlignHeight); |
| |
| Depth = pTexInfo->Depth; |
| MipLevel = pTexInfo->MaxLod; |
| |
| // Start with base offset |
| OffsetValue = 0; |
| |
| // calculate the offset for each Mip level |
| for(i = 0; i <= MipLevel; i++) |
| { |
| // store the value in blockdesc |
| if(Compress) |
| { |
| // If there is compression, compress after the alignment at each level |
| AlignedMipWidth /= CompressWidth; |
| AlignedMipHeight /= CompressHeight; |
| } |
| else if(pTexInfo->Flags.Gpu.SeparateStencil) |
| { |
| AlignedMipWidth *= 2; |
| AlignedMipHeight /= 2; |
| } |
| else if(pTexInfo->Flags.Gpu.CCS) |
| { |
| if(pTexInfo->Flags.Gpu.__NonMsaaTileYCcs) |
| { |
| switch(pTexInfo->BitsPerPixel) |
| { |
| case 32: |
| AlignedMipWidth /= 8; |
| break; |
| case 64: |
| AlignedMipWidth /= 4; |
| break; |
| case 128: |
| AlignedMipWidth /= 2; |
| break; |
| default: |
| __GMM_ASSERT(0); |
| } |
| |
| AlignedMipHeight /= 32; |
| } |
| else if(pTexInfo->Flags.Gpu.__NonMsaaTileXCcs) |
| { |
| switch(pTexInfo->BitsPerPixel) |
| { |
| case 32: |
| AlignedMipWidth /= 16; |
| break; |
| case 64: |
| AlignedMipWidth /= 8; |
| break; |
| case 128: |
| AlignedMipWidth /= 4; |
| break; |
| default: |
| __GMM_ASSERT(0); |
| } |
| |
| AlignedMipHeight /= 16; |
| } |
| } |
| |
| pTexInfo->OffsetInfo.Texture3DOffsetInfo.Offset[i] = OffsetValue; |
| |
| // See how many mip can fit in one row |
| MipsInThisRow = GFX_2_TO_POWER_OF(i); |
| |
| // Slice pitch for LOD0 |
| if(MipsInThisRow == 1) |
| { |
| pTexInfo->OffsetInfo.Texture3DOffsetInfo.Mip0SlicePitch = |
| AlignedMipHeight * pTexInfo->Pitch; |
| } |
| |
| // calculate if the mips will spill over to multiple rows |
| MipRows = GFX_CEIL_DIV(GFX_MAX(1, Depth >> i), MipsInThisRow); |
| |
| // Offset in terms of height |
| OffsetMipRows += MipRows * AlignedMipHeight; |
| |
| // For a particular mip This is offset of a base slice (i.e. Slice 0) |
| OffsetValue = OffsetMipRows * pTexInfo->Pitch; |
| |
| // next level height |
| MipHeight >>= 1; |
| // Clamp such that mip height is at least1 |
| MipHeight = GFX_MAX(MipHeight, 1); |
| AlignedMipHeight = GFX_ALIGN(MipHeight, UnitAlignHeight); |
| |
| MipWidth >>= 1; |
| // Clamp such that mip width is at least 1 |
| MipWidth = GFX_MAX(MipWidth, 1); |
| AlignedMipWidth = GFX_ALIGN(MipWidth, UnitAlignWidth); |
| } |
| } |
| |
| ///////////////////////////////////////////////////////////////////////////////////// |
| /// Calculates the 3D offset and QPitch for surface state programming. |
| /// |
| /// @param[in] pTexInfo: ptr to ::GMM_TEXTURE_INFO, |
| /// @param[in] pRestrictions: ptr to surface alignment and size restrictions |
| /// |
| /// @return ::GMM_STATUS |
| ///////////////////////////////////////////////////////////////////////////////////// |
| GMM_STATUS GMM_STDCALL GmmLib::GmmGen7TextureCalc::FillTex3D(GMM_TEXTURE_INFO * pTexInfo, |
| __GMM_BUFFER_TYPE *pRestrictions) |
| { |
| uint32_t AlignedMipWidth; |
| uint32_t BitsPerPixel; |
| uint32_t Depth, Height, Width; |
| uint32_t i, MipsInThisRow, MipWidth; |
| uint32_t RenderPitch = 0, ThisRowPitch; |
| uint32_t UnitAlignWidth; |
| uint32_t Total3DHeight; |
| uint32_t WidthBytesPhysical; |
| uint8_t Compress; |
| uint32_t CompressHeight, CompressWidth, CompressDepth; |
| bool SeparateStencil; |
| GMM_STATUS Status; |
| |
| GMM_DPF_ENTER; |
| |
| __GMM_ASSERTPTR(pTexInfo, GMM_ERROR); |
| __GMM_ASSERTPTR(pRestrictions, GMM_ERROR); |
| |
| const __GMM_PLATFORM_RESOURCE *pPlatformResource = GMM_OVERRIDE_PLATFORM_INFO(pTexInfo); |
| |
| BitsPerPixel = pTexInfo->BitsPerPixel; |
| Height = pTexInfo->BaseHeight; |
| Width = GFX_ULONG_CAST(pTexInfo->BaseWidth); |
| Depth = pTexInfo->Depth; |
| |
| // Align before we compress |
| UnitAlignWidth = pTexInfo->Alignment.HAlign; |
| Compress = GmmIsCompressed(pTexInfo->Format); |
| GetCompressionBlockDimensions(pTexInfo->Format, &CompressWidth, &CompressHeight, &CompressDepth); |
| SeparateStencil = pTexInfo->Flags.Gpu.SeparateStencil ? true : false; |
| |
| // Unaligned MipMap dimension variables |
| MipWidth = Width; |
| |
| // Aligned MipMap dimension variables |
| AlignedMipWidth = GFX_ALIGN(MipWidth, UnitAlignWidth); |
| |
| // Calculate the render pitch exactly the same way we do the |
| // offset for each Mip level |
| for(i = 0; i <= pTexInfo->MaxLod; i++) |
| { |
| if(Compress) |
| { |
| // If there is compression, compress after the alignment at each level |
| AlignedMipWidth /= CompressWidth; |
| } |
| else if(SeparateStencil) |
| { |
| AlignedMipWidth *= 2; |
| } |
| else if(pTexInfo->Flags.Gpu.CCS) |
| { |
| if(pTexInfo->Flags.Gpu.__NonMsaaTileYCcs) |
| { |
| switch(BitsPerPixel) |
| { |
| case 32: |
| AlignedMipWidth /= 8; |
| break; |
| case 64: |
| AlignedMipWidth /= 4; |
| break; |
| case 128: |
| AlignedMipWidth /= 2; |
| break; |
| default: |
| __GMM_ASSERT(0); |
| } |
| } |
| else if(pTexInfo->Flags.Gpu.__NonMsaaTileXCcs) |
| { |
| switch(BitsPerPixel) |
| { |
| case 32: |
| AlignedMipWidth /= 16; |
| break; |
| case 64: |
| AlignedMipWidth /= 8; |
| break; |
| case 128: |
| AlignedMipWidth /= 4; |
| break; |
| default: |
| __GMM_ASSERT(0); |
| } |
| } |
| } |
| |
| // See how many mip can fit in one row |
| MipsInThisRow = GFX_2_TO_POWER_OF(i); |
| |
| // LOD planes may be less than MipsInThisRow, take the smaller value for pitch |
| MipsInThisRow = GFX_MIN(GFX_MAX(1, (Depth >> i)), MipsInThisRow); |
| ThisRowPitch = AlignedMipWidth * MipsInThisRow; |
| |
| // Default pitch |
| WidthBytesPhysical = ThisRowPitch * BitsPerPixel >> 3; |
| |
| if(RenderPitch < WidthBytesPhysical) |
| { |
| RenderPitch = WidthBytesPhysical; |
| } |
| |
| MipWidth >>= 1; |
| // Clamp such that mip width is at least 1 |
| MipWidth = GFX_MAX(MipWidth, 1); |
| AlignedMipWidth = GFX_ALIGN(MipWidth, UnitAlignWidth); |
| } |
| |
| WidthBytesPhysical = RenderPitch; |
| |
| // Make sure the pitch satisfy linear min pitch requirment |
| WidthBytesPhysical = GFX_MAX(WidthBytesPhysical, |
| pRestrictions->MinPitch); |
| |
| // Make sure pitch satisfy alignment restriction |
| WidthBytesPhysical = GFX_ALIGN(WidthBytesPhysical, |
| pRestrictions->PitchAlignment); |
| |
| // Get Total height for the entire 3D texture |
| Total3DHeight = GetTotal3DHeight(pTexInfo); |
| |
| if(GMM_IS_TILED(pPlatformResource->TileInfo[pTexInfo->TileMode])) |
| { |
| // Align to tile boundary |
| Total3DHeight = GFX_ALIGN(Total3DHeight, |
| pPlatformResource->TileInfo[pTexInfo->TileMode].LogicalTileHeight); |
| WidthBytesPhysical = GFX_ALIGN(WidthBytesPhysical, |
| pPlatformResource->TileInfo[pTexInfo->TileMode].LogicalTileWidth); |
| } |
| |
| // If a texture is YUV packed, 96, or 48 bpp then one row plus 16 bytes of |
| // padding needs to be added. Since this will create a none pitch aligned |
| // surface the padding is aligned to the next row |
| if(GmmIsYUVPacked(pTexInfo->Format) || |
| (pTexInfo->BitsPerPixel == GMM_BITS(96)) || |
| (pTexInfo->BitsPerPixel == GMM_BITS(48))) |
| { |
| Total3DHeight += GMM_SCANLINES(1) + GFX_CEIL_DIV(GMM_BYTES(16), WidthBytesPhysical); |
| } |
| |
| Total3DHeight = GFX_ALIGN(Total3DHeight, __GMM_EVEN_ROW); |
| |
| if((Status = // <-- Note assignment. |
| FillTexPitchAndSize( |
| pTexInfo, WidthBytesPhysical, Total3DHeight, pRestrictions)) == GMM_SUCCESS) |
| { |
| Fill3DTexOffsetAddress(pTexInfo); |
| } |
| |
| GMM_DPF_EXIT; |
| return (Status); |
| } |
| |
| ///////////////////////////////////////////////////////////////////////////////////// |
| /// Allocates the 1D mip layout for surface state programming. |
| /// |
| /// @param[in] pTexInfo: ptr to ::GMM_TEXTURE_INFO, |
| /// @param[in] pRestrictions: ptr to surface alignment and size restrictions |
| /// |
| /// @return ::GMM_STATUS |
| ///////////////////////////////////////////////////////////////////////////////////// |
| GMM_STATUS GMM_STDCALL GmmLib::GmmGen7TextureCalc::FillTex1D(GMM_TEXTURE_INFO * pTexInfo, |
| __GMM_BUFFER_TYPE *pRestrictions) |
| { |
| return FillTex2D(pTexInfo, pRestrictions); |
| } |
| |
| ///////////////////////////////////////////////////////////////////////////////////// |
| /// Calculates the cube layout for surface state programming. |
| /// |
| /// @param[in] pTexInfo: ptr to ::GMM_TEXTURE_INFO, |
| /// @param[in] pRestrictions: ptr to surface alignment and size restrictions |
| /// |
| /// @return ::GMM_STATUS |
| ///////////////////////////////////////////////////////////////////////////////////// |
| GMM_STATUS GMM_STDCALL GmmLib::GmmGen7TextureCalc::FillTexCube(GMM_TEXTURE_INFO * pTexInfo, |
| __GMM_BUFFER_TYPE *pRestrictions) |
| { |
| return FillTex2D(pTexInfo, pRestrictions); |
| } |