blob: f8f9d4fcdf1898307db0b408ffe6c871ed677e96 [file] [log] [blame]
/*
* Copyright (C) 2005 Aapo Tahkola.
*
* 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, 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 COPYRIGHT OWNER(S) 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.
*
*/
/**
* \file
*
* \author Aapo Tahkola <aet@rasterburn.org>
*/
#include <unistd.h>
#include "r300_context.h"
#include "r300_cmdbuf.h"
#include "r300_ioctl.h"
#include "r300_mem.h"
#include "radeon_ioctl.h"
#ifdef USER_BUFFERS
static void resize_u_list(r300ContextPtr rmesa)
{
void *temp;
int nsize;
temp = rmesa->rmm->u_list;
nsize = rmesa->rmm->u_size * 2;
rmesa->rmm->u_list = _mesa_malloc(nsize * sizeof(*rmesa->rmm->u_list));
_mesa_memset(rmesa->rmm->u_list, 0,
nsize * sizeof(*rmesa->rmm->u_list));
if (temp) {
r300FlushCmdBuf(rmesa, __FUNCTION__);
_mesa_memcpy(rmesa->rmm->u_list, temp,
rmesa->rmm->u_size * sizeof(*rmesa->rmm->u_list));
_mesa_free(temp);
}
rmesa->rmm->u_size = nsize;
}
void r300_mem_init(r300ContextPtr rmesa)
{
rmesa->rmm = malloc(sizeof(struct r300_memory_manager));
memset(rmesa->rmm, 0, sizeof(struct r300_memory_manager));
rmesa->rmm->u_size = 128;
resize_u_list(rmesa);
}
void r300_mem_destroy(r300ContextPtr rmesa)
{
_mesa_free(rmesa->rmm->u_list);
rmesa->rmm->u_list = NULL;
_mesa_free(rmesa->rmm);
rmesa->rmm = NULL;
}
void *r300_mem_ptr(r300ContextPtr rmesa, int id)
{
assert(id <= rmesa->rmm->u_last);
return rmesa->rmm->u_list[id].ptr;
}
int r300_mem_find(r300ContextPtr rmesa, void *ptr)
{
int i;
for (i = 1; i < rmesa->rmm->u_size + 1; i++)
if (rmesa->rmm->u_list[i].ptr &&
ptr >= rmesa->rmm->u_list[i].ptr &&
ptr <
rmesa->rmm->u_list[i].ptr + rmesa->rmm->u_list[i].size)
break;
if (i < rmesa->rmm->u_size + 1)
return i;
fprintf(stderr, "%p failed\n", ptr);
return 0;
}
//#define MM_DEBUG
int r300_mem_alloc(r300ContextPtr rmesa, int alignment, int size)
{
drm_radeon_mem_alloc_t alloc;
int offset = 0, ret;
int i, free = -1;
int done_age;
drm_radeon_mem_free_t memfree;
int tries = 0;
static int bytes_wasted = 0, allocated = 0;
if (size < 4096)
bytes_wasted += 4096 - size;
allocated += size;
#if 0
static int t = 0;
if (t != time(NULL)) {
t = time(NULL);
fprintf(stderr, "slots used %d, wasted %d kb, allocated %d\n",
rmesa->rmm->u_last, bytes_wasted / 1024,
allocated / 1024);
}
#endif
memfree.region = RADEON_MEM_REGION_GART;
again:
done_age = radeonGetAge((radeonContextPtr) rmesa);
if (rmesa->rmm->u_last + 1 >= rmesa->rmm->u_size)
resize_u_list(rmesa);
for (i = rmesa->rmm->u_last + 1; i > 0; i--) {
if (rmesa->rmm->u_list[i].ptr == NULL) {
free = i;
continue;
}
if (rmesa->rmm->u_list[i].h_pending == 0 &&
rmesa->rmm->u_list[i].pending
&& rmesa->rmm->u_list[i].age <= done_age) {
memfree.region_offset =
(char *)rmesa->rmm->u_list[i].ptr -
(char *)rmesa->radeon.radeonScreen->gartTextures.
map;
ret =
drmCommandWrite(rmesa->radeon.radeonScreen->
driScreen->fd, DRM_RADEON_FREE,
&memfree, sizeof(memfree));
if (ret) {
fprintf(stderr, "Failed to free at %p\n",
rmesa->rmm->u_list[i].ptr);
fprintf(stderr, "ret = %s\n", strerror(-ret));
exit(1);
} else {
#ifdef MM_DEBUG
fprintf(stderr, "really freed %d at age %x\n",
i,
radeonGetAge((radeonContextPtr) rmesa));
#endif
if (i == rmesa->rmm->u_last)
rmesa->rmm->u_last--;
if (rmesa->rmm->u_list[i].size < 4096)
bytes_wasted -=
4096 - rmesa->rmm->u_list[i].size;
allocated -= rmesa->rmm->u_list[i].size;
rmesa->rmm->u_list[i].pending = 0;
rmesa->rmm->u_list[i].ptr = NULL;
free = i;
}
}
}
rmesa->rmm->u_head = i;
if (free == -1) {
WARN_ONCE("Ran out of slots!\n");
//usleep(100);
r300FlushCmdBuf(rmesa, __FUNCTION__);
tries++;
if (tries > 100) {
WARN_ONCE("Ran out of slots!\n");
exit(1);
}
goto again;
}
alloc.region = RADEON_MEM_REGION_GART;
alloc.alignment = alignment;
alloc.size = size;
alloc.region_offset = &offset;
ret =
drmCommandWriteRead(rmesa->radeon.dri.fd, DRM_RADEON_ALLOC, &alloc,
sizeof(alloc));
if (ret) {
#if 0
WARN_ONCE("Ran out of mem!\n");
r300FlushCmdBuf(rmesa, __FUNCTION__);
//usleep(100);
tries2++;
tries = 0;
if (tries2 > 100) {
WARN_ONCE("Ran out of GART memory!\n");
exit(1);
}
goto again;
#else
WARN_ONCE
("Ran out of GART memory (for %d)!\nPlease consider adjusting GARTSize option.\n",
size);
return 0;
#endif
}
i = free;
if (i > rmesa->rmm->u_last)
rmesa->rmm->u_last = i;
rmesa->rmm->u_list[i].ptr =
((GLubyte *) rmesa->radeon.radeonScreen->gartTextures.map) + offset;
rmesa->rmm->u_list[i].size = size;
rmesa->rmm->u_list[i].age = 0;
//fprintf(stderr, "alloc %p at id %d\n", rmesa->rmm->u_list[i].ptr, i);
#ifdef MM_DEBUG
fprintf(stderr, "allocated %d at age %x\n", i,
radeonGetAge((radeonContextPtr) rmesa));
#endif
return i;
}
void r300_mem_use(r300ContextPtr rmesa, int id)
{
uint64_t ull;
#ifdef MM_DEBUG
fprintf(stderr, "%s: %d at age %x\n", __FUNCTION__, id,
radeonGetAge((radeonContextPtr) rmesa));
#endif
drm_r300_cmd_header_t *cmd;
assert(id <= rmesa->rmm->u_last);
if (id == 0)
return;
cmd =
(drm_r300_cmd_header_t *) r300AllocCmdBuf(rmesa,
2 + sizeof(ull) / 4,
__FUNCTION__);
cmd[0].scratch.cmd_type = R300_CMD_SCRATCH;
cmd[0].scratch.reg = R300_MEM_SCRATCH;
cmd[0].scratch.n_bufs = 1;
cmd[0].scratch.flags = 0;
cmd++;
ull = (uint64_t) (intptr_t) & rmesa->rmm->u_list[id].age;
_mesa_memcpy(cmd, &ull, sizeof(ull));
cmd += sizeof(ull) / 4;
cmd[0].u = /*id */ 0;
LOCK_HARDWARE(&rmesa->radeon); /* Protect from DRM. */
rmesa->rmm->u_list[id].h_pending++;
UNLOCK_HARDWARE(&rmesa->radeon);
}
unsigned long r300_mem_offset(r300ContextPtr rmesa, int id)
{
unsigned long offset;
assert(id <= rmesa->rmm->u_last);
offset = (char *)rmesa->rmm->u_list[id].ptr -
(char *)rmesa->radeon.radeonScreen->gartTextures.map;
offset += rmesa->radeon.radeonScreen->gart_texture_offset;
return offset;
}
void *r300_mem_map(r300ContextPtr rmesa, int id, int access)
{
#ifdef MM_DEBUG
fprintf(stderr, "%s: %d at age %x\n", __FUNCTION__, id,
radeonGetAge((radeonContextPtr) rmesa));
#endif
void *ptr;
int tries = 0;
assert(id <= rmesa->rmm->u_last);
if (access == R300_MEM_R) {
if (rmesa->rmm->u_list[id].mapped == 1)
WARN_ONCE("buffer %d already mapped\n", id);
rmesa->rmm->u_list[id].mapped = 1;
ptr = r300_mem_ptr(rmesa, id);
return ptr;
}
if (rmesa->rmm->u_list[id].h_pending)
r300FlushCmdBuf(rmesa, __FUNCTION__);
if (rmesa->rmm->u_list[id].h_pending) {
return NULL;
}
while (rmesa->rmm->u_list[id].age >
radeonGetAge((radeonContextPtr) rmesa) && tries++ < 1000)
usleep(10);
if (tries >= 1000) {
fprintf(stderr, "Idling failed (%x vs %x)\n",
rmesa->rmm->u_list[id].age,
radeonGetAge((radeonContextPtr) rmesa));
return NULL;
}
if (rmesa->rmm->u_list[id].mapped == 1)
WARN_ONCE("buffer %d already mapped\n", id);
rmesa->rmm->u_list[id].mapped = 1;
ptr = r300_mem_ptr(rmesa, id);
return ptr;
}
void r300_mem_unmap(r300ContextPtr rmesa, int id)
{
#ifdef MM_DEBUG
fprintf(stderr, "%s: %d at age %x\n", __FUNCTION__, id,
radeonGetAge((radeonContextPtr) rmesa));
#endif
assert(id <= rmesa->rmm->u_last);
if (rmesa->rmm->u_list[id].mapped == 0)
WARN_ONCE("buffer %d not mapped\n", id);
rmesa->rmm->u_list[id].mapped = 0;
}
void r300_mem_free(r300ContextPtr rmesa, int id)
{
#ifdef MM_DEBUG
fprintf(stderr, "%s: %d at age %x\n", __FUNCTION__, id,
radeonGetAge((radeonContextPtr) rmesa));
#endif
assert(id <= rmesa->rmm->u_last);
if (id == 0)
return;
if (rmesa->rmm->u_list[id].ptr == NULL) {
WARN_ONCE("Not allocated!\n");
return;
}
if (rmesa->rmm->u_list[id].pending) {
WARN_ONCE("%p already pended!\n", rmesa->rmm->u_list[id].ptr);
return;
}
rmesa->rmm->u_list[id].pending = 1;
}
#endif