diff options
-rw-r--r-- | src/gallium/drivers/ilo/Makefile.sources | 1 | ||||
-rw-r--r-- | src/gallium/drivers/ilo/ilo_cp.c | 323 | ||||
-rw-r--r-- | src/gallium/drivers/ilo/ilo_cp.h | 325 |
3 files changed, 649 insertions, 0 deletions
diff --git a/src/gallium/drivers/ilo/Makefile.sources b/src/gallium/drivers/ilo/Makefile.sources index 157f6c748f1..8911322e9cd 100644 --- a/src/gallium/drivers/ilo/Makefile.sources +++ b/src/gallium/drivers/ilo/Makefile.sources @@ -2,6 +2,7 @@ C_SOURCES := \ ilo_3d.c \ ilo_blit.c \ ilo_context.c \ + ilo_cp.c \ ilo_format.c \ ilo_gpgpu.c \ ilo_query.c \ diff --git a/src/gallium/drivers/ilo/ilo_cp.c b/src/gallium/drivers/ilo/ilo_cp.c new file mode 100644 index 00000000000..e0e53d91cd7 --- /dev/null +++ b/src/gallium/drivers/ilo/ilo_cp.c @@ -0,0 +1,323 @@ +/* + * Mesa 3-D graphics library + * + * Copyright (C) 2012-2013 LunarG, 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 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. + * + * Authors: + * Chia-I Wu <[email protected]> + */ + +#include "intel_reg.h" /* for MI_xxx */ +#include "intel_winsys.h" + +#include "ilo_cp.h" + +/* the size of the private space */ +static const int ilo_cp_private = 2; + +/** + * Dump the contents of the parser bo. This must be called in a post-flush + * hook. + */ +void +ilo_cp_dump(struct ilo_cp *cp) +{ + ilo_printf("dumping %d bytes\n", cp->used * 4); + if (cp->used) + cp->winsys->decode_commands(cp->winsys, cp->bo, cp->used * 4); +} + +/** + * Save the command parser state for rewind. + * + * Note that this cannot rewind a flush, and the caller must make sure + * that does not happend. + */ +void +ilo_cp_setjmp(struct ilo_cp *cp, struct ilo_cp_jmp_buf *jmp) +{ + jmp->id = pointer_to_intptr(cp->bo); + + jmp->size = cp->size; + jmp->used = cp->used; + jmp->stolen = cp->stolen; + /* save reloc count to rewind ilo_cp_write_bo() */ + jmp->reloc_count = cp->bo->get_reloc_count(cp->bo); +} + +/** + * Rewind to the saved state. + */ +void +ilo_cp_longjmp(struct ilo_cp *cp, const struct ilo_cp_jmp_buf *jmp) +{ + if (jmp->id != pointer_to_intptr(cp->bo)) { + assert(!"invalid use of CP longjmp"); + return; + } + + cp->size = jmp->size; + cp->used = jmp->used; + cp->stolen = jmp->stolen; + cp->bo->clear_relocs(cp->bo, jmp->reloc_count); +} + +/** + * Clear the parser buffer. + */ +static void +ilo_cp_clear_buffer(struct ilo_cp *cp) +{ + cp->cmd_cur = 0; + cp->cmd_end = 0; + + cp->used = 0; + cp->stolen = 0; + + /* + * Recalculate cp->size. This is needed not only because cp->stolen is + * reset above, but also that we added cp->reserve_for_pre_flush and + * ilo_cp_private to cp->size in ilo_cp_flush(). + */ + cp->size = cp->bo_size - (cp->reserve_for_pre_flush + ilo_cp_private); +} + +/** + * Add MI_BATCH_BUFFER_END to the private space of the parser buffer. + */ +static void +ilo_cp_end_buffer(struct ilo_cp *cp) +{ + /* make the private space available */ + cp->size += ilo_cp_private; + + assert(cp->used + 2 <= cp->size); + + cp->ptr[cp->used++] = MI_BATCH_BUFFER_END; + + /* + * From the Sandy Bridge PRM, volume 1 part 1, page 107: + * + * "The batch buffer must be QWord aligned and a multiple of QWords in + * length." + */ + if (cp->used & 1) + cp->ptr[cp->used++] = MI_NOOP; +} + +/** + * Upload the parser buffer to the bo. + */ +static int +ilo_cp_upload_buffer(struct ilo_cp *cp) +{ + int err; + + err = cp->bo->pwrite(cp->bo, 0, cp->used * 4, cp->ptr); + if (likely(!err && cp->stolen)) { + const int offset = cp->bo_size - cp->stolen; + + err = cp->bo->pwrite(cp->bo, offset * 4, + cp->stolen * 4, &cp->ptr[offset]); + } + + return err; +} + +/** + * Reallocate the parser bo. + */ +static void +ilo_cp_realloc_bo(struct ilo_cp *cp) +{ + struct intel_bo *bo; + + /* + * allocate the new bo before unreferencing the old one so that they + * won't point at the same address, which is needed for jmpbuf + */ + bo = cp->winsys->alloc_buffer(cp->winsys, + "batch buffer", cp->bo_size * 4, 0); + if (unlikely(!bo)) + return; + + if (cp->bo) + cp->bo->unreference(cp->bo); + cp->bo = bo; + + if (!cp->sys) { + cp->bo->map(cp->bo, true); + cp->ptr = cp->bo->get_virtual(cp->bo); + } +} + +/** + * Execute the parser bo. + */ +static int +ilo_cp_exec_bo(struct ilo_cp *cp) +{ + const bool do_exec = !(ilo_debug & ILO_DEBUG_NOHW); + unsigned long flags; + int err; + + switch (cp->ring) { + case ILO_CP_RING_RENDER: + flags = INTEL_EXEC_RENDER; + break; + case ILO_CP_RING_BLT: + flags = INTEL_EXEC_BLT; + break; + default: + flags = 0; + break; + } + + if (likely(do_exec)) + err = cp->bo->exec(cp->bo, cp->used * 4, cp->hw_ctx, flags); + else + err = 0; + + return err; +} + +static void +ilo_cp_call_hook(struct ilo_cp *cp, enum ilo_cp_hook hook) +{ + const bool no_implicit_flush = cp->no_implicit_flush; + + if (!cp->hooks[hook].func) + return; + + /* no implicit flush in hooks */ + cp->no_implicit_flush = true; + cp->hooks[hook].func(cp, cp->hooks[hook].data); + + cp->no_implicit_flush = no_implicit_flush; +} + +/** + * Flush the command parser and execute the commands. When the parser buffer + * is empty, the hooks are not invoked. + */ +void +ilo_cp_flush(struct ilo_cp *cp) +{ + int err; + + /* sanity check */ + assert(cp->bo_size == cp->size + + cp->reserve_for_pre_flush + ilo_cp_private + cp->stolen); + + if (!cp->used) { + ilo_cp_clear_buffer(cp); + return; + } + + /* make the reserved space available temporarily */ + cp->size += cp->reserve_for_pre_flush; + ilo_cp_call_hook(cp, ILO_CP_HOOK_PRE_FLUSH); + + ilo_cp_end_buffer(cp); + + if (cp->sys) { + err = ilo_cp_upload_buffer(cp); + if (likely(!err)) + err = ilo_cp_exec_bo(cp); + } + else { + cp->bo->unmap(cp->bo); + err = ilo_cp_exec_bo(cp); + } + + if (likely(!err)) { + ilo_cp_call_hook(cp, ILO_CP_HOOK_POST_FLUSH); + ilo_cp_clear_buffer(cp); + } + else { + /* reset first so that post-flush hook knows nothing was executed */ + ilo_cp_clear_buffer(cp); + ilo_cp_call_hook(cp, ILO_CP_HOOK_POST_FLUSH); + } + + ilo_cp_realloc_bo(cp); + ilo_cp_call_hook(cp, ILO_CP_HOOK_NEW_BATCH); +} + +/** + * Destroy the command parser. + */ +void +ilo_cp_destroy(struct ilo_cp *cp) +{ + if (cp->bo) + cp->bo->unreference(cp->bo); + if (cp->hw_ctx) + cp->winsys->destroy_context(cp->winsys, cp->hw_ctx); + + FREE(cp->sys); + FREE(cp); +} + +/** + * Create a command parser. + */ +struct ilo_cp * +ilo_cp_create(struct intel_winsys *winsys, bool direct_map) +{ + struct ilo_cp *cp; + + cp = CALLOC_STRUCT(ilo_cp); + if (!cp) + return NULL; + + cp->winsys = winsys; + cp->hw_ctx = winsys->create_context(winsys); + + cp->ring = ILO_CP_RING_RENDER; + cp->no_implicit_flush = false; + cp->reserve_for_pre_flush = 0; + + memset(cp->hooks, 0, sizeof(cp->hooks)); + + cp->bo_size = 8192; + + if (!direct_map) { + cp->sys = MALLOC(cp->bo_size * 4); + if (!cp->sys) { + FREE(cp); + return NULL; + } + + cp->ptr = cp->sys; + } + + ilo_cp_realloc_bo(cp); + if (!cp->bo) { + FREE(cp->sys); + FREE(cp); + return NULL; + } + + ilo_cp_clear_buffer(cp); + + return cp; +} diff --git a/src/gallium/drivers/ilo/ilo_cp.h b/src/gallium/drivers/ilo/ilo_cp.h new file mode 100644 index 00000000000..8f3bda48267 --- /dev/null +++ b/src/gallium/drivers/ilo/ilo_cp.h @@ -0,0 +1,325 @@ +/* + * Mesa 3-D graphics library + * + * Copyright (C) 2012-2013 LunarG, 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 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. + * + * Authors: + * Chia-I Wu <[email protected]> + */ + +#ifndef ILO_CP_H +#define ILO_CP_H + +#include "intel_winsys.h" + +#include "ilo_common.h" + +struct ilo_cp; + +enum ilo_cp_ring { + ILO_CP_RING_RENDER, + ILO_CP_RING_BLT, + + ILO_CP_RING_COUNT, +}; + +enum ilo_cp_hook { + ILO_CP_HOOK_NEW_BATCH, + ILO_CP_HOOK_PRE_FLUSH, + ILO_CP_HOOK_POST_FLUSH, + + ILO_CP_HOOK_COUNT, +}; + +typedef void (*ilo_cp_hook_func)(struct ilo_cp *cp, void *data); + +/** + * Command parser. + */ +struct ilo_cp { + struct intel_winsys *winsys; + struct intel_context *hw_ctx; + + enum ilo_cp_ring ring; + bool no_implicit_flush; + int reserve_for_pre_flush; + + struct { + ilo_cp_hook_func func; + void *data; + } hooks[ILO_CP_HOOK_COUNT]; + + int bo_size; + struct intel_bo *bo; + uint32_t *sys; + + uint32_t *ptr; + int size, used, stolen; + + int cmd_cur, cmd_end; +}; + +/** + * Jump buffer to save command parser state for rewind. + */ +struct ilo_cp_jmp_buf { + intptr_t id; + int size, used, stolen; + int reloc_count; +}; + +struct ilo_cp * +ilo_cp_create(struct intel_winsys *winsys, bool direct_map); + +void +ilo_cp_destroy(struct ilo_cp *cp); + +void +ilo_cp_flush(struct ilo_cp *cp); + +void +ilo_cp_dump(struct ilo_cp *cp); + +void +ilo_cp_setjmp(struct ilo_cp *cp, struct ilo_cp_jmp_buf *jmp); + +void +ilo_cp_longjmp(struct ilo_cp *cp, const struct ilo_cp_jmp_buf *jmp); + +/** + * Return true if the parser buffer is empty. + */ +static inline bool +ilo_cp_empty(struct ilo_cp *cp) +{ + return !cp->used; +} + +/** + * Return the remaining space (in dwords) in the parser buffer. + */ +static inline int +ilo_cp_space(struct ilo_cp *cp) +{ + return cp->size - cp->used; +} + +/** + * Internal function called by functions that flush implicitly. + */ +static inline void +ilo_cp_implicit_flush(struct ilo_cp *cp) +{ + if (cp->no_implicit_flush) { + assert(!"unexpected command parser flush"); + /* discard the commands */ + cp->used = 0; + } + + ilo_cp_flush(cp); +} + +/** + * Set the ring buffer. + */ +static inline void +ilo_cp_set_ring(struct ilo_cp *cp, enum ilo_cp_ring ring) +{ + if (cp->ring != ring) { + ilo_cp_implicit_flush(cp); + cp->ring = ring; + } +} + +/** + * Assert that no function should flush implicitly. + */ +static inline void +ilo_cp_assert_no_implicit_flush(struct ilo_cp *cp, bool enable) +{ + cp->no_implicit_flush = enable; +} + +/** + * Reserve the given size of space from the parser buffer. The reserved space + * will be made available temporarily for the pre-flush hook. + * + * \param reserve size in dwords to reserve. It may be negative. + */ +static inline void +ilo_cp_reserve_for_pre_flush(struct ilo_cp *cp, int reserve) +{ + assert(cp->reserve_for_pre_flush + reserve >= 0); + + if (cp->used > cp->size - reserve) { + ilo_cp_implicit_flush(cp); + assert(cp->used <= cp->size - reserve); + } + + cp->size -= reserve; + cp->reserve_for_pre_flush += reserve; +} + +/** + * Set a command parser hook. + */ +static inline void +ilo_cp_set_hook(struct ilo_cp *cp, enum ilo_cp_hook hook, + ilo_cp_hook_func func, void *data) +{ + cp->hooks[hook].func = func; + cp->hooks[hook].data = data; +} + +/** + * Begin writing a command. + */ +static inline void +ilo_cp_begin(struct ilo_cp *cp, int cmd_size) +{ + if (cp->used + cmd_size > cp->size) { + ilo_cp_implicit_flush(cp); + assert(cp->used + cmd_size <= cp->size); + } + + assert(cp->cmd_cur == cp->cmd_end); + cp->cmd_cur = cp->used; + cp->cmd_end = cp->cmd_cur + cmd_size; + cp->used = cp->cmd_end; +} + +/** + * Begin writing data to a space stolen from the top of the parser buffer. + * + * \param desc informative description of the data to be written + * \param data_size in dwords + * \param align in dwords + * \param bo_offset in bytes to the stolen space + */ +static inline void +ilo_cp_steal(struct ilo_cp *cp, const char *desc, + int data_size, int align, uint32_t *bo_offset) +{ + int pad, steal; + + if (!align) + align = 1; + + pad = (cp->bo_size - cp->stolen - data_size) % align; + steal = data_size + pad; + + /* flush if there is not enough space after stealing */ + if (cp->used > cp->size - steal) { + ilo_cp_implicit_flush(cp); + + pad = (cp->bo_size - cp->stolen - data_size) % align; + steal = data_size + steal; + + assert(cp->used <= cp->size - steal); + } + + cp->size -= steal; + cp->stolen += steal; + + assert(cp->cmd_cur == cp->cmd_end); + cp->cmd_cur = cp->bo_size - cp->stolen; + cp->cmd_end = cp->cmd_cur + data_size; + + /* offset in cp->bo */ + if (bo_offset) + *bo_offset = cp->cmd_cur * 4; +} + +/** + * Write a dword to the parser buffer. This function must be enclosed by + * ilo_cp_begin()/ilo_cp_steal() and ilo_cp_end(). + */ +static inline void +ilo_cp_write(struct ilo_cp *cp, uint32_t val) +{ + assert(cp->cmd_cur < cp->cmd_end); + cp->ptr[cp->cmd_cur++] = val; +} + +/** + * Write multiple dwords to the parser buffer. + */ +static inline void +ilo_cp_write_multi(struct ilo_cp *cp, const void *vals, int num_vals) +{ + assert(cp->cmd_cur + num_vals <= cp->cmd_end); + memcpy(cp->ptr + cp->cmd_cur, vals, num_vals * 4); + cp->cmd_cur += num_vals; +} + +/** + * Write a bo to the parser buffer. In addition to writing the offset of the + * bo to the buffer, it also emits a relocation. + */ +static inline void +ilo_cp_write_bo(struct ilo_cp *cp, uint32_t val, struct intel_bo *bo, + uint32_t read_domains, uint32_t write_domain) +{ + if (bo) { + cp->bo->emit_reloc(cp->bo, cp->cmd_cur * 4, + bo, val, read_domains, write_domain); + + ilo_cp_write(cp, val + bo->get_offset(bo)); + } + else { + ilo_cp_write(cp, val); + } +} + +/** + * End a command. Every ilo_cp_begin() or ilo_cp_steal() must have a + * matching ilo_cp_end(). + */ +static inline void +ilo_cp_end(struct ilo_cp *cp) +{ + assert(cp->cmd_cur == cp->cmd_end); +} + +/** + * A variant of ilo_cp_steal() where the data are written via the returned + * pointer. + * + * \return ptr pointer where the data are written to. It is valid until any + * change is made to the parser. + */ +static inline void * +ilo_cp_steal_ptr(struct ilo_cp *cp, const char *desc, + int data_size, int align, uint32_t *bo_offset) +{ + void *ptr; + + ilo_cp_steal(cp, desc, data_size, align, bo_offset); + + ptr = &cp->ptr[cp->cmd_cur]; + cp->cmd_cur = cp->cmd_end; + + ilo_cp_end(cp); + + return ptr; +} + +#endif /* ILO_CP_H */ |