| /* |
| * Copyright © 2016 Red Hat. |
| * Copyright © 2016 Bas Nieuwenhuizen |
| * |
| * based on amdgpu winsys. |
| * Copyright © 2011 Marek Olšák <maraeo@gmail.com> |
| * Copyright © 2015 Advanced Micro Devices, Inc. |
| * |
| * 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 (including the next |
| * paragraph) 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 <errno.h> |
| #include "radv_private.h" |
| #include "addrlib/addrinterface.h" |
| #include "util/bitset.h" |
| #include "radv_amdgpu_winsys.h" |
| #include "radv_amdgpu_surface.h" |
| #include "sid.h" |
| #ifndef NO_ENTRIES |
| #define NO_ENTRIES 32 |
| #endif |
| |
| #ifndef NO_MACRO_ENTRIES |
| #define NO_MACRO_ENTRIES 16 |
| #endif |
| |
| #ifndef CIASICIDGFXENGINE_SOUTHERNISLAND |
| #define CIASICIDGFXENGINE_SOUTHERNISLAND 0x0000000A |
| #endif |
| |
| static int radv_amdgpu_surface_sanity(const struct radeon_surf *surf) |
| { |
| unsigned type = RADEON_SURF_GET(surf->flags, TYPE); |
| |
| if (!(surf->flags & RADEON_SURF_HAS_TILE_MODE_INDEX)) |
| return -EINVAL; |
| |
| /* all dimension must be at least 1 ! */ |
| if (!surf->npix_x || !surf->npix_y || !surf->npix_z || |
| !surf->array_size) |
| return -EINVAL; |
| |
| if (!surf->blk_w || !surf->blk_h || !surf->blk_d) |
| return -EINVAL; |
| |
| switch (surf->nsamples) { |
| case 1: |
| case 2: |
| case 4: |
| case 8: |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| switch (type) { |
| case RADEON_SURF_TYPE_1D: |
| if (surf->npix_y > 1) |
| return -EINVAL; |
| /* fall through */ |
| case RADEON_SURF_TYPE_2D: |
| case RADEON_SURF_TYPE_CUBEMAP: |
| if (surf->npix_z > 1 || surf->array_size > 1) |
| return -EINVAL; |
| break; |
| case RADEON_SURF_TYPE_3D: |
| if (surf->array_size > 1) |
| return -EINVAL; |
| break; |
| case RADEON_SURF_TYPE_1D_ARRAY: |
| if (surf->npix_y > 1) |
| return -EINVAL; |
| /* fall through */ |
| case RADEON_SURF_TYPE_2D_ARRAY: |
| if (surf->npix_z > 1) |
| return -EINVAL; |
| break; |
| default: |
| return -EINVAL; |
| } |
| return 0; |
| } |
| |
| static void *ADDR_API radv_allocSysMem(const ADDR_ALLOCSYSMEM_INPUT * pInput) |
| { |
| return malloc(pInput->sizeInBytes); |
| } |
| |
| static ADDR_E_RETURNCODE ADDR_API radv_freeSysMem(const ADDR_FREESYSMEM_INPUT * pInput) |
| { |
| free(pInput->pVirtAddr); |
| return ADDR_OK; |
| } |
| |
| ADDR_HANDLE radv_amdgpu_addr_create(struct amdgpu_gpu_info *amdinfo, int family, int rev_id, |
| enum chip_class chip_class) |
| { |
| ADDR_CREATE_INPUT addrCreateInput = {0}; |
| ADDR_CREATE_OUTPUT addrCreateOutput = {0}; |
| ADDR_REGISTER_VALUE regValue = {0}; |
| ADDR_CREATE_FLAGS createFlags = {{0}}; |
| ADDR_E_RETURNCODE addrRet; |
| |
| addrCreateInput.size = sizeof(ADDR_CREATE_INPUT); |
| addrCreateOutput.size = sizeof(ADDR_CREATE_OUTPUT); |
| |
| regValue.noOfBanks = amdinfo->mc_arb_ramcfg & 0x3; |
| regValue.gbAddrConfig = amdinfo->gb_addr_cfg; |
| regValue.noOfRanks = (amdinfo->mc_arb_ramcfg & 0x4) >> 2; |
| |
| regValue.backendDisables = amdinfo->backend_disable[0]; |
| regValue.pTileConfig = amdinfo->gb_tile_mode; |
| regValue.noOfEntries = ARRAY_SIZE(amdinfo->gb_tile_mode); |
| if (chip_class == SI) { |
| regValue.pMacroTileConfig = NULL; |
| regValue.noOfMacroEntries = 0; |
| } else { |
| regValue.pMacroTileConfig = amdinfo->gb_macro_tile_mode; |
| regValue.noOfMacroEntries = ARRAY_SIZE(amdinfo->gb_macro_tile_mode); |
| } |
| |
| createFlags.value = 0; |
| createFlags.useTileIndex = 1; |
| createFlags.degradeBaseLevel = 1; |
| |
| addrCreateInput.chipEngine = CIASICIDGFXENGINE_SOUTHERNISLAND; |
| addrCreateInput.chipFamily = family; |
| addrCreateInput.chipRevision = rev_id; |
| addrCreateInput.createFlags = createFlags; |
| addrCreateInput.callbacks.allocSysMem = radv_allocSysMem; |
| addrCreateInput.callbacks.freeSysMem = radv_freeSysMem; |
| addrCreateInput.callbacks.debugPrint = 0; |
| addrCreateInput.regValue = regValue; |
| |
| addrRet = AddrCreate(&addrCreateInput, &addrCreateOutput); |
| if (addrRet != ADDR_OK) |
| return NULL; |
| |
| return addrCreateOutput.hLib; |
| } |
| |
| static int radv_compute_level(ADDR_HANDLE addrlib, |
| struct radeon_surf *surf, bool is_stencil, |
| unsigned level, unsigned type, bool compressed, |
| ADDR_COMPUTE_SURFACE_INFO_INPUT *AddrSurfInfoIn, |
| ADDR_COMPUTE_SURFACE_INFO_OUTPUT *AddrSurfInfoOut, |
| ADDR_COMPUTE_DCCINFO_INPUT *AddrDccIn, |
| ADDR_COMPUTE_DCCINFO_OUTPUT *AddrDccOut) |
| { |
| struct radeon_surf_level *surf_level; |
| ADDR_E_RETURNCODE ret; |
| |
| AddrSurfInfoIn->mipLevel = level; |
| AddrSurfInfoIn->width = u_minify(surf->npix_x, level); |
| AddrSurfInfoIn->height = u_minify(surf->npix_y, level); |
| |
| if (type == RADEON_SURF_TYPE_3D) |
| AddrSurfInfoIn->numSlices = u_minify(surf->npix_z, level); |
| else if (type == RADEON_SURF_TYPE_CUBEMAP) |
| AddrSurfInfoIn->numSlices = 6; |
| else |
| AddrSurfInfoIn->numSlices = surf->array_size; |
| |
| if (level > 0) { |
| /* Set the base level pitch. This is needed for calculation |
| * of non-zero levels. */ |
| if (is_stencil) |
| AddrSurfInfoIn->basePitch = surf->stencil_level[0].nblk_x; |
| else |
| AddrSurfInfoIn->basePitch = surf->level[0].nblk_x; |
| |
| /* Convert blocks to pixels for compressed formats. */ |
| if (compressed) |
| AddrSurfInfoIn->basePitch *= surf->blk_w; |
| } |
| |
| ret = AddrComputeSurfaceInfo(addrlib, |
| AddrSurfInfoIn, |
| AddrSurfInfoOut); |
| if (ret != ADDR_OK) { |
| return ret; |
| } |
| |
| surf_level = is_stencil ? &surf->stencil_level[level] : &surf->level[level]; |
| surf_level->offset = align64(surf->bo_size, AddrSurfInfoOut->baseAlign); |
| surf_level->slice_size = AddrSurfInfoOut->sliceSize; |
| surf_level->pitch_bytes = AddrSurfInfoOut->pitch * (is_stencil ? 1 : surf->bpe); |
| surf_level->npix_x = u_minify(surf->npix_x, level); |
| surf_level->npix_y = u_minify(surf->npix_y, level); |
| surf_level->npix_z = u_minify(surf->npix_z, level); |
| surf_level->nblk_x = AddrSurfInfoOut->pitch; |
| surf_level->nblk_y = AddrSurfInfoOut->height; |
| if (type == RADEON_SURF_TYPE_3D) |
| surf_level->nblk_z = AddrSurfInfoOut->depth; |
| else |
| surf_level->nblk_z = 1; |
| |
| switch (AddrSurfInfoOut->tileMode) { |
| case ADDR_TM_LINEAR_ALIGNED: |
| surf_level->mode = RADEON_SURF_MODE_LINEAR_ALIGNED; |
| break; |
| case ADDR_TM_1D_TILED_THIN1: |
| surf_level->mode = RADEON_SURF_MODE_1D; |
| break; |
| case ADDR_TM_2D_TILED_THIN1: |
| surf_level->mode = RADEON_SURF_MODE_2D; |
| break; |
| default: |
| assert(0); |
| } |
| |
| if (is_stencil) |
| surf->stencil_tiling_index[level] = AddrSurfInfoOut->tileIndex; |
| else |
| surf->tiling_index[level] = AddrSurfInfoOut->tileIndex; |
| |
| surf->bo_size = surf_level->offset + AddrSurfInfoOut->surfSize; |
| |
| /* Clear DCC fields at the beginning. */ |
| surf_level->dcc_offset = 0; |
| surf_level->dcc_enabled = false; |
| |
| /* The previous level's flag tells us if we can use DCC for this level. */ |
| if (AddrSurfInfoIn->flags.dccCompatible && |
| (level == 0 || AddrDccOut->subLvlCompressible)) { |
| AddrDccIn->colorSurfSize = AddrSurfInfoOut->surfSize; |
| AddrDccIn->tileMode = AddrSurfInfoOut->tileMode; |
| AddrDccIn->tileInfo = *AddrSurfInfoOut->pTileInfo; |
| AddrDccIn->tileIndex = AddrSurfInfoOut->tileIndex; |
| AddrDccIn->macroModeIndex = AddrSurfInfoOut->macroModeIndex; |
| |
| ret = AddrComputeDccInfo(addrlib, |
| AddrDccIn, |
| AddrDccOut); |
| |
| if (ret == ADDR_OK) { |
| surf_level->dcc_offset = surf->dcc_size; |
| surf_level->dcc_fast_clear_size = AddrDccOut->dccFastClearSize; |
| surf_level->dcc_enabled = true; |
| surf->dcc_size = surf_level->dcc_offset + AddrDccOut->dccRamSize; |
| surf->dcc_alignment = MAX(surf->dcc_alignment, AddrDccOut->dccRamBaseAlign); |
| } |
| } |
| |
| return 0; |
| } |
| |
| static void radv_set_micro_tile_mode(struct radeon_surf *surf, |
| struct radeon_info *info) |
| { |
| uint32_t tile_mode = info->si_tile_mode_array[surf->tiling_index[0]]; |
| |
| if (info->chip_class >= CIK) |
| surf->micro_tile_mode = G_009910_MICRO_TILE_MODE_NEW(tile_mode); |
| else |
| surf->micro_tile_mode = G_009910_MICRO_TILE_MODE(tile_mode); |
| } |
| |
| |
| static int radv_amdgpu_winsys_surface_init(struct radeon_winsys *_ws, |
| struct radeon_surf *surf) |
| { |
| struct radv_amdgpu_winsys *ws = radv_amdgpu_winsys(_ws); |
| unsigned level, mode, type; |
| bool compressed; |
| ADDR_COMPUTE_SURFACE_INFO_INPUT AddrSurfInfoIn = {0}; |
| ADDR_COMPUTE_SURFACE_INFO_OUTPUT AddrSurfInfoOut = {0}; |
| ADDR_COMPUTE_DCCINFO_INPUT AddrDccIn = {0}; |
| ADDR_COMPUTE_DCCINFO_OUTPUT AddrDccOut = {0}; |
| ADDR_TILEINFO AddrTileInfoIn = {0}; |
| ADDR_TILEINFO AddrTileInfoOut = {0}; |
| int r; |
| |
| r = radv_amdgpu_surface_sanity(surf); |
| if (r) |
| return r; |
| |
| AddrSurfInfoIn.size = sizeof(ADDR_COMPUTE_SURFACE_INFO_INPUT); |
| AddrSurfInfoOut.size = sizeof(ADDR_COMPUTE_SURFACE_INFO_OUTPUT); |
| AddrDccIn.size = sizeof(ADDR_COMPUTE_DCCINFO_INPUT); |
| AddrDccOut.size = sizeof(ADDR_COMPUTE_DCCINFO_OUTPUT); |
| AddrSurfInfoOut.pTileInfo = &AddrTileInfoOut; |
| |
| type = RADEON_SURF_GET(surf->flags, TYPE); |
| mode = RADEON_SURF_GET(surf->flags, MODE); |
| compressed = surf->blk_w == 4 && surf->blk_h == 4; |
| |
| /* MSAA and FMASK require 2D tiling. */ |
| if (surf->nsamples > 1 || |
| (surf->flags & RADEON_SURF_FMASK)) |
| mode = RADEON_SURF_MODE_2D; |
| |
| /* DB doesn't support linear layouts. */ |
| if (surf->flags & (RADEON_SURF_Z_OR_SBUFFER) && |
| mode < RADEON_SURF_MODE_1D) |
| mode = RADEON_SURF_MODE_1D; |
| |
| /* Set the requested tiling mode. */ |
| switch (mode) { |
| case RADEON_SURF_MODE_LINEAR_ALIGNED: |
| AddrSurfInfoIn.tileMode = ADDR_TM_LINEAR_ALIGNED; |
| break; |
| case RADEON_SURF_MODE_1D: |
| AddrSurfInfoIn.tileMode = ADDR_TM_1D_TILED_THIN1; |
| break; |
| case RADEON_SURF_MODE_2D: |
| AddrSurfInfoIn.tileMode = ADDR_TM_2D_TILED_THIN1; |
| break; |
| default: |
| assert(0); |
| } |
| |
| /* The format must be set correctly for the allocation of compressed |
| * textures to work. In other cases, setting the bpp is sufficient. */ |
| if (compressed) { |
| switch (surf->bpe) { |
| case 8: |
| AddrSurfInfoIn.format = ADDR_FMT_BC1; |
| break; |
| case 16: |
| AddrSurfInfoIn.format = ADDR_FMT_BC3; |
| break; |
| default: |
| assert(0); |
| } |
| } |
| else { |
| AddrDccIn.bpp = AddrSurfInfoIn.bpp = surf->bpe * 8; |
| } |
| |
| AddrDccIn.numSamples = AddrSurfInfoIn.numSamples = surf->nsamples; |
| AddrSurfInfoIn.tileIndex = -1; |
| |
| /* Set the micro tile type. */ |
| if (surf->flags & RADEON_SURF_SCANOUT) |
| AddrSurfInfoIn.tileType = ADDR_DISPLAYABLE; |
| else if (surf->flags & RADEON_SURF_Z_OR_SBUFFER) |
| AddrSurfInfoIn.tileType = ADDR_DEPTH_SAMPLE_ORDER; |
| else |
| AddrSurfInfoIn.tileType = ADDR_NON_DISPLAYABLE; |
| |
| AddrSurfInfoIn.flags.color = !(surf->flags & RADEON_SURF_Z_OR_SBUFFER); |
| AddrSurfInfoIn.flags.depth = (surf->flags & RADEON_SURF_ZBUFFER) != 0; |
| AddrSurfInfoIn.flags.cube = type == RADEON_SURF_TYPE_CUBEMAP; |
| AddrSurfInfoIn.flags.display = (surf->flags & RADEON_SURF_SCANOUT) != 0; |
| AddrSurfInfoIn.flags.pow2Pad = surf->last_level > 0; |
| AddrSurfInfoIn.flags.degrade4Space = 1; |
| |
| /* DCC notes: |
| * - If we add MSAA support, keep in mind that CB can't decompress 8bpp |
| * with samples >= 4. |
| * - Mipmapped array textures have low performance (discovered by a closed |
| * driver team). |
| */ |
| AddrSurfInfoIn.flags.dccCompatible = !(surf->flags & RADEON_SURF_Z_OR_SBUFFER) && |
| !(surf->flags & RADEON_SURF_DISABLE_DCC) && |
| !compressed && AddrDccIn.numSamples <= 1 && |
| ((surf->array_size == 1 && surf->npix_z == 1) || |
| surf->last_level == 0); |
| |
| AddrSurfInfoIn.flags.noStencil = (surf->flags & RADEON_SURF_SBUFFER) == 0; |
| AddrSurfInfoIn.flags.compressZ = AddrSurfInfoIn.flags.depth; |
| |
| /* noStencil = 0 can result in a depth part that is incompatible with |
| * mipmapped texturing. So set noStencil = 1 when mipmaps are requested (in |
| * this case, we may end up setting stencil_adjusted). |
| * |
| * TODO: update addrlib to a newer version, remove this, and |
| * use flags.matchStencilTileCfg = 1 as an alternative fix. |
| */ |
| if (surf->last_level > 0) |
| AddrSurfInfoIn.flags.noStencil = 1; |
| |
| /* Set preferred macrotile parameters. This is usually required |
| * for shared resources. This is for 2D tiling only. */ |
| if (AddrSurfInfoIn.tileMode >= ADDR_TM_2D_TILED_THIN1 && |
| surf->bankw && surf->bankh && surf->mtilea && surf->tile_split) { |
| /* If any of these parameters are incorrect, the calculation |
| * will fail. */ |
| AddrTileInfoIn.banks = surf->num_banks; |
| AddrTileInfoIn.bankWidth = surf->bankw; |
| AddrTileInfoIn.bankHeight = surf->bankh; |
| AddrTileInfoIn.macroAspectRatio = surf->mtilea; |
| AddrTileInfoIn.tileSplitBytes = surf->tile_split; |
| AddrTileInfoIn.pipeConfig = surf->pipe_config + 1; /* +1 compared to GB_TILE_MODE */ |
| AddrSurfInfoIn.flags.degrade4Space = 0; |
| AddrSurfInfoIn.pTileInfo = &AddrTileInfoIn; |
| |
| /* If AddrSurfInfoIn.pTileInfo is set, Addrlib doesn't set |
| * the tile index, because we are expected to know it if |
| * we know the other parameters. |
| * |
| * This is something that can easily be fixed in Addrlib. |
| * For now, just figure it out here. |
| * Note that only 2D_TILE_THIN1 is handled here. |
| */ |
| assert(!(surf->flags & RADEON_SURF_Z_OR_SBUFFER)); |
| assert(AddrSurfInfoIn.tileMode == ADDR_TM_2D_TILED_THIN1); |
| |
| if (ws->info.chip_class == SI) { |
| if (AddrSurfInfoIn.tileType == ADDR_DISPLAYABLE) { |
| if (surf->bpe == 2) |
| AddrSurfInfoIn.tileIndex = 11; /* 16bpp */ |
| else |
| AddrSurfInfoIn.tileIndex = 12; /* 32bpp */ |
| } else { |
| if (surf->bpe == 1) |
| AddrSurfInfoIn.tileIndex = 14; /* 8bpp */ |
| else if (surf->bpe == 2) |
| AddrSurfInfoIn.tileIndex = 15; /* 16bpp */ |
| else if (surf->bpe == 4) |
| AddrSurfInfoIn.tileIndex = 16; /* 32bpp */ |
| else |
| AddrSurfInfoIn.tileIndex = 17; /* 64bpp (and 128bpp) */ |
| } |
| } else { |
| if (AddrSurfInfoIn.tileType == ADDR_DISPLAYABLE) |
| AddrSurfInfoIn.tileIndex = 10; /* 2D displayable */ |
| else |
| AddrSurfInfoIn.tileIndex = 14; /* 2D non-displayable */ |
| } |
| } |
| |
| surf->bo_size = 0; |
| surf->dcc_size = 0; |
| surf->dcc_alignment = 1; |
| |
| /* Calculate texture layout information. */ |
| for (level = 0; level <= surf->last_level; level++) { |
| r = radv_compute_level(ws->addrlib, surf, false, level, type, compressed, |
| &AddrSurfInfoIn, &AddrSurfInfoOut, &AddrDccIn, &AddrDccOut); |
| if (r) |
| return r; |
| |
| if (level == 0) { |
| surf->bo_alignment = AddrSurfInfoOut.baseAlign; |
| surf->pipe_config = AddrSurfInfoOut.pTileInfo->pipeConfig - 1; |
| radv_set_micro_tile_mode(surf, &ws->info); |
| |
| /* For 2D modes only. */ |
| if (AddrSurfInfoOut.tileMode >= ADDR_TM_2D_TILED_THIN1) { |
| surf->bankw = AddrSurfInfoOut.pTileInfo->bankWidth; |
| surf->bankh = AddrSurfInfoOut.pTileInfo->bankHeight; |
| surf->mtilea = AddrSurfInfoOut.pTileInfo->macroAspectRatio; |
| surf->tile_split = AddrSurfInfoOut.pTileInfo->tileSplitBytes; |
| surf->num_banks = AddrSurfInfoOut.pTileInfo->banks; |
| surf->macro_tile_index = AddrSurfInfoOut.macroModeIndex; |
| } else { |
| surf->macro_tile_index = 0; |
| } |
| } |
| } |
| |
| /* Calculate texture layout information for stencil. */ |
| if (surf->flags & RADEON_SURF_SBUFFER) { |
| AddrSurfInfoIn.bpp = 8; |
| AddrSurfInfoIn.flags.depth = 0; |
| AddrSurfInfoIn.flags.stencil = 1; |
| /* This will be ignored if AddrSurfInfoIn.pTileInfo is NULL. */ |
| AddrTileInfoIn.tileSplitBytes = surf->stencil_tile_split; |
| |
| for (level = 0; level <= surf->last_level; level++) { |
| r = radv_compute_level(ws->addrlib, surf, true, level, type, compressed, |
| &AddrSurfInfoIn, &AddrSurfInfoOut, &AddrDccIn, &AddrDccOut); |
| if (r) |
| return r; |
| |
| /* DB uses the depth pitch for both stencil and depth. */ |
| if (surf->stencil_level[level].nblk_x != surf->level[level].nblk_x) |
| surf->stencil_adjusted = true; |
| |
| if (level == 0) { |
| /* For 2D modes only. */ |
| if (AddrSurfInfoOut.tileMode >= ADDR_TM_2D_TILED_THIN1) { |
| surf->stencil_tile_split = |
| AddrSurfInfoOut.pTileInfo->tileSplitBytes; |
| } |
| } |
| } |
| } |
| |
| /* Recalculate the whole DCC miptree size including disabled levels. |
| * This is what addrlib does, but calling addrlib would be a lot more |
| * complicated. |
| */ |
| #if 0 |
| if (surf->dcc_size && surf->last_level > 0) { |
| surf->dcc_size = align64(surf->bo_size >> 8, |
| ws->info.pipe_interleave_bytes * |
| ws->info.num_tile_pipes); |
| } |
| #endif |
| return 0; |
| } |
| |
| static int radv_amdgpu_winsys_surface_best(struct radeon_winsys *rws, |
| struct radeon_surf *surf) |
| { |
| return 0; |
| } |
| |
| void radv_amdgpu_surface_init_functions(struct radv_amdgpu_winsys *ws) |
| { |
| ws->base.surface_init = radv_amdgpu_winsys_surface_init; |
| ws->base.surface_best = radv_amdgpu_winsys_surface_best; |
| } |