Path: blob/21.2-virgl/src/gallium/drivers/i915/i915_prim_vbuf.c
4570 views
/**************************************************************************1*2* Copyright 2007 VMware, Inc.3* All Rights Reserved.4*5* Permission is hereby granted, free of charge, to any person obtaining a6* copy of this software and associated documentation files (the7* "Software"), to deal in the Software without restriction, including8* without limitation the rights to use, copy, modify, merge, publish,9* distribute, sub license, and/or sell copies of the Software, and to10* permit persons to whom the Software is furnished to do so, subject to11* the following conditions:12*13* The above copyright notice and this permission notice (including the14* next paragraph) shall be included in all copies or substantial portions15* of the Software.16*17* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS18* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF19* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.20* IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR21* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,22* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE23* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.24*25**************************************************************************/2627/**28* \file29* Build post-transformation, post-clipping vertex buffers and element30* lists by hooking into the end of the primitive pipeline and31* manipulating the vertex_id field in the vertex headers.32*33* XXX: work in progress34*35* \author José Fonseca <[email protected]>36* \author Keith Whitwell <[email protected]>37*/3839#include "draw/draw_context.h"40#include "draw/draw_vbuf.h"41#include "util/u_debug.h"42#include "util/u_fifo.h"43#include "util/u_inlines.h"44#include "util/u_math.h"45#include "util/u_memory.h"4647#include "i915_batch.h"48#include "i915_context.h"49#include "i915_reg.h"50#include "i915_state.h"5152#define VBUF_MAP_BUFFER5354/**55* Primitive renderer for i915.56*/57struct i915_vbuf_render {58struct vbuf_render base;5960struct i915_context *i915;6162/** Vertex size in bytes */63size_t vertex_size;6465/** Software primitive */66unsigned prim;6768/** Hardware primitive */69unsigned hwprim;7071/** Genereate a vertex list */72unsigned fallback;7374/* Stuff for the vbo */75struct i915_winsys_buffer *vbo;76size_t vbo_size; /**< current size of allocated buffer */77size_t vbo_alloc_size; /**< minimum buffer size to allocate */78size_t vbo_hw_offset; /**< offset that we program the hardware with */79size_t vbo_sw_offset; /**< offset that we work with */80size_t vbo_index; /**< index offset to be added to all indices */81void *vbo_ptr;82size_t vbo_max_used;83size_t vbo_max_index; /**< index offset to be added to all indices */8485#ifndef VBUF_MAP_BUFFER86size_t map_used_start;87size_t map_used_end;88size_t map_size;89#endif90};9192/**93* Basically a cast wrapper.94*/95static inline struct i915_vbuf_render *96i915_vbuf_render(struct vbuf_render *render)97{98assert(render);99return (struct i915_vbuf_render *)render;100}101102/**103* If vbo state differs between renderer and context104* push state to the context. This function pushes105* hw_offset to i915->vbo_offset and vbo to i915->vbo.106*107* Side effects:108* May updates context vbo_offset and vbo fields.109*/110static void111i915_vbuf_update_vbo_state(struct vbuf_render *render)112{113struct i915_vbuf_render *i915_render = i915_vbuf_render(render);114struct i915_context *i915 = i915_render->i915;115116if (i915->vbo != i915_render->vbo ||117i915->vbo_offset != i915_render->vbo_hw_offset) {118i915->vbo = i915_render->vbo;119i915->vbo_offset = i915_render->vbo_hw_offset;120i915->dirty |= I915_NEW_VBO;121}122}123124/**125* Callback exported to the draw module.126* Returns the current vertex_info.127*128* Side effects:129* If state is dirty update derived state.130*/131static const struct vertex_info *132i915_vbuf_render_get_vertex_info(struct vbuf_render *render)133{134struct i915_vbuf_render *i915_render = i915_vbuf_render(render);135struct i915_context *i915 = i915_render->i915;136137if (i915->dirty) {138/* make sure we have up to date vertex layout */139i915_update_derived(i915);140}141142return &i915->current.vertex_info;143}144145/**146* Reserve space in the vbo for vertices.147*148* Side effects:149* None.150*/151static bool152i915_vbuf_render_reserve(struct i915_vbuf_render *i915_render, size_t size)153{154struct i915_context *i915 = i915_render->i915;155156if (i915_render->vbo_size < size + i915_render->vbo_sw_offset)157return false;158159if (i915->vbo_flushed)160return false;161162return true;163}164165/**166* Allocate a new vbo buffer should there not be enough space for167* the requested number of vertices by the draw module.168*169* Side effects:170* Updates hw_offset, sw_offset, index and allocates a new buffer.171* Will set i915->vbo to null on buffer allocation.172*/173static void174i915_vbuf_render_new_buf(struct i915_vbuf_render *i915_render, size_t size)175{176struct i915_context *i915 = i915_render->i915;177struct i915_winsys *iws = i915->iws;178179if (i915_render->vbo) {180iws->buffer_unmap(iws, i915_render->vbo);181iws->buffer_destroy(iws, i915_render->vbo);182/*183* XXX If buffers where referenced then this should be done in184* update_vbo_state but since they arn't and malloc likes to reuse185* memory we need to set it to null186*/187i915->vbo = NULL;188i915_render->vbo = NULL;189}190191i915->vbo_flushed = 0;192193i915_render->vbo_size = MAX2(size, i915_render->vbo_alloc_size);194i915_render->vbo_hw_offset = 0;195i915_render->vbo_sw_offset = 0;196i915_render->vbo_index = 0;197198#ifndef VBUF_MAP_BUFFER199if (i915_render->vbo_size > i915_render->map_size) {200i915_render->map_size = i915_render->vbo_size;201FREE(i915_render->vbo_ptr);202i915_render->vbo_ptr = MALLOC(i915_render->map_size);203}204#endif205206i915_render->vbo =207iws->buffer_create(iws, i915_render->vbo_size, I915_NEW_VERTEX);208i915_render->vbo_ptr = iws->buffer_map(iws, i915_render->vbo, true);209}210211/**212* Callback exported to the draw module.213*214* Side effects:215* Updates hw_offset, sw_offset, index and may allocate216* a new buffer. Also updates may update the vbo state217* on the i915 context.218*/219static boolean220i915_vbuf_render_allocate_vertices(struct vbuf_render *render,221ushort vertex_size, ushort nr_vertices)222{223struct i915_vbuf_render *i915_render = i915_vbuf_render(render);224size_t size = (size_t)vertex_size * (size_t)nr_vertices;225size_t offset;226227/*228* Align sw_offset with first multiple of vertex size from hw_offset.229* Set index to be the multiples from from hw_offset to sw_offset.230* i915_vbuf_render_new_buf will reset index, sw_offset, hw_offset231* when it allocates a new buffer this is correct.232*/233{234offset = i915_render->vbo_sw_offset - i915_render->vbo_hw_offset;235offset = util_align_npot(offset, vertex_size);236i915_render->vbo_sw_offset = i915_render->vbo_hw_offset + offset;237i915_render->vbo_index = offset / vertex_size;238}239240if (!i915_vbuf_render_reserve(i915_render, size))241i915_vbuf_render_new_buf(i915_render, size);242243/*244* If a new buffer has been alocated sw_offset,245* hw_offset & index will be reset by new_buf246*/247248i915_render->vertex_size = vertex_size;249250i915_vbuf_update_vbo_state(render);251252if (!i915_render->vbo)253return FALSE;254return TRUE;255}256257static void *258i915_vbuf_render_map_vertices(struct vbuf_render *render)259{260struct i915_vbuf_render *i915_render = i915_vbuf_render(render);261struct i915_context *i915 = i915_render->i915;262263if (i915->vbo_flushed)264debug_printf("%s bad vbo flush occurred stalling on hw\n", __FUNCTION__);265266#ifdef VBUF_MAP_BUFFER267return (unsigned char *)i915_render->vbo_ptr + i915_render->vbo_sw_offset;268#else269return (unsigned char *)i915_render->vbo_ptr;270#endif271}272273static void274i915_vbuf_render_unmap_vertices(struct vbuf_render *render, ushort min_index,275ushort max_index)276{277struct i915_vbuf_render *i915_render = i915_vbuf_render(render);278struct i915_context *i915 = i915_render->i915;279struct i915_winsys *iws = i915->iws;280281i915_render->vbo_max_index = max_index;282i915_render->vbo_max_used = MAX2(i915_render->vbo_max_used,283i915_render->vertex_size * (max_index + 1));284#ifdef VBUF_MAP_BUFFER285(void)iws;286#else287i915_render->map_used_start = i915_render->vertex_size * min_index;288i915_render->map_used_end = i915_render->vertex_size * (max_index + 1);289iws->buffer_write(290iws, i915_render->vbo,291i915_render->map_used_start + i915_render->vbo_sw_offset,292i915_render->map_used_end - i915_render->map_used_start,293(unsigned char *)i915_render->vbo_ptr + i915_render->map_used_start);294295#endif296}297298/**299* Ensure that the given max_index given is not larger ushort max.300* If it is larger then ushort max it advanced the hw_offset to the301* same position in the vbo as sw_offset and set index to zero.302*303* Side effects:304* On failure update hw_offset and index.305*/306static void307i915_vbuf_ensure_index_bounds(struct vbuf_render *render, unsigned max_index)308{309struct i915_vbuf_render *i915_render = i915_vbuf_render(render);310311if (max_index + i915_render->vbo_index < ((1 << 17) - 1))312return;313314i915_render->vbo_hw_offset = i915_render->vbo_sw_offset;315i915_render->vbo_index = 0;316317i915_vbuf_update_vbo_state(render);318}319320static void321i915_vbuf_render_set_primitive(struct vbuf_render *render,322enum pipe_prim_type prim)323{324struct i915_vbuf_render *i915_render = i915_vbuf_render(render);325i915_render->prim = prim;326327switch (prim) {328case PIPE_PRIM_POINTS:329i915_render->hwprim = PRIM3D_POINTLIST;330i915_render->fallback = 0;331break;332case PIPE_PRIM_LINES:333i915_render->hwprim = PRIM3D_LINELIST;334i915_render->fallback = 0;335break;336case PIPE_PRIM_LINE_LOOP:337i915_render->hwprim = PRIM3D_LINELIST;338i915_render->fallback = PIPE_PRIM_LINE_LOOP;339break;340case PIPE_PRIM_LINE_STRIP:341i915_render->hwprim = PRIM3D_LINESTRIP;342i915_render->fallback = 0;343break;344case PIPE_PRIM_TRIANGLES:345i915_render->hwprim = PRIM3D_TRILIST;346i915_render->fallback = 0;347break;348case PIPE_PRIM_TRIANGLE_STRIP:349i915_render->hwprim = PRIM3D_TRISTRIP;350i915_render->fallback = 0;351break;352case PIPE_PRIM_TRIANGLE_FAN:353i915_render->hwprim = PRIM3D_TRIFAN;354i915_render->fallback = 0;355break;356case PIPE_PRIM_QUADS:357i915_render->hwprim = PRIM3D_TRILIST;358i915_render->fallback = PIPE_PRIM_QUADS;359break;360case PIPE_PRIM_QUAD_STRIP:361i915_render->hwprim = PRIM3D_TRILIST;362i915_render->fallback = PIPE_PRIM_QUAD_STRIP;363break;364case PIPE_PRIM_POLYGON:365i915_render->hwprim = PRIM3D_POLY;366i915_render->fallback = 0;367break;368default:369/* FIXME: Actually, can handle a lot more just fine... */370assert(0 && "unexpected prim in i915_vbuf_render_set_primitive()");371}372}373374/**375* Used for fallbacks in draw_arrays376*/377static void378draw_arrays_generate_indices(struct vbuf_render *render, unsigned start,379uint32_t nr, unsigned type)380{381struct i915_vbuf_render *i915_render = i915_vbuf_render(render);382struct i915_context *i915 = i915_render->i915;383unsigned i;384unsigned end = start + nr + i915_render->vbo_index;385start += i915_render->vbo_index;386387switch (type) {388case 0:389for (i = start; i + 1 < end; i += 2)390OUT_BATCH((i + 0) | (i + 1) << 16);391if (i < end)392OUT_BATCH(i);393break;394case PIPE_PRIM_LINE_LOOP:395if (nr >= 2) {396for (i = start + 1; i < end; i++)397OUT_BATCH((i - 1) | (i + 0) << 16);398OUT_BATCH((i - 1) | (start) << 16);399}400break;401case PIPE_PRIM_QUADS:402for (i = start; i + 3 < end; i += 4) {403OUT_BATCH((i + 0) | (i + 1) << 16);404OUT_BATCH((i + 3) | (i + 1) << 16);405OUT_BATCH((i + 2) | (i + 3) << 16);406}407break;408case PIPE_PRIM_QUAD_STRIP:409for (i = start; i + 3 < end; i += 2) {410OUT_BATCH((i + 0) | (i + 1) << 16);411OUT_BATCH((i + 3) | (i + 2) << 16);412OUT_BATCH((i + 0) | (i + 3) << 16);413}414break;415default:416assert(0);417}418}419420static unsigned421draw_arrays_calc_nr_indices(uint32_t nr, unsigned type)422{423switch (type) {424case 0:425return nr;426case PIPE_PRIM_LINE_LOOP:427if (nr >= 2)428return nr * 2;429else430return 0;431case PIPE_PRIM_QUADS:432return (nr / 4) * 6;433case PIPE_PRIM_QUAD_STRIP:434return ((nr - 2) / 2) * 6;435default:436assert(0);437return 0;438}439}440441static void442draw_arrays_fallback(struct vbuf_render *render, unsigned start, uint32_t nr)443{444struct i915_vbuf_render *i915_render = i915_vbuf_render(render);445struct i915_context *i915 = i915_render->i915;446unsigned nr_indices;447448nr_indices = draw_arrays_calc_nr_indices(nr, i915_render->fallback);449if (!nr_indices)450return;451452i915_vbuf_ensure_index_bounds(render, start + nr_indices);453454if (i915->dirty)455i915_update_derived(i915);456457if (i915->hardware_dirty)458i915_emit_hardware_state(i915);459460if (!BEGIN_BATCH(1 + (nr_indices + 1) / 2)) {461FLUSH_BATCH(NULL, I915_FLUSH_ASYNC);462463/* Make sure state is re-emitted after a flush:464*/465i915_emit_hardware_state(i915);466i915->vbo_flushed = 1;467468if (!BEGIN_BATCH(1 + (nr_indices + 1) / 2)) {469assert(0);470goto out;471}472}473474OUT_BATCH(_3DPRIMITIVE | PRIM_INDIRECT | i915_render->hwprim |475PRIM_INDIRECT_ELTS | nr_indices);476477draw_arrays_generate_indices(render, start, nr, i915_render->fallback);478479out:480return;481}482483static void484i915_vbuf_render_draw_arrays(struct vbuf_render *render, unsigned start,485uint32_t nr)486{487struct i915_vbuf_render *i915_render = i915_vbuf_render(render);488struct i915_context *i915 = i915_render->i915;489490if (i915_render->fallback) {491draw_arrays_fallback(render, start, nr);492return;493}494495i915_vbuf_ensure_index_bounds(render, start + nr);496start += i915_render->vbo_index;497498if (i915->dirty)499i915_update_derived(i915);500501if (i915->hardware_dirty)502i915_emit_hardware_state(i915);503504if (!BEGIN_BATCH(2)) {505FLUSH_BATCH(NULL, I915_FLUSH_ASYNC);506507/* Make sure state is re-emitted after a flush:508*/509i915_emit_hardware_state(i915);510i915->vbo_flushed = 1;511512if (!BEGIN_BATCH(2)) {513assert(0);514goto out;515}516}517518OUT_BATCH(_3DPRIMITIVE | PRIM_INDIRECT | PRIM_INDIRECT_SEQUENTIAL |519i915_render->hwprim | nr);520OUT_BATCH(start); /* Beginning vertex index */521522out:523return;524}525526/**527* Used for normal and fallback emitting of indices528* If type is zero normal operation assumed.529*/530static void531draw_generate_indices(struct vbuf_render *render, const ushort *indices,532uint32_t nr_indices, unsigned type)533{534struct i915_vbuf_render *i915_render = i915_vbuf_render(render);535struct i915_context *i915 = i915_render->i915;536unsigned i;537unsigned o = i915_render->vbo_index;538539switch (type) {540case 0:541for (i = 0; i + 1 < nr_indices; i += 2) {542OUT_BATCH((o + indices[i]) | (o + indices[i + 1]) << 16);543}544if (i < nr_indices) {545OUT_BATCH((o + indices[i]));546}547break;548case PIPE_PRIM_LINE_LOOP:549if (nr_indices >= 2) {550for (i = 1; i < nr_indices; i++)551OUT_BATCH((o + indices[i - 1]) | (o + indices[i]) << 16);552OUT_BATCH((o + indices[i - 1]) | (o + indices[0]) << 16);553}554break;555case PIPE_PRIM_QUADS:556for (i = 0; i + 3 < nr_indices; i += 4) {557OUT_BATCH((o + indices[i + 0]) | (o + indices[i + 1]) << 16);558OUT_BATCH((o + indices[i + 3]) | (o + indices[i + 1]) << 16);559OUT_BATCH((o + indices[i + 2]) | (o + indices[i + 3]) << 16);560}561break;562case PIPE_PRIM_QUAD_STRIP:563for (i = 0; i + 3 < nr_indices; i += 2) {564OUT_BATCH((o + indices[i + 0]) | (o + indices[i + 1]) << 16);565OUT_BATCH((o + indices[i + 3]) | (o + indices[i + 2]) << 16);566OUT_BATCH((o + indices[i + 0]) | (o + indices[i + 3]) << 16);567}568break;569default:570assert(0);571break;572}573}574575static unsigned576draw_calc_nr_indices(uint32_t nr_indices, unsigned type)577{578switch (type) {579case 0:580return nr_indices;581case PIPE_PRIM_LINE_LOOP:582if (nr_indices >= 2)583return nr_indices * 2;584else585return 0;586case PIPE_PRIM_QUADS:587return (nr_indices / 4) * 6;588case PIPE_PRIM_QUAD_STRIP:589return ((nr_indices - 2) / 2) * 6;590default:591assert(0);592return 0;593}594}595596static void597i915_vbuf_render_draw_elements(struct vbuf_render *render,598const ushort *indices, uint32_t nr_indices)599{600struct i915_vbuf_render *i915_render = i915_vbuf_render(render);601struct i915_context *i915 = i915_render->i915;602unsigned save_nr_indices;603604save_nr_indices = nr_indices;605606nr_indices = draw_calc_nr_indices(nr_indices, i915_render->fallback);607if (!nr_indices)608return;609610i915_vbuf_ensure_index_bounds(render, i915_render->vbo_max_index);611612if (i915->dirty)613i915_update_derived(i915);614615if (i915->hardware_dirty)616i915_emit_hardware_state(i915);617618if (!BEGIN_BATCH(1 + (nr_indices + 1) / 2)) {619FLUSH_BATCH(NULL, I915_FLUSH_ASYNC);620621/* Make sure state is re-emitted after a flush:622*/623i915_emit_hardware_state(i915);624i915->vbo_flushed = 1;625626if (!BEGIN_BATCH(1 + (nr_indices + 1) / 2)) {627assert(0);628goto out;629}630}631632OUT_BATCH(_3DPRIMITIVE | PRIM_INDIRECT | i915_render->hwprim |633PRIM_INDIRECT_ELTS | nr_indices);634draw_generate_indices(render, indices, save_nr_indices,635i915_render->fallback);636637out:638return;639}640641static void642i915_vbuf_render_release_vertices(struct vbuf_render *render)643{644struct i915_vbuf_render *i915_render = i915_vbuf_render(render);645646i915_render->vbo_sw_offset += i915_render->vbo_max_used;647i915_render->vbo_max_used = 0;648649/*650* Micro optimization, by calling update here we the offset change651* will be picked up on the next pipe_context::draw_*.652*/653i915_vbuf_update_vbo_state(render);654}655656static void657i915_vbuf_render_destroy(struct vbuf_render *render)658{659struct i915_vbuf_render *i915_render = i915_vbuf_render(render);660struct i915_context *i915 = i915_render->i915;661struct i915_winsys *iws = i915->iws;662663if (i915_render->vbo) {664i915->vbo = NULL;665iws->buffer_unmap(iws, i915_render->vbo);666iws->buffer_destroy(iws, i915_render->vbo);667}668669FREE(i915_render);670}671672/**673* Create a new primitive render.674*/675static struct vbuf_render *676i915_vbuf_render_create(struct i915_context *i915)677{678struct i915_vbuf_render *i915_render = CALLOC_STRUCT(i915_vbuf_render);679struct i915_winsys *iws = i915->iws;680int i;681682i915_render->i915 = i915;683684i915_render->base.max_vertex_buffer_bytes = 4 * 4096;685686/* NOTE: it must be such that state and vertices indices fit in a single687* batch buffer. 4096 is one batch buffer and 430 is the max amount of688* state in dwords. The result is the number of 16-bit indices which can689* fit in a single batch buffer.690*/691i915_render->base.max_indices = (4096 - 430 * 4) / 2;692693i915_render->base.get_vertex_info = i915_vbuf_render_get_vertex_info;694i915_render->base.allocate_vertices = i915_vbuf_render_allocate_vertices;695i915_render->base.map_vertices = i915_vbuf_render_map_vertices;696i915_render->base.unmap_vertices = i915_vbuf_render_unmap_vertices;697i915_render->base.set_primitive = i915_vbuf_render_set_primitive;698i915_render->base.draw_elements = i915_vbuf_render_draw_elements;699i915_render->base.draw_arrays = i915_vbuf_render_draw_arrays;700i915_render->base.release_vertices = i915_vbuf_render_release_vertices;701i915_render->base.destroy = i915_vbuf_render_destroy;702703#ifndef VBUF_MAP_BUFFER704i915_render->map_size = 0;705i915_render->map_used_start = 0;706i915_render->map_used_end = 0;707#endif708709i915_render->vbo = NULL;710i915_render->vbo_ptr = NULL;711i915_render->vbo_size = 0;712i915_render->vbo_hw_offset = 0;713i915_render->vbo_sw_offset = 0;714i915_render->vbo_alloc_size = i915_render->base.max_vertex_buffer_bytes * 4;715716#ifdef VBUF_USE_POOL717i915_render->pool_used = false;718i915_render->pool_buffer_size = i915_render->vbo_alloc_size;719i915_render->pool_fifo = u_fifo_create(6);720for (i = 0; i < 6; i++)721u_fifo_add(i915_render->pool_fifo,722iws->buffer_create(iws, i915_render->pool_buffer_size,723I915_NEW_VERTEX));724#else725(void)i;726(void)iws;727#endif728729return &i915_render->base;730}731732/**733* Create a new primitive vbuf/render stage.734*/735struct draw_stage *736i915_draw_vbuf_stage(struct i915_context *i915)737{738struct vbuf_render *render;739struct draw_stage *stage;740741render = i915_vbuf_render_create(i915);742if (!render)743return NULL;744745stage = draw_vbuf_stage(i915->draw, render);746if (!stage) {747render->destroy(render);748return NULL;749}750/** TODO JB: this shouldn't be here */751draw_set_render(i915->draw, render);752753return stage;754}755756757