1/**********************************************************
2 * Copyright 2008-2009 VMware, Inc.  All rights reserved.
3 *
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation
6 * files (the "Software"), to deal in the Software without
7 * restriction, including without limitation the rights to use, copy,
8 * modify, merge, publish, distribute, sublicense, and/or sell copies
9 * of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 *
24 **********************************************************/
25
26
27#include "util/u_helpers.h"
28#include "util/u_inlines.h"
29#include "util/u_prim.h"
30#include "util/u_prim_restart.h"
31
32#include "svga_context.h"
33#include "svga_draw.h"
34#include "svga_shader.h"
35#include "svga_surface.h"
36#include "svga_swtnl.h"
37#include "svga_debug.h"
38#include "svga_resource_buffer.h"
39
40/* Returns TRUE if we are currently using flat shading.
41 */
42static boolean
43is_using_flat_shading(const struct svga_context *svga)
44{
45   return
46      svga->state.hw_draw.fs ? svga->state.hw_draw.fs->uses_flat_interp : FALSE;
47}
48
49
50static enum pipe_error
51retry_draw_range_elements(struct svga_context *svga,
52                          struct pipe_resource *index_buffer,
53                          unsigned index_size,
54                          int index_bias,
55                          unsigned min_index,
56                          unsigned max_index,
57                          enum pipe_prim_type prim,
58                          unsigned start,
59                          unsigned count,
60                          unsigned start_instance,
61                          unsigned instance_count)
62{
63   enum pipe_error ret;
64
65   SVGA_STATS_TIME_PUSH(svga_sws(svga), SVGA_STATS_TIME_DRAWELEMENTS);
66
67   for (unsigned try = 0; try < 2; try++) {
68      ret = svga_hwtnl_draw_range_elements(svga->hwtnl,
69                                           index_buffer, index_size,
70                                           index_bias,
71                                           min_index, max_index,
72                                           prim, start, count,
73                                           start_instance, instance_count);
74      if (ret == PIPE_OK)
75         break;
76      svga_context_flush(svga, NULL);
77   }
78
79   SVGA_STATS_TIME_POP(svga_sws(svga));
80   return ret;
81}
82
83
84static enum pipe_error
85retry_draw_arrays(struct svga_context *svga,
86                  enum pipe_prim_type prim, unsigned start, unsigned count,
87                  unsigned start_instance, unsigned instance_count)
88{
89   enum pipe_error ret;
90
91   SVGA_STATS_TIME_PUSH(svga_sws(svga), SVGA_STATS_TIME_DRAWARRAYS);
92
93   for (unsigned try = 0; try < 2; try++) {
94      ret = svga_hwtnl_draw_arrays(svga->hwtnl, prim, start, count,
95                                   start_instance, instance_count);
96      if (ret == PIPE_OK)
97         break;
98      svga_context_flush(svga, NULL);
99   }
100
101   SVGA_STATS_TIME_POP(svga_sws(svga));
102   return ret;
103}
104
105
106/**
107 * Determine if we need to implement primitive restart with a fallback
108 * path which breaks the original primitive into sub-primitive at the
109 * restart indexes.
110 */
111static boolean
112need_fallback_prim_restart(const struct svga_context *svga,
113                           const struct pipe_draw_info *info)
114{
115   if (info->primitive_restart && info->index_size) {
116      if (!svga_have_vgpu10(svga))
117         return TRUE;
118      else if (!svga->state.sw.need_swtnl) {
119         if (info->index_size == 1)
120            return TRUE; /* no device support for 1-byte indexes */
121         else if (info->index_size == 2)
122            return info->restart_index != 0xffff;
123         else
124            return info->restart_index != 0xffffffff;
125      }
126   }
127
128   return FALSE;
129}
130
131
132static void
133svga_draw_vbo(struct pipe_context *pipe, const struct pipe_draw_info *info)
134{
135   struct svga_context *svga = svga_context(pipe);
136   enum pipe_prim_type reduced_prim = u_reduced_prim(info->mode);
137   unsigned count = info->count;
138   enum pipe_error ret = 0;
139   boolean needed_swtnl;
140   struct pipe_resource *indexbuf =
141      info->has_user_indices ? NULL : info->index.resource;
142
143   SVGA_STATS_TIME_PUSH(svga_sws(svga), SVGA_STATS_TIME_DRAWVBO);
144
145   svga->hud.num_draw_calls++;  /* for SVGA_QUERY_NUM_DRAW_CALLS */
146
147   if (u_reduced_prim(info->mode) == PIPE_PRIM_TRIANGLES &&
148       svga->curr.rast->templ.cull_face == PIPE_FACE_FRONT_AND_BACK)
149      goto done;
150
151   /* Upload a user index buffer. */
152   unsigned index_offset = 0;
153   if (info->index_size && info->has_user_indices &&
154       !util_upload_index_buffer(pipe, info, &indexbuf, &index_offset)) {
155      goto done;
156   }
157
158   /*
159    * Mark currently bound target surfaces as dirty
160    * doesn't really matter if it is done before drawing.
161    *
162    * TODO If we ever normaly return something other then
163    * true we should not mark it as dirty then.
164    */
165   svga_mark_surfaces_dirty(svga_context(pipe));
166
167   if (svga->curr.reduced_prim != reduced_prim) {
168      svga->curr.reduced_prim = reduced_prim;
169      svga->dirty |= SVGA_NEW_REDUCED_PRIMITIVE;
170   }
171
172   if (need_fallback_prim_restart(svga, info)) {
173      enum pipe_error r;
174      r = util_draw_vbo_without_prim_restart(pipe, info);
175      assert(r == PIPE_OK);
176      (void) r;
177      goto done;
178   }
179
180   if (!u_trim_pipe_prim(info->mode, &count))
181      goto done;
182
183   needed_swtnl = svga->state.sw.need_swtnl;
184
185   svga_update_state_retry(svga, SVGA_STATE_NEED_SWTNL);
186
187   if (svga->state.sw.need_swtnl) {
188      svga->hud.num_fallbacks++;  /* for SVGA_QUERY_NUM_FALLBACKS */
189      if (!needed_swtnl) {
190         /*
191          * We're switching from HW to SW TNL.  SW TNL will require mapping all
192          * currently bound vertex buffers, some of which may already be
193          * referenced in the current command buffer as result of previous HW
194          * TNL. So flush now, to prevent the context to flush while a referred
195          * vertex buffer is mapped.
196          */
197
198         svga_context_flush(svga, NULL);
199      }
200
201      /* Avoid leaking the previous hwtnl bias to swtnl */
202      svga_hwtnl_set_index_bias(svga->hwtnl, 0);
203      ret = svga_swtnl_draw_vbo(svga, info, indexbuf, index_offset);
204   }
205   else {
206      if (!svga_update_state_retry(svga, SVGA_STATE_HW_DRAW)) {
207         static const char *msg = "State update failed, skipping draw call";
208         debug_printf("%s\n", msg);
209         pipe_debug_message(&svga->debug.callback, INFO, "%s", msg);
210         goto done;
211      }
212
213      svga_hwtnl_set_fillmode(svga->hwtnl, svga->curr.rast->hw_fillmode);
214
215      /** determine if flatshade is to be used after svga_update_state()
216       *  in case the fragment shader is changed.
217       */
218      svga_hwtnl_set_flatshade(svga->hwtnl,
219                               svga->curr.rast->templ.flatshade ||
220                               is_using_flat_shading(svga),
221                               svga->curr.rast->templ.flatshade_first);
222
223      if (info->index_size && indexbuf) {
224         unsigned offset;
225
226         assert(index_offset % info->index_size == 0);
227         offset = index_offset / info->index_size;
228
229         ret = retry_draw_range_elements(svga,
230                                         indexbuf,
231                                         info->index_size,
232                                         info->index_bias,
233                                         info->min_index,
234                                         info->max_index,
235                                         info->mode,
236                                         info->start + offset,
237                                         count,
238                                         info->start_instance,
239                                         info->instance_count);
240      }
241      else {
242         ret = retry_draw_arrays(svga, info->mode, info->start, count,
243                                 info->start_instance, info->instance_count);
244      }
245   }
246
247   /* XXX: Silence warnings, do something sensible here? */
248   (void)ret;
249
250   if (SVGA_DEBUG & DEBUG_FLUSH) {
251      svga_hwtnl_flush_retry(svga);
252      svga_context_flush(svga, NULL);
253   }
254
255done:
256   if (info->index_size && info->index.resource != indexbuf)
257      pipe_resource_reference(&indexbuf, NULL);
258   SVGA_STATS_TIME_POP(svga_sws(svga));
259}
260
261
262void
263svga_init_draw_functions(struct svga_context *svga)
264{
265   svga->pipe.draw_vbo = svga_draw_vbo;
266}
267