mesa/src/gallium/winsys/radeon/drm/radeon_drm_winsys.c

987 lines
32 KiB
C
Raw Normal View History

/*
* Copyright © 2009 Corbin Simpson
* Copyright © 2011 Marek Olšák <maraeo@gmail.com>
* All Rights Reserved.
*
* 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, sub license, 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 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
* NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS, AUTHORS
* AND/OR ITS SUPPLIERS 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.
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*/
#include "radeon_drm_bo.h"
r300g: rework command submission and resource space checking The motivation behind this rework is to get some speed by reducing CPU overhead. The performance increase depends on many factors, but it's measurable (I think it's about 10% increase in Torcs). This commit replaces libdrm's radeon_cs_gem with our own implemention. It's optimized specifically for r300g, but r600g could use it as well. Reloc writes and space checking are faster and simpler than their counterparts in libdrm (the time complexity of all the functions is O(1) in nearly all scenarios, thanks to hashing). (libdrm's radeon_bo_gem is still being used in the driver.) It works like this: cs_add_reloc(cs, buf, read_domain, write_domain) adds a new relocation and also adds the size of 'buf' to the used_gart and used_vram winsys variables based on the domains, which are simply or'd for the accounting purposes. The adding is skipped if the reloc is already present in the list, but it accounts any newly-referenced domains. cs_validate is then called, which just checks: used_vram/gart < vram/gart_size * 0.8 The 0.8 number allows for some memory fragmentation. If the validation fails, the pipe driver flushes CS and tries do the validation again, i.e. it validates only that one operation. If it fails again, it drops the operation on the floor and prints some nasty message to stderr. cs_write_reloc(cs, buf) just writes a reloc that has been added using cs_add_reloc. The read_domain and write_domain parameters have been removed, because we already specify them in cs_add_reloc. The space checking has been tested by putting small values in vram/gart_size variables.
2010-12-04 03:38:15 +00:00
#include "radeon_drm_cs.h"
2010-02-27 01:46:20 +00:00
#include "util/os_file.h"
#include "util/u_cpu_detect.h"
2010-02-27 01:46:20 +00:00
#include "util/u_memory.h"
#include "util/u_hash_table.h"
#include "util/u_pointer.h"
2010-02-27 01:46:20 +00:00
2010-12-04 03:36:02 +00:00
#include <xf86drm.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <radeon_surface.h>
static struct hash_table *fd_tab = NULL;
static mtx_t fd_tab_mutex = _MTX_INITIALIZER_NP;
/* Enable/disable feature access for one command stream.
* If enable == true, return true on success.
* Otherwise, return false.
*
* We basically do the same thing kernel does, because we have to deal
* with multiple contexts (here command streams) backed by one winsys. */
static bool radeon_set_fd_access(struct radeon_drm_cs *applier,
struct radeon_drm_cs **owner,
mtx_t *mutex,
unsigned request, const char *request_name,
bool enable)
2011-01-27 22:13:28 +00:00
{
struct drm_radeon_info info;
unsigned value = enable ? 1 : 0;
memset(&info, 0, sizeof(info));
mtx_lock(&*mutex);
/* Early exit if we are sure the request will fail. */
if (enable) {
if (*owner) {
mtx_unlock(&*mutex);
return false;
}
} else {
if (*owner != applier) {
mtx_unlock(&*mutex);
return false;
}
}
/* Pass through the request to the kernel. */
info.value = (unsigned long)&value;
info.request = request;
if (drmCommandWriteRead(applier->ws->fd, DRM_RADEON_INFO,
&info, sizeof(info)) != 0) {
mtx_unlock(&*mutex);
return false;
}
/* Update the rights in the winsys. */
if (enable) {
if (value) {
*owner = applier;
mtx_unlock(&*mutex);
return true;
}
} else {
*owner = NULL;
}
mtx_unlock(&*mutex);
return false;
}
static bool radeon_get_drm_value(int fd, unsigned request,
const char *errname, uint32_t *out)
{
struct drm_radeon_info info;
int retval;
memset(&info, 0, sizeof(info));
info.value = (unsigned long)out;
info.request = request;
retval = drmCommandWriteRead(fd, DRM_RADEON_INFO, &info, sizeof(info));
if (retval) {
if (errname) {
fprintf(stderr, "radeon: Failed to get %s, error number %d\n",
errname, retval);
}
return false;
}
return true;
}
/* Helper function to do the ioctls needed for setup and init. */
static bool do_winsys_init(struct radeon_drm_winsys *ws)
{
struct drm_radeon_gem_info gem_info;
int retval;
drmVersionPtr version;
memset(&gem_info, 0, sizeof(gem_info));
/* We do things in a specific order here.
*
* DRM version first. We need to be sure we're running on a KMS chipset.
* This is also for some features.
*
* Then, the PCI ID. This is essential and should return usable numbers
* for all Radeons. If this fails, we probably got handed an FD for some
* non-Radeon card.
*
* The GEM info is actually bogus on the kernel side, as well as our side
* (see radeon_gem_info_ioctl in radeon_gem.c) but that's alright because
* we don't actually use the info for anything yet.
*
* The GB and Z pipe requests should always succeed, but they might not
* return sensical values for all chipsets, but that's alright because
* the pipe drivers already know that.
*/
/* Get DRM version. */
version = drmGetVersion(ws->fd);
if (version->version_major != 2 ||
version->version_minor < 12) {
fprintf(stderr, "%s: DRM version is %d.%d.%d but this driver is "
"only compatible with 2.12.0 (kernel 3.2) or later.\n",
__FUNCTION__,
version->version_major,
version->version_minor,
version->version_patchlevel);
drmFreeVersion(version);
return false;
}
ws->info.drm_major = version->version_major;
ws->info.drm_minor = version->version_minor;
ws->info.drm_patchlevel = version->version_patchlevel;
ws->info.is_amdgpu = false;
drmFreeVersion(version);
/* Get PCI ID. */
if (!radeon_get_drm_value(ws->fd, RADEON_INFO_DEVICE_ID, "PCI ID",
&ws->info.pci_id))
return false;
/* Check PCI ID. */
switch (ws->info.pci_id) {
#define CHIPSET(pci_id, name, cfamily) case pci_id: ws->info.family = CHIP_##cfamily; ws->gen = DRV_R300; break;
#include "pci_ids/r300_pci_ids.h"
#undef CHIPSET
#define CHIPSET(pci_id, name, cfamily) case pci_id: ws->info.family = CHIP_##cfamily; ws->gen = DRV_R600; break;
#include "pci_ids/r600_pci_ids.h"
#undef CHIPSET
#define CHIPSET(pci_id, cfamily) \
case pci_id: \
ws->info.family = CHIP_##cfamily; \
ws->info.name = #cfamily; \
ws->gen = DRV_SI; \
break;
radeonsi: initial WIP SI code This commit adds initial support for acceleration on SI chips. egltri is starting to work. The SI/R600 llvm backend is currently included in mesa but that may change in the future. The plan is to write a single gallium driver and use gallium to support X acceleration. This commit contains patches from: Tom Stellard <thomas.stellard@amd.com> Michel Dänzer <michel.daenzer@amd.com> Alex Deucher <alexander.deucher@amd.com> Vadim Girlin <vadimgirlin@gmail.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com> The following commits were squashed in: ====================================================================== radeonsi: Remove unused winsys pointer This was removed from r600g in commit: commit 96d882939d612fcc8332f107befec470ed4359de Author: Marek Olšák <maraeo@gmail.com> Date: Fri Feb 17 01:49:49 2012 +0100 gallium: remove unused winsys pointers in pipe_screen and pipe_context A winsys is already a private object of a driver. ====================================================================== radeonsi: Copy color clamping CAPs from r600 Not sure if the values of these CAPS are correct for radeonsi, but the same changed were made to r600g in commit: commit bc1c8369384b5e16547c5bf9728aa78f8dfd66cc Author: Marek Olšák <maraeo@gmail.com> Date: Mon Jan 23 03:11:17 2012 +0100 st/mesa: do vertex and fragment color clamping in shaders For ARB_color_buffer_float. Most hardware can't do it and st/mesa is the perfect place for a fallback. The exceptions are: - r500 (vertex clamp only) - nv50 (both) - nvc0 (both) - softpipe (both) We also have to take into account that r300 can do CLAMPED vertex colors only, while r600 can do UNCLAMPED vertex colors only. The difference can be expressed with the two new CAPs. ====================================================================== radeonsi: Remove PIPE_CAP_OUTPUT_READ This CAP was dropped in commit: commit 04e324008759282728a95a1394bac2c4c2a1a3f9 Author: Marek Olšák <maraeo@gmail.com> Date: Thu Feb 23 23:44:36 2012 +0100 gallium: remove PIPE_SHADER_CAP_OUTPUT_READ r600g is the only driver which has made use of it. The reason the CAP was added was to fix some piglit tests when the GLSL pass lower_output_reads didn't exist. However, not removing output reads breaks the fallback for glClampColorARB, which assumes outputs are not readable. The fix would be non-trivial and my personal preference is to remove the CAP, considering that reading outputs is uncommon and that we can now use lower_output_reads to fix the issue that the CAP was supposed to workaround in the first place. ====================================================================== radeonsi: Add missing parameters to rws->buffer_get_tiling() call This was changed in commit: commit c0c979eebc076b95cc8d18a013ce2968fe6311ad Author: Jerome Glisse <jglisse@redhat.com> Date: Mon Jan 30 17:22:13 2012 -0500 r600g: add support for common surface allocator for tiling v13 Tiled surface have all kind of alignment constraint that needs to be met. Instead of having all this code duplicated btw ddx and mesa use common code in libdrm_radeon this also ensure that both ddx and mesa compute those alignment in the same way. v2 fix evergreen v3 fix compressed texture and workaround cube texture issue by disabling 2D array mode for cubemap (need to check if r7xx and newer are also affected by the issue) v4 fix texture array v5 fix evergreen and newer, split surface values computation from mipmap tree generation so that we can get them directly from the ddx v6 final fix to evergreen tile split value v7 fix mipmap offset to avoid to use random value, use color view depth view to address different layer as hardware is doing some magic rotation depending on the layer v8 fix COLOR_VIEW on r6xx for linear array mode, use COLOR_VIEW on evergreen, align bytes per pixel to a multiple of a dword v9 fix handling of stencil on evergreen, half fix for compressed texture v10 fix evergreen compressed texture proper support for stencil tile split. Fix stencil issue when array mode was clear by the kernel, always program stencil bo. On evergreen depth buffer bo need to be big enough to hold depth buffer + stencil buffer as even with stencil disabled things get written there. v11 rebase on top of mesa, fix pitch issue with 1d surface on evergreen, old ddx overestimate those. Fix linear case when pitch*height < 64. Fix r300g. v12 Fix linear case when pitch*height < 64 for old path, adapt to libdrm API change v13 add libdrm check Signed-off-by: Jerome Glisse <jglisse@redhat.com> ====================================================================== radeonsi: Remove PIPE_TRANSFER_MAP_PERMANENTLY This was removed in commit: commit 62f44f670bb0162e89fd4786af877f8da9ff607c Author: Marek Olšák <maraeo@gmail.com> Date: Mon Mar 5 13:45:00 2012 +0100 Revert "gallium: add flag PIPE_TRANSFER_MAP_PERMANENTLY" This reverts commit 0950086376b1c8b7fb89eda81ed7f2f06dee58bc. It was decided to refactor the transfer API instead of adding workarounds to address the performance issues. ====================================================================== radeonsi: Handle PIPE_VIDEO_CAP_PREFERED_FORMAT. Reintroduced in commit 9d9afcb5bac2931d4b8e6d1aa571e941c5110c90. ====================================================================== radeonsi: nuke the fallback for vertex and fragment color clamping Ported from r600g commit c2b800cf38b299c1ab1c53dc0e4ea00c7acef853. ====================================================================== radeonsi: don't expose transform_feedback2 without kernel support Ported from r600g commit 15146fd1bcbb08e44a1cbb984440ee1a5de63d48. ====================================================================== radeonsi: Handle PIPE_CAP_GLSL_FEATURE_LEVEL. Ported from r600g part of commit 171be755223d99f8cc5cc1bdaf8bd7b4caa04b4f. ====================================================================== radeonsi: set minimum point size to 1.0 for non-sprite non-aa points. Ported from r600g commit f183cc9ce3ad1d043bdf8b38fd519e8f437714fc. ====================================================================== radeonsi: rework and consolidate stencilref state setting. Ported from r600g commit a2361946e782b57f0c63587841ca41c0ea707070. ====================================================================== radeonsi: cleanup setting DB_SHADER_CONTROL. Ported from r600g commit 3d061caaed13b646ff40754f8ebe73f3d4983c5b. ====================================================================== radeonsi: Get rid of register masks. Ported from r600g commits 3d061caaed13b646ff40754f8ebe73f3d4983c5b..9344ab382a1765c1a7c2560e771485edf4954fe2. ====================================================================== radeonsi: get rid of r600_context_reg. Ported from r600g commits 9344ab382a1765c1a7c2560e771485edf4954fe2..bed20f02a771f43e1c5092254705701c228cfa7f. ====================================================================== radeonsi: Fix regression from 'Get rid of register masks'. ====================================================================== radeonsi: optimize r600_resource_va. Ported from r600g commit 669d8766ff3403938794eb80d7769347b6e52174. ====================================================================== radeonsi: remove u8,u16,u32,u64 types. Ported from r600g commit 78293b99b23268e6698f1267aaf40647c17d95a5. ====================================================================== radeonsi: merge r600_context with r600_pipe_context. Ported from r600g commit e4340c1908a6a3b09e1a15d5195f6da7d00494d0. ====================================================================== radeonsi: Miscellaneous context cleanups. Ported from r600g commits e4340c1908a6a3b09e1a15d5195f6da7d00494d0..621e0db71c5ddcb379171064a4f720c9cf01e888. ====================================================================== radeonsi: add a new simple API for state emission. Ported from r600g commits 621e0db71c5ddcb379171064a4f720c9cf01e888..f661405637bba32c2cfbeecf6e2e56e414e9521e. ====================================================================== radeonsi: Also remove sbu_flags member of struct r600_reg. Requires using sid.h instead of r600d.h for the new CP_COHER_CNTL definitions, so some code needs to be disabled for now. ====================================================================== radeonsi: Miscellaneous simplifications. Ported from r600g commits 38bf2763482b4f1b6d95cd51aecec75601d8b90f and b0337b679ad4c2feae59215104cfa60b58a619d5. ====================================================================== radeonsi: Handle PIPE_CAP_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION. Ported from commit 8b4f7b0672d663273310fffa9490ad996f5b914a. ====================================================================== radeonsi: Use a fake reloc to sleep for fences. Ported from r600g commit 8cd03b933cf868ff867e2db4a0937005a02fd0e4. ====================================================================== radeonsi: adapt to get_query_result interface change. Ported from r600g commit 4445e170bee23a3607ece0e010adef7058ac6a11.
2012-01-06 22:38:37 +00:00
#include "pci_ids/radeonsi_pci_ids.h"
#undef CHIPSET
default:
fprintf(stderr, "radeon: Invalid PCI ID.\n");
return false;
}
switch (ws->info.family) {
default:
case CHIP_UNKNOWN:
fprintf(stderr, "radeon: Unknown family.\n");
return false;
case CHIP_R300:
case CHIP_R350:
case CHIP_RV350:
case CHIP_RV370:
case CHIP_RV380:
case CHIP_RS400:
case CHIP_RC410:
case CHIP_RS480:
ws->info.gfx_level = R300;
break;
case CHIP_R420: /* R4xx-based cores. */
case CHIP_R423:
case CHIP_R430:
case CHIP_R480:
case CHIP_R481:
case CHIP_RV410:
case CHIP_RS600:
case CHIP_RS690:
case CHIP_RS740:
ws->info.gfx_level = R400;
break;
case CHIP_RV515: /* R5xx-based cores. */
case CHIP_R520:
case CHIP_RV530:
case CHIP_R580:
case CHIP_RV560:
case CHIP_RV570:
ws->info.gfx_level = R500;
break;
case CHIP_R600:
case CHIP_RV610:
case CHIP_RV630:
case CHIP_RV670:
case CHIP_RV620:
case CHIP_RV635:
case CHIP_RS780:
case CHIP_RS880:
ws->info.gfx_level = R600;
break;
case CHIP_RV770:
case CHIP_RV730:
case CHIP_RV710:
case CHIP_RV740:
ws->info.gfx_level = R700;
break;
case CHIP_CEDAR:
case CHIP_REDWOOD:
case CHIP_JUNIPER:
case CHIP_CYPRESS:
case CHIP_HEMLOCK:
case CHIP_PALM:
case CHIP_SUMO:
case CHIP_SUMO2:
case CHIP_BARTS:
case CHIP_TURKS:
case CHIP_CAICOS:
ws->info.gfx_level = EVERGREEN;
break;
case CHIP_CAYMAN:
case CHIP_ARUBA:
ws->info.gfx_level = CAYMAN;
break;
case CHIP_TAHITI:
case CHIP_PITCAIRN:
case CHIP_VERDE:
case CHIP_OLAND:
case CHIP_HAINAN:
ws->info.gfx_level = GFX6;
break;
case CHIP_BONAIRE:
case CHIP_KAVERI:
case CHIP_KABINI:
case CHIP_HAWAII:
ws->info.gfx_level = GFX7;
break;
}
/* Set which chips don't have dedicated VRAM. */
switch (ws->info.family) {
case CHIP_RS400:
case CHIP_RC410:
case CHIP_RS480:
case CHIP_RS600:
case CHIP_RS690:
case CHIP_RS740:
case CHIP_RS780:
case CHIP_RS880:
case CHIP_PALM:
case CHIP_SUMO:
case CHIP_SUMO2:
case CHIP_ARUBA:
case CHIP_KAVERI:
case CHIP_KABINI:
ws->info.has_dedicated_vram = false;
break;
default:
ws->info.has_dedicated_vram = true;
}
ws->info.ip[AMD_IP_GFX].num_queues = 1;
/* Check for dma */
ws->info.ip[AMD_IP_SDMA].num_queues = 0;
/* DMA is disabled on R700. There is IB corruption and hangs. */
if (ws->info.gfx_level >= EVERGREEN && ws->info.drm_minor >= 27) {
ws->info.ip[AMD_IP_SDMA].num_queues = 1;
}
/* Check for UVD and VCE */
ws->info.has_video_hw.uvd_decode = false;
ws->info.has_video_hw.vce_encode = false;
ws->info.vce_fw_version = 0x00000000;
if (ws->info.drm_minor >= 32) {
uint32_t value = RADEON_CS_RING_UVD;
if (radeon_get_drm_value(ws->fd, RADEON_INFO_RING_WORKING,
"UVD Ring working", &value)) {
ws->info.has_video_hw.uvd_decode = value;
ws->info.ip[AMD_IP_UVD].num_queues = 1;
}
value = RADEON_CS_RING_VCE;
if (radeon_get_drm_value(ws->fd, RADEON_INFO_RING_WORKING,
NULL, &value) && value) {
if (radeon_get_drm_value(ws->fd, RADEON_INFO_VCE_FW_VERSION,
"VCE FW version", &value)) {
ws->info.vce_fw_version = value;
ws->info.ip[AMD_IP_VCE].num_queues = 1;
ws->info.has_video_hw.vce_encode = true;
}
}
}
/* Check for userptr support. */
{
struct drm_radeon_gem_userptr args = {0};
/* If the ioctl doesn't exist, -EINVAL is returned.
*
* If the ioctl exists, it should return -EACCES
* if RADEON_GEM_USERPTR_READONLY or RADEON_GEM_USERPTR_REGISTER
* aren't set.
*/
ws->info.has_userptr =
drmCommandWriteRead(ws->fd, DRM_RADEON_GEM_USERPTR,
&args, sizeof(args)) == -EACCES;
}
/* Get GEM info. */
retval = drmCommandWriteRead(ws->fd, DRM_RADEON_GEM_INFO,
&gem_info, sizeof(gem_info));
if (retval) {
fprintf(stderr, "radeon: Failed to get MM info, error number %d\n",
retval);
return false;
}
ws->info.gart_size = gem_info.gart_size;
ws->info.vram_size = gem_info.vram_size;
ws->info.vram_vis_size = gem_info.vram_visible;
/* Older versions of the kernel driver reported incorrect values, and
* didn't support more than 256MB of visible VRAM anyway
*/
if (ws->info.drm_minor < 49)
ws->info.vram_vis_size = MIN2(ws->info.vram_vis_size, 256*1024*1024);
ws->info.gart_size_kb = DIV_ROUND_UP(ws->info.gart_size, 1024);
ws->info.vram_size_kb = DIV_ROUND_UP(ws->info.vram_size, 1024);
/* Radeon allocates all buffers contiguously, which makes large allocations
* unlikely to succeed. */
if (ws->info.has_dedicated_vram)
ws->info.max_heap_size_kb = ws->info.vram_size_kb;
else
ws->info.max_heap_size_kb = ws->info.gart_size_kb;
/* Old kernel driver limitation for allocation sizes. We only use this to limit per-buffer
* allocation size.
*/
if (ws->info.drm_minor < 40)
ws->info.max_heap_size_kb = MIN2(ws->info.max_heap_size_kb, 256 * 1024);
/* Both 32-bit and 64-bit address spaces only have 4GB.
* This is a limitation of the VM allocator in the winsys.
*/
ws->info.max_heap_size_kb = MIN2(ws->info.max_heap_size_kb, 4 * 1024 * 1024); /* 4 GB */
/* Get max clock frequency info and convert it to MHz */
radeon_get_drm_value(ws->fd, RADEON_INFO_MAX_SCLK, NULL,
&ws->info.max_shader_clock);
ws->info.max_shader_clock /= 1000;
ws->num_cpus = sysconf(_SC_NPROCESSORS_ONLN);
/* Generation-specific queries. */
if (ws->gen == DRV_R300) {
if (!radeon_get_drm_value(ws->fd, RADEON_INFO_NUM_GB_PIPES,
"GB pipe count",
&ws->info.r300_num_gb_pipes))
return false;
if (!radeon_get_drm_value(ws->fd, RADEON_INFO_NUM_Z_PIPES,
"Z pipe count",
&ws->info.r300_num_z_pipes))
return false;
}
else if (ws->gen >= DRV_R600) {
uint32_t tiling_config = 0;
if (!radeon_get_drm_value(ws->fd, RADEON_INFO_NUM_BACKENDS,
"num backends",
&ws->info.max_render_backends))
return false;
/* get the GPU counter frequency, failure is not fatal */
radeon_get_drm_value(ws->fd, RADEON_INFO_CLOCK_CRYSTAL_FREQ, NULL,
&ws->info.clock_crystal_freq);
radeon_get_drm_value(ws->fd, RADEON_INFO_TILING_CONFIG, NULL,
&tiling_config);
ws->info.r600_num_banks =
ws->info.gfx_level >= EVERGREEN ?
4 << ((tiling_config & 0xf0) >> 4) :
4 << ((tiling_config & 0x30) >> 4);
ws->info.pipe_interleave_bytes =
ws->info.gfx_level >= EVERGREEN ?
256 << ((tiling_config & 0xf00) >> 8) :
256 << ((tiling_config & 0xc0) >> 6);
if (!ws->info.pipe_interleave_bytes)
ws->info.pipe_interleave_bytes =
ws->info.gfx_level >= EVERGREEN ? 512 : 256;
radeon_get_drm_value(ws->fd, RADEON_INFO_NUM_TILE_PIPES, NULL,
&ws->info.num_tile_pipes);
/* "num_tiles_pipes" must be equal to the number of pipes (Px) in the
* pipe config field of the GB_TILE_MODE array. Only one card (Tahiti)
* reports a different value (12). Fix it by setting what's in the
* GB_TILE_MODE array (8).
*/
if (ws->gen == DRV_SI && ws->info.num_tile_pipes == 12)
ws->info.num_tile_pipes = 8;
if (radeon_get_drm_value(ws->fd, RADEON_INFO_BACKEND_MAP, NULL,
&ws->info.r600_gb_backend_map))
ws->info.r600_gb_backend_map_valid = true;
/* Default value. */
ws->info.enabled_rb_mask = u_bit_consecutive(0, ws->info.max_render_backends);
/*
* This fails (silently) on non-GCN or older kernels, overwriting the
* default enabled_rb_mask with the result of the last query.
*/
if (ws->gen >= DRV_SI)
radeon_get_drm_value(ws->fd, RADEON_INFO_SI_BACKEND_ENABLED_MASK, NULL,
&ws->info.enabled_rb_mask);
ws->info.r600_has_virtual_memory = false;
if (ws->info.drm_minor >= 13) {
uint32_t ib_vm_max_size;
ws->info.r600_has_virtual_memory = true;
if (!radeon_get_drm_value(ws->fd, RADEON_INFO_VA_START, NULL,
&ws->va_start))
ws->info.r600_has_virtual_memory = false;
if (!radeon_get_drm_value(ws->fd, RADEON_INFO_IB_VM_MAX_SIZE, NULL,
&ib_vm_max_size))
ws->info.r600_has_virtual_memory = false;
radeon_get_drm_value(ws->fd, RADEON_INFO_VA_UNMAP_WORKING, NULL,
&ws->va_unmap_working);
}
if (ws->gen == DRV_R600 && !debug_get_bool_option("RADEON_VA", false))
ws->info.r600_has_virtual_memory = false;
}
/* Get max pipes, this is only needed for compute shaders. All evergreen+
* chips have at least 2 pipes, so we use 2 as a default. */
ws->info.r600_max_quad_pipes = 2;
radeon_get_drm_value(ws->fd, RADEON_INFO_MAX_PIPES, NULL,
&ws->info.r600_max_quad_pipes);
/* All GPUs have at least one compute unit */
ws->info.num_cu = 1;
radeon_get_drm_value(ws->fd, RADEON_INFO_ACTIVE_CU_COUNT, NULL,
&ws->info.num_cu);
radeon_get_drm_value(ws->fd, RADEON_INFO_MAX_SE, NULL,
&ws->info.max_se);
switch (ws->info.family) {
case CHIP_HAINAN:
case CHIP_KABINI:
ws->info.max_tcc_blocks = 2;
break;
case CHIP_VERDE:
case CHIP_OLAND:
case CHIP_BONAIRE:
case CHIP_KAVERI:
ws->info.max_tcc_blocks = 4;
break;
case CHIP_PITCAIRN:
ws->info.max_tcc_blocks = 8;
break;
case CHIP_TAHITI:
ws->info.max_tcc_blocks = 12;
break;
case CHIP_HAWAII:
ws->info.max_tcc_blocks = 16;
break;
default:
ws->info.max_tcc_blocks = 0;
break;
}
if (!ws->info.max_se) {
switch (ws->info.family) {
default:
ws->info.max_se = 1;
break;
case CHIP_CYPRESS:
case CHIP_HEMLOCK:
case CHIP_BARTS:
case CHIP_CAYMAN:
case CHIP_TAHITI:
case CHIP_PITCAIRN:
case CHIP_BONAIRE:
ws->info.max_se = 2;
break;
case CHIP_HAWAII:
ws->info.max_se = 4;
break;
}
}
ws->info.num_se = ws->info.max_se;
radeon_get_drm_value(ws->fd, RADEON_INFO_MAX_SH_PER_SE, NULL,
&ws->info.max_sa_per_se);
if (ws->gen == DRV_SI) {
ws->info.max_good_cu_per_sa =
ws->info.min_good_cu_per_sa = ws->info.num_cu /
(ws->info.max_se * ws->info.max_sa_per_se);
}
radeon_get_drm_value(ws->fd, RADEON_INFO_ACCEL_WORKING2, NULL,
&ws->accel_working2);
if (ws->info.family == CHIP_HAWAII && ws->accel_working2 < 2) {
fprintf(stderr, "radeon: GPU acceleration for Hawaii disabled, "
"returned accel_working2 value %u is smaller than 2. "
"Please install a newer kernel.\n",
ws->accel_working2);
return false;
}
if (ws->info.gfx_level == GFX7) {
if (!radeon_get_drm_value(ws->fd, RADEON_INFO_CIK_MACROTILE_MODE_ARRAY, NULL,
ws->info.cik_macrotile_mode_array)) {
fprintf(stderr, "radeon: Kernel 3.13 is required for Sea Islands support.\n");
return false;
}
}
if (ws->info.gfx_level >= GFX6) {
if (!radeon_get_drm_value(ws->fd, RADEON_INFO_SI_TILE_MODE_ARRAY, NULL,
ws->info.si_tile_mode_array)) {
fprintf(stderr, "radeon: Kernel 3.10 is required for Southern Islands support.\n");
return false;
}
}
/* Hawaii with old firmware needs type2 nop packet.
* accel_working2 with value 3 indicates the new firmware.
*/
ws->info.gfx_ib_pad_with_type2 = ws->info.gfx_level <= GFX6 ||
(ws->info.family == CHIP_HAWAII &&
ws->accel_working2 < 3);
ws->info.tcc_cache_line_size = 64; /* TC L2 line size on GCN */
ws->info.ib_alignment = 4096;
ws->info.kernel_flushes_hdp_before_ib = ws->info.drm_minor >= 40;
/* HTILE is broken with 1D tiling on old kernels and GFX7. */
ws->info.htile_cmask_support_1d_tiling = ws->info.gfx_level != GFX7 ||
ws->info.drm_minor >= 38;
ws->info.si_TA_CS_BC_BASE_ADDR_allowed = ws->info.drm_minor >= 48;
ws->info.has_bo_metadata = false;
ws->info.has_gpu_reset_status_query = ws->info.drm_minor >= 43;
ws->info.has_eqaa_surface_allocator = false;
ws->info.has_format_bc1_through_bc7 = ws->info.drm_minor >= 31;
ws->info.kernel_flushes_tc_l2_after_ib = true;
/* Old kernels disallowed register writes via COPY_DATA
* that are used for indirect compute dispatches. */
ws->info.has_indirect_compute_dispatch = ws->info.gfx_level == GFX7 ||
(ws->info.gfx_level == GFX6 &&
ws->info.drm_minor >= 45);
/* GFX6 doesn't support unaligned loads. */
ws->info.has_unaligned_shader_loads = ws->info.gfx_level == GFX7 &&
ws->info.drm_minor >= 50;
ws->info.has_sparse_vm_mappings = false;
/* 2D tiling on GFX7 is supported since DRM 2.35.0 */
ws->info.has_2d_tiling = ws->info.gfx_level <= GFX6 || ws->info.drm_minor >= 35;
ws->info.has_read_registers_query = ws->info.drm_minor >= 42;
ws->info.max_alignment = 1024*1024;
ws->info.has_graphics = true;
ws->info.cpdma_prefetch_writes_memory = true;
ws->info.max_wave64_per_simd = 10;
ws->info.num_physical_sgprs_per_simd = 512;
ws->info.num_physical_wave64_vgprs_per_simd = 256;
ws->info.has_3d_cube_border_color_mipmap = true;
ws->info.spi_cu_en_has_effect = false;
ws->info.spi_cu_en = 0xffff;
ws->info.never_stop_sq_perf_counters = false;
ws->check_vm = strstr(debug_get_option("R600_DEBUG", ""), "check_vm") != NULL ||
strstr(debug_get_option("AMD_DEBUG", ""), "check_vm") != NULL;
ws->noop_cs = debug_get_bool_option("RADEON_NOOP", false);
return true;
}
static void radeon_winsys_destroy(struct radeon_winsys *rws)
2010-12-04 03:36:02 +00:00
{
struct radeon_drm_winsys *ws = (struct radeon_drm_winsys*)rws;
2010-12-04 03:36:02 +00:00
if (util_queue_is_initialized(&ws->cs_queue))
util_queue_destroy(&ws->cs_queue);
mtx_destroy(&ws->hyperz_owner_mutex);
mtx_destroy(&ws->cmask_owner_mutex);
if (ws->info.r600_has_virtual_memory)
pb_slabs_deinit(&ws->bo_slabs);
pb_cache_deinit(&ws->bo_cache);
if (ws->gen >= DRV_R600) {
radeon_surface_manager_free(ws->surf_man);
}
_mesa_hash_table_destroy(ws->bo_names, NULL);
_mesa_hash_table_destroy(ws->bo_handles, NULL);
_mesa_hash_table_u64_destroy(ws->bo_vas);
mtx_destroy(&ws->bo_handles_mutex);
mtx_destroy(&ws->vm32.mutex);
mtx_destroy(&ws->vm64.mutex);
mtx_destroy(&ws->bo_fence_lock);
if (ws->fd >= 0)
close(ws->fd);
FREE(rws);
2010-12-04 03:36:02 +00:00
}
static void radeon_query_info(struct radeon_winsys *rws,
struct radeon_info *info,
bool enable_smart_access_memory,
bool disable_smart_access_memory)
{
*info = ((struct radeon_drm_winsys *)rws)->info;
}
static bool radeon_cs_request_feature(struct radeon_cmdbuf *rcs,
enum radeon_feature_id fid,
bool enable)
{
struct radeon_drm_cs *cs = radeon_drm_cs(rcs);
switch (fid) {
case RADEON_FID_R300_HYPERZ_ACCESS:
return radeon_set_fd_access(cs, &cs->ws->hyperz_owner,
&cs->ws->hyperz_owner_mutex,
RADEON_INFO_WANT_HYPERZ, "Hyper-Z",
enable);
case RADEON_FID_R300_CMASK_ACCESS:
return radeon_set_fd_access(cs, &cs->ws->cmask_owner,
&cs->ws->cmask_owner_mutex,
RADEON_INFO_WANT_CMASK, "AA optimizations",
enable);
}
return false;
}
uint32_t radeon_drm_get_gpu_reset_counter(struct radeon_drm_winsys *ws)
{
uint64_t retval = 0;
if (!ws->info.has_gpu_reset_status_query)
return 0;
radeon_get_drm_value(ws->fd, RADEON_INFO_GPU_RESET_COUNTER,
"gpu-reset-counter", (uint32_t*)&retval);
return retval;
}
static uint64_t radeon_query_value(struct radeon_winsys *rws,
enum radeon_value_id value)
{
struct radeon_drm_winsys *ws = (struct radeon_drm_winsys*)rws;
uint64_t retval = 0;
switch (value) {
case RADEON_REQUESTED_VRAM_MEMORY:
return ws->allocated_vram;
case RADEON_REQUESTED_GTT_MEMORY:
return ws->allocated_gtt;
case RADEON_MAPPED_VRAM:
return ws->mapped_vram;
case RADEON_MAPPED_GTT:
return ws->mapped_gtt;
case RADEON_BUFFER_WAIT_TIME_NS:
return ws->buffer_wait_time;
case RADEON_NUM_MAPPED_BUFFERS:
return ws->num_mapped_buffers;
case RADEON_TIMESTAMP:
if (ws->info.drm_minor < 20 || ws->gen < DRV_R600) {
assert(0);
return 0;
}
radeon_get_drm_value(ws->fd, RADEON_INFO_TIMESTAMP, "timestamp",
(uint32_t*)&retval);
return retval;
case RADEON_NUM_GFX_IBS:
return ws->num_gfx_IBs;
case RADEON_NUM_SDMA_IBS:
return ws->num_sdma_IBs;
case RADEON_NUM_BYTES_MOVED:
radeon_get_drm_value(ws->fd, RADEON_INFO_NUM_BYTES_MOVED,
"num-bytes-moved", (uint32_t*)&retval);
return retval;
case RADEON_NUM_EVICTIONS:
case RADEON_NUM_VRAM_CPU_PAGE_FAULTS:
case RADEON_VRAM_VIS_USAGE:
case RADEON_GFX_BO_LIST_COUNTER:
case RADEON_GFX_IB_SIZE_COUNTER:
case RADEON_SLAB_WASTED_VRAM:
case RADEON_SLAB_WASTED_GTT:
return 0; /* unimplemented */
case RADEON_VRAM_USAGE:
radeon_get_drm_value(ws->fd, RADEON_INFO_VRAM_USAGE,
"vram-usage", (uint32_t*)&retval);
return retval;
case RADEON_GTT_USAGE:
radeon_get_drm_value(ws->fd, RADEON_INFO_GTT_USAGE,
"gtt-usage", (uint32_t*)&retval);
return retval;
case RADEON_GPU_TEMPERATURE:
radeon_get_drm_value(ws->fd, RADEON_INFO_CURRENT_GPU_TEMP,
"gpu-temp", (uint32_t*)&retval);
return retval;
case RADEON_CURRENT_SCLK:
radeon_get_drm_value(ws->fd, RADEON_INFO_CURRENT_GPU_SCLK,
"current-gpu-sclk", (uint32_t*)&retval);
return retval;
case RADEON_CURRENT_MCLK:
radeon_get_drm_value(ws->fd, RADEON_INFO_CURRENT_GPU_MCLK,
"current-gpu-mclk", (uint32_t*)&retval);
return retval;
case RADEON_CS_THREAD_TIME:
return util_queue_get_thread_time_nano(&ws->cs_queue, 0);
}
return 0;
}
static bool radeon_read_registers(struct radeon_winsys *rws,
unsigned reg_offset,
unsigned num_registers, uint32_t *out)
{
struct radeon_drm_winsys *ws = (struct radeon_drm_winsys*)rws;
unsigned i;
for (i = 0; i < num_registers; i++) {
uint32_t reg = reg_offset + i*4;
if (!radeon_get_drm_value(ws->fd, RADEON_INFO_READ_REG, NULL, &reg))
return false;
out[i] = reg;
}
return true;
}
DEBUG_GET_ONCE_BOOL_OPTION(thread, "RADEON_THREAD", true)
static bool radeon_winsys_unref(struct radeon_winsys *ws)
{
struct radeon_drm_winsys *rws = (struct radeon_drm_winsys*)ws;
bool destroy;
/* When the reference counter drops to zero, remove the fd from the table.
* This must happen while the mutex is locked, so that
* radeon_drm_winsys_create in another thread doesn't get the winsys
* from the table when the counter drops to 0. */
mtx_lock(&fd_tab_mutex);
destroy = pipe_reference(&rws->reference, NULL);
if (destroy && fd_tab) {
_mesa_hash_table_remove_key(fd_tab, intptr_to_pointer(rws->fd));
if (_mesa_hash_table_num_entries(fd_tab) == 0) {
_mesa_hash_table_destroy(fd_tab, NULL);
fd_tab = NULL;
}
}
mtx_unlock(&fd_tab_mutex);
return destroy;
}
static void radeon_pin_threads_to_L3_cache(struct radeon_winsys *ws,
unsigned cache)
{
struct radeon_drm_winsys *rws = (struct radeon_drm_winsys*)ws;
if (util_queue_is_initialized(&rws->cs_queue)) {
util_set_thread_affinity(rws->cs_queue.threads[0],
util_get_cpu_caps()->L3_affinity_mask[cache],
NULL, util_get_cpu_caps()->num_cpu_mask_bits);
}
}
static bool radeon_cs_is_secure(struct radeon_cmdbuf* cs)
{
return false;
}
PUBLIC struct radeon_winsys *
radeon_drm_winsys_create(int fd, const struct pipe_screen_config *config,
radeon_screen_create_t screen_create)
{
struct radeon_drm_winsys *ws;
mtx_lock(&fd_tab_mutex);
if (!fd_tab) {
fd_tab = util_hash_table_create_fd_keys();
}
ws = util_hash_table_get(fd_tab, intptr_to_pointer(fd));
if (ws) {
pipe_reference(NULL, &ws->reference);
mtx_unlock(&fd_tab_mutex);
return &ws->base;
}
ws = CALLOC_STRUCT(radeon_drm_winsys);
if (!ws) {
mtx_unlock(&fd_tab_mutex);
return NULL;
}
ws->fd = os_dupfd_cloexec(fd);
if (!do_winsys_init(ws))
goto fail1;
pb_cache_init(&ws->bo_cache, RADEON_NUM_HEAPS,
500000, ws->check_vm ? 1.0f : 2.0f, 0,
MIN2(ws->info.vram_size, ws->info.gart_size), NULL,
radeon_bo_destroy,
radeon_bo_can_reclaim);
if (ws->info.r600_has_virtual_memory) {
/* There is no fundamental obstacle to using slab buffer allocation
* without GPUVM, but enabling it requires making sure that the drivers
* honor the address offset.
*/
if (!pb_slabs_init(&ws->bo_slabs,
RADEON_SLAB_MIN_SIZE_LOG2, RADEON_SLAB_MAX_SIZE_LOG2,
RADEON_NUM_HEAPS, false,
ws,
radeon_bo_can_reclaim_slab,
radeon_bo_slab_alloc,
radeon_bo_slab_free))
goto fail_cache;
ws->info.min_alloc_size = 1 << RADEON_SLAB_MIN_SIZE_LOG2;
} else {
ws->info.min_alloc_size = ws->info.gart_page_size;
}
if (ws->gen >= DRV_R600) {
ws->surf_man = radeon_surface_manager_new(ws->fd);
if (!ws->surf_man)
goto fail_slab;
}
/* init reference */
pipe_reference_init(&ws->reference, 1);
/* Set functions. */
ws->base.unref = radeon_winsys_unref;
ws->base.destroy = radeon_winsys_destroy;
ws->base.query_info = radeon_query_info;
ws->base.pin_threads_to_L3_cache = radeon_pin_threads_to_L3_cache;
ws->base.cs_request_feature = radeon_cs_request_feature;
ws->base.query_value = radeon_query_value;
ws->base.read_registers = radeon_read_registers;
ws->base.cs_is_secure = radeon_cs_is_secure;
radeon_drm_bo_init_functions(ws);
radeon_drm_cs_init_functions(ws);
radeon_surface_init_functions(ws);
(void) mtx_init(&ws->hyperz_owner_mutex, mtx_plain);
(void) mtx_init(&ws->cmask_owner_mutex, mtx_plain);
ws->bo_names = util_hash_table_create_ptr_keys();
ws->bo_handles = util_hash_table_create_ptr_keys();
ws->bo_vas = _mesa_hash_table_u64_create(NULL);
(void) mtx_init(&ws->bo_handles_mutex, mtx_plain);
(void) mtx_init(&ws->vm32.mutex, mtx_plain);
(void) mtx_init(&ws->vm64.mutex, mtx_plain);
(void) mtx_init(&ws->bo_fence_lock, mtx_plain);
list_inithead(&ws->vm32.holes);
list_inithead(&ws->vm64.holes);
/* The kernel currently returns 8MB. Make sure this doesn't change. */
if (ws->va_start > 8 * 1024 * 1024) {
/* Not enough 32-bit address space. */
radeon_winsys_destroy(&ws->base);
mtx_unlock(&fd_tab_mutex);
return NULL;
}
ws->vm32.start = ws->va_start;
ws->vm32.end = 1ull << 32;
/* The maximum is 8GB of virtual address space limited by the kernel.
* It's obviously not enough for bigger cards, like Hawaiis with 4GB
* and 8GB of physical memory and 4GB of GART.
*
* Older kernels set the limit to 4GB, which is even worse, so they only
* have 32-bit address space.
*/
if (ws->info.drm_minor >= 41) {
ws->vm64.start = 1ull << 32;
ws->vm64.end = 1ull << 33;
}
/* TTM aligns the BO size to the CPU page size */
ws->info.gart_page_size = sysconf(_SC_PAGESIZE);
ws->info.pte_fragment_size = 64 * 1024; /* GPUVM page size */
if (ws->num_cpus > 1 && debug_get_option_thread())
util_queue_init(&ws->cs_queue, "rcs", 8, 1, 0, NULL);
/* Create the screen at the end. The winsys must be initialized
* completely.
*
* Alternatively, we could create the screen based on "ws->gen"
* and link all drivers into one binary blob. */
ws->base.screen = screen_create(&ws->base, config);
if (!ws->base.screen) {
radeon_winsys_destroy(&ws->base);
mtx_unlock(&fd_tab_mutex);
return NULL;
}
_mesa_hash_table_insert(fd_tab, intptr_to_pointer(ws->fd), ws);
/* We must unlock the mutex once the winsys is fully initialized, so that
* other threads attempting to create the winsys from the same fd will
* get a fully initialized winsys and not just half-way initialized. */
mtx_unlock(&fd_tab_mutex);
return &ws->base;
2010-12-04 03:36:02 +00:00
fail_slab:
if (ws->info.r600_has_virtual_memory)
pb_slabs_deinit(&ws->bo_slabs);
fail_cache:
pb_cache_deinit(&ws->bo_cache);
fail1:
mtx_unlock(&fd_tab_mutex);
if (ws->surf_man)
radeon_surface_manager_free(ws->surf_man);
if (ws->fd >= 0)
close(ws->fd);
FREE(ws);
return NULL;
}