/* * Copyright (C) 2009-2010 Francisco Jerez. * 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. * */ #include "tnl/t_context.h" #include "tnl/t_pipeline.h" #include "tnl/t_vertex.h" #define SWTNL_VBO_SIZE 65536 static enum tnl_attr_format swtnl_get_format(int type, int fields) { switch (type) { case GL_FLOAT: switch (fields){ case 1: return EMIT_1F; case 2: return EMIT_2F; case 3: return EMIT_3F; case 4: return EMIT_4F; default: assert(0); } case GL_UNSIGNED_BYTE: switch (fields) { case 4: return EMIT_4UB_4F_RGBA; default: assert(0); } default: assert(0); } } static struct swtnl_attr_info { int type; int fields; } swtnl_attrs[VERT_ATTRIB_MAX] = { [VERT_ATTRIB_POS] = { .type = GL_FLOAT, .fields = 4, }, [VERT_ATTRIB_NORMAL] = { .type = GL_FLOAT, .fields = -1, }, [VERT_ATTRIB_COLOR0] = { .type = GL_UNSIGNED_BYTE, .fields = 4, }, [VERT_ATTRIB_COLOR1] = { .type = GL_UNSIGNED_BYTE, .fields = 4, }, [VERT_ATTRIB_FOG] = { .type = GL_FLOAT, .fields = 1, }, [VERT_ATTRIB_TEX0] = { .type = GL_FLOAT, .fields = -1, }, [VERT_ATTRIB_TEX1] = { .type = GL_FLOAT, .fields = -1, }, [VERT_ATTRIB_TEX2] = { .type = GL_FLOAT, .fields = -1, }, [VERT_ATTRIB_TEX3] = { .type = GL_FLOAT, .fields = -1, }, }; static void swtnl_choose_attrs(struct gl_context *ctx) { struct nouveau_render_state *render = to_render_state(ctx); TNLcontext *tnl = TNL_CONTEXT(ctx); struct tnl_clipspace *vtx = &tnl->clipspace; static struct tnl_attr_map map[NUM_VERTEX_ATTRS]; int fields, attr, i, n = 0; render->mode = VBO; render->attr_count = NUM_VERTEX_ATTRS; /* We always want non Ndc coords format */ tnl->vb.AttribPtr[VERT_ATTRIB_POS] = tnl->vb.ClipPtr; for (i = 0; i < VERT_ATTRIB_MAX; i++) { struct nouveau_attr_info *ha = &TAG(vertex_attrs)[i]; struct swtnl_attr_info *sa = &swtnl_attrs[i]; struct nouveau_array *a = &render->attrs[i]; if (!sa->fields) continue; /* Unsupported attribute. */ if (tnl->render_inputs_bitset & BITFIELD64_BIT(i)) { if (sa->fields > 0) fields = sa->fields; else fields = tnl->vb.AttribPtr[i]->size; map[n++] = (struct tnl_attr_map) { .attrib = i, .format = swtnl_get_format(sa->type, fields), }; render->map[ha->vbo_index] = i; a->attr = i; a->fields = fields; a->type = sa->type; } } _tnl_install_attrs(ctx, map, n, NULL, 0); FOR_EACH_BOUND_ATTR(render, i, attr) render->attrs[attr].stride = vtx->vertex_size; TAG(render_set_format)(ctx); } static void swtnl_alloc_vertices(struct gl_context *ctx) { struct nouveau_swtnl_state *swtnl = &to_render_state(ctx)->swtnl; nouveau_bo_ref(NULL, &swtnl->vbo); swtnl->buf = nouveau_get_scratch(ctx, SWTNL_VBO_SIZE, &swtnl->vbo, &swtnl->offset); swtnl->vertex_count = 0; } static void swtnl_bind_vertices(struct gl_context *ctx) { struct nouveau_render_state *render = to_render_state(ctx); struct nouveau_swtnl_state *swtnl = &render->swtnl; struct tnl_clipspace *vtx = &TNL_CONTEXT(ctx)->clipspace; int i; for (i = 0; i < vtx->attr_count; i++) { struct tnl_clipspace_attr *ta = &vtx->attr[i]; struct nouveau_array *a = &render->attrs[ta->attrib]; nouveau_bo_ref(swtnl->vbo, &a->bo); a->offset = swtnl->offset + ta->vertoffset; } TAG(render_bind_vertices)(ctx); } static void swtnl_unbind_vertices(struct gl_context *ctx) { struct nouveau_render_state *render = to_render_state(ctx); int i, attr; TAG(render_release_vertices)(ctx); FOR_EACH_BOUND_ATTR(render, i, attr) { nouveau_bo_ref(NULL, &render->attrs[attr].bo); render->map[i] = -1; } render->attr_count = 0; } static void swtnl_flush_vertices(struct gl_context *ctx) { struct nouveau_pushbuf *push = context_push(ctx); struct nouveau_swtnl_state *swtnl = &to_render_state(ctx)->swtnl; unsigned npush, start = 0, count = swtnl->vertex_count; RENDER_LOCALS(ctx); swtnl_bind_vertices(ctx); while (count) { npush = get_max_vertices(ctx, NULL, PUSH_AVAIL(push)); npush = MIN2(npush / 12 * 12, count); count -= npush; if (!npush) { PUSH_KICK(push); continue; } BATCH_BEGIN(nvgl_primitive(swtnl->primitive)); EMIT_VBO(L, ctx, start, 0, npush); BATCH_END(); PUSH_KICK(push); } swtnl_alloc_vertices(ctx); } /* TnL renderer entry points */ static void swtnl_start(struct gl_context *ctx) { swtnl_choose_attrs(ctx); } static void swtnl_finish(struct gl_context *ctx) { swtnl_flush_vertices(ctx); swtnl_unbind_vertices(ctx); } static void swtnl_primitive(struct gl_context *ctx, GLenum mode) { } static void swtnl_reset_stipple(struct gl_context *ctx) { } /* Primitive rendering */ #define BEGIN_PRIMITIVE(p, n) \ struct nouveau_swtnl_state *swtnl = &to_render_state(ctx)->swtnl; \ int vertex_len = TNL_CONTEXT(ctx)->clipspace.vertex_size; \ \ if (swtnl->vertex_count + (n) > SWTNL_VBO_SIZE/vertex_len \ || (swtnl->vertex_count && swtnl->primitive != p)) \ swtnl_flush_vertices(ctx); \ \ swtnl->primitive = p; #define OUT_VERTEX(i) do { \ memcpy(swtnl->buf + swtnl->vertex_count * vertex_len, \ _tnl_get_vertex(ctx, (i)), vertex_len); \ swtnl->vertex_count++; \ } while (0) static void swtnl_points(struct gl_context *ctx, GLuint first, GLuint last) { int i, count; while (first < last) { BEGIN_PRIMITIVE(GL_POINTS, last - first); count = MIN2(SWTNL_VBO_SIZE / vertex_len, last - first); for (i = 0; i < count; i++) OUT_VERTEX(first + i); first += count; } } static void swtnl_line(struct gl_context *ctx, GLuint v1, GLuint v2) { BEGIN_PRIMITIVE(GL_LINES, 2); OUT_VERTEX(v1); OUT_VERTEX(v2); } static void swtnl_triangle(struct gl_context *ctx, GLuint v1, GLuint v2, GLuint v3) { BEGIN_PRIMITIVE(GL_TRIANGLES, 3); OUT_VERTEX(v1); OUT_VERTEX(v2); OUT_VERTEX(v3); } static void swtnl_quad(struct gl_context *ctx, GLuint v1, GLuint v2, GLuint v3, GLuint v4) { BEGIN_PRIMITIVE(GL_QUADS, 4); OUT_VERTEX(v1); OUT_VERTEX(v2); OUT_VERTEX(v3); OUT_VERTEX(v4); } /* TnL initialization. */ void TAG(swtnl_init)(struct gl_context *ctx) { TNLcontext *tnl = TNL_CONTEXT(ctx); tnl->Driver.RunPipeline = _tnl_run_pipeline; tnl->Driver.Render.Interp = _tnl_interp; tnl->Driver.Render.CopyPV = _tnl_copy_pv; tnl->Driver.Render.ClippedPolygon = _tnl_RenderClippedPolygon; tnl->Driver.Render.ClippedLine = _tnl_RenderClippedLine; tnl->Driver.Render.BuildVertices = _tnl_build_vertices; tnl->Driver.Render.Start = swtnl_start; tnl->Driver.Render.Finish = swtnl_finish; tnl->Driver.Render.PrimitiveNotify = swtnl_primitive; tnl->Driver.Render.ResetLineStipple = swtnl_reset_stipple; tnl->Driver.Render.Points = swtnl_points; tnl->Driver.Render.Line = swtnl_line; tnl->Driver.Render.Triangle = swtnl_triangle; tnl->Driver.Render.Quad = swtnl_quad; _tnl_init_vertices(ctx, tnl->vb.Size, NUM_VERTEX_ATTRS * 4 * sizeof(GLfloat)); _tnl_need_projected_coords(ctx, GL_FALSE); _tnl_allow_vertex_fog(ctx, GL_FALSE); swtnl_alloc_vertices(ctx); } void TAG(swtnl_destroy)(struct gl_context *ctx) { nouveau_bo_ref(NULL, &to_render_state(ctx)->swtnl.vbo); }