/* * Copyright 2010 Jerome Glisse * * 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 * on 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 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 NON-INFRINGEMENT. IN NO EVENT SHALL * THE AUTHOR(S) AND/OR THEIR 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. * * Authors: * Jerome Glisse */ #include "r600.h" #include "r600d.h" /* state creation functions prototype */ int r600_framebuffer_emit(struct r600_winsys *rdev, struct r600_atom *atom, void *data, struct radeon_ib *ib); int r600_framebuffer_create(struct r600_winsys *rdev, struct r600_atom *atom, void *data); int r600_cb_cntl_create(struct r600_winsys *rdev, struct r600_atom *atom, void *data); int r600_rasterizer_create(struct r600_winsys *rdev, struct r600_atom *atom, void *data); int r600_viewport_create(struct r600_winsys *rdev, struct r600_atom *atom, void *data); int r600_scissor_create(struct r600_winsys *rdev, struct r600_atom *atom, void *data); int r600_blend_create(struct r600_winsys *rdev, struct r600_atom *atom, void *data); int r600_constants_create(struct r600_winsys *rdev, struct r600_atom *atom, void *data); int r600_dsa_create(struct r600_winsys *rdev, struct r600_atom *atom, void *data); int r600_vs_shader_emit(struct r600_winsys *rdev, struct r600_atom *atom, void *data, struct radeon_ib *ib); int r600_vs_shader_create(struct r600_winsys *rdev, struct r600_atom *atom, void *data); int r600_fs_shader_emit(struct r600_winsys *rdev, struct r600_atom *atom, void *data, struct radeon_ib *ib); int r600_fs_shader_create(struct r600_winsys *rdev, struct r600_atom *atom, void *data); int r600_shader_resource_emit(struct r600_winsys *rdev, struct r600_atom *atom, void *data, struct radeon_ib *ib); int r600_shader_resource_create(struct r600_winsys *rdev, struct r600_atom *atom, void *data); /* * helpers functions */ static void r600_emit_flush(struct r600_winsys *rdev, struct radeon_ib *ib, struct radeon_bo *bo, u32 flags) { ib->ptr[ib->cpkts++] = PKT3(PKT3_SURFACE_SYNC, 3); ib->ptr[ib->cpkts++] = flags; ib->ptr[ib->cpkts++] = bo->size >> 8; ib->ptr[ib->cpkts++] = 0x00000000; ib->ptr[ib->cpkts++] = 0x0000000A; ib->ptr[ib->cpkts++] = PKT3(PKT3_NOP, 0); ib->ptr[ib->cpkts++] = radeon_ib_reloc(ib, bo, RADEON_GEM_DOMAIN_VRAM | RADEON_GEM_DOMAIN_GTT); } void r600_atom_flush_add(struct r600_work *batch, struct radeon_bo *bo, u32 flags) { int i; for (i = 0; i < batch->nflush; i++) { if (batch->flush[i].bo == bo) { batch->flush[i].flags |= flags; return; } } batch->flush[batch->nflush].bo = bo; batch->flush[batch->nflush].flags = flags; batch->nflush++; } /* * r600_scheduler */ static void r600_scheduler_clear_locked(struct r600_winsys *rw, struct r600_scheduler *scheduler) { struct r600_work *batch, *n; int i, j; for (i = 0; i < scheduler->nwork; i++) { for (j = 0; j < R600_BATCH_NATOMS; j++) { r600_atom_destroy(scheduler->work[i].atoms[j]); } } scheduler->ib->cpkts = 0; scheduler->nwork = 0; r700_scheduler_states_default(rw, scheduler); scheduler->npkts = scheduler->ib->cpkts; } int r600_scheduler_flush(struct r600_winsys *rw) { struct r600_scheduler *scheduler = &rw->scheduler; int r, i, j; for (i = 0; i < scheduler->nwork; i++) { for (j = 0; j < scheduler->work[i].nflush; j++) { r600_emit_flush(rw, scheduler->ib, scheduler->work[i].flush[j].bo, scheduler->work[i].flush[j].flags); } for (j = 0; j < scheduler->work[i].nemit_atoms; j++) { r = scheduler->work[i].emit_atoms[j]->emit(rw, scheduler->work[i].emit_atoms[j], &scheduler->work[i], scheduler->ib); if (r) goto out_err; } r = r600_draw_cmd_emit(scheduler->ib, &scheduler->work[i].drm); /* flush + wait until */ scheduler->ib->ptr[scheduler->ib->cpkts++] = PKT3(PKT3_EVENT_WRITE, 0); scheduler->ib->ptr[scheduler->ib->cpkts++] = 0x00000016; scheduler->ib->ptr[scheduler->ib->cpkts++] = PKT3(PKT3_SET_CONFIG_REG, 1); scheduler->ib->ptr[scheduler->ib->cpkts++] = 0x00000010; scheduler->ib->ptr[scheduler->ib->cpkts++] = 0x00028000; } printf("ib %d dw\n", scheduler->ib->cpkts); r = radeon_ib_schedule(rw, scheduler->ib); out_err: /* FIXME helper function */ scheduler->ib->cpkts = 0; scheduler->ib->nrelocs = 0; r600_scheduler_clear_locked(rw, scheduler); return r; } int r600_scheduler_queue(struct r600_winsys *rw, struct r600_request *rq) { struct r600_batch *batch = rq->data; struct r600_work *rbatch; struct r600_scheduler *scheduler = &rw->scheduler; int r, i, j; if (scheduler->nwork >= R600_MAX_WORK) { r = r600_scheduler_flush(rw); if (r) return r; } rbatch = &scheduler->work[scheduler->nwork]; memset(rbatch, 0, sizeof(struct r600_work)); i = 0; if (batch->blend == NULL || batch->cb_cntl == NULL || batch->rasterizer == NULL || batch->viewport == NULL || batch->framebuffer == NULL || batch->dsa == NULL || batch->vs_shader == NULL || batch->fs_shader == NULL || batch->scissor == NULL || batch->vs_resource == NULL) { free(rbatch); fprintf(stderr, "invalid batch\n"); return -EINVAL; } rbatch->atoms[i++] = batch->blend; rbatch->atoms[i++] = batch->cb_cntl; rbatch->atoms[i++] = batch->rasterizer; rbatch->atoms[i++] = batch->viewport; rbatch->atoms[i++] = batch->scissor; rbatch->atoms[i++] = batch->vs_shader; rbatch->atoms[i++] = batch->fs_shader; rbatch->atoms[i++] = batch->dsa; rbatch->atoms[i++] = batch->vs_constants; rbatch->atoms[i++] = batch->ps_constants; rbatch->atoms[i++] = batch->vs_resource; rbatch->atoms[i++] = batch->framebuffer; for (i = 0; i < R600_BATCH_NATOMS; i++) { r600_atom_ref(rbatch->atoms[i]); } memcpy(&rbatch->drm, batch, sizeof(struct r600_batch)); reprocess: rbatch->nflush = 0; rbatch->npkts = 0; /* flush + wait until = 5dw */ rbatch->npkts += 5; rbatch->npkts += r600_draw_cmd_size(batch); for (i = 0; i < R600_BATCH_NATOMS; i++) { if (rbatch->atoms[i]) { for (j = 0; j < rbatch->atoms[i]->nbo; j++) { r600_atom_flush_add(rbatch, rbatch->atoms[i]->bo[j], rbatch->atoms[i]->flags[j]); } rbatch->emit_atoms[rbatch->nemit_atoms++] = rbatch->atoms[i]; rbatch->npkts += rbatch->atoms[i]->npkts; } } /* add flush */ rbatch->npkts += rbatch->nflush * 7; /* if batch is bigger than ib size it's an invalid one, this should * not happen */ if (rbatch->npkts > scheduler->ib->length_dw) { fprintf(stderr, "single batch to big (%d) to fit into ib (%d)\n", rbatch->npkts, scheduler->ib->length_dw); goto out_err; } /* flush or not ? */ if (scheduler->npkts + rbatch->npkts > scheduler->ib->length_dw) { r = r600_scheduler_flush(rw); if (r) goto out_err; goto reprocess; } /* batch is queued */ for (i = 0; i < R600_BATCH_NATOMS; i++) { if (rbatch->atoms[i]) { scheduler->last_id[i] = rbatch->atoms[i]->id; } } printf("batch %d dw scheduler with %d dw\n", rbatch->npkts, scheduler->npkts); scheduler->npkts += rbatch->npkts; scheduler->nwork++; return 0; out_err: for (i = 0; i < R600_BATCH_NATOMS; i++) { if (rbatch->atoms[i]) r600_atom_destroy(rbatch->atoms[i]); } free(rbatch); return r; } static int r600_scheduler_init(struct r600_winsys *rw, struct r600_scheduler *scheduler) { int r; memset(scheduler, 0 , sizeof(struct r600_scheduler)); r = radeon_ib_get(rw, &scheduler->ib); if (r) return r; r600_scheduler_clear_locked(rw, scheduler); return 0; } static void r600_scheduler_cleanup_locked(struct r600_winsys *rw, struct r600_scheduler *scheduler) { r600_scheduler_clear_locked(rw, scheduler); radeon_ib_free(scheduler->ib); scheduler->ib = NULL; } int r600_atoms_init(struct r600_winsys *rdev) { rdev->npipes = 2; rdev->nbanks = 4; rdev->group_bytes = 256; return r600_scheduler_init(rdev, &rdev->scheduler); } void r600_atoms_release(struct r600_winsys *rdev) { r600_scheduler_cleanup_locked(rdev, &rdev->scheduler); }