| /********************************************************** |
| * Copyright 2008-2009 VMware, Inc. 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 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. |
| * |
| **********************************************************/ |
| |
| |
| #include "util/u_draw.h" |
| #include "util/format/u_format.h" |
| #include "util/u_helpers.h" |
| #include "util/u_inlines.h" |
| #include "util/u_prim.h" |
| #include "util/u_prim_restart.h" |
| |
| #include "svga_context.h" |
| #include "svga_draw_private.h" |
| #include "svga_screen.h" |
| #include "svga_draw.h" |
| #include "svga_shader.h" |
| #include "svga_surface.h" |
| #include "svga_swtnl.h" |
| #include "svga_debug.h" |
| #include "svga_resource_buffer.h" |
| |
| |
| static enum pipe_error |
| retry_draw_range_elements(struct svga_context *svga, |
| const struct pipe_draw_info *info, |
| unsigned count) |
| { |
| SVGA_STATS_TIME_PUSH(svga_sws(svga), SVGA_STATS_TIME_DRAWELEMENTS); |
| |
| SVGA_RETRY(svga, svga_hwtnl_draw_range_elements(svga->hwtnl, info, count)); |
| |
| SVGA_STATS_TIME_POP(svga_sws(svga)); |
| return PIPE_OK; |
| } |
| |
| |
| static enum pipe_error |
| retry_draw_arrays( struct svga_context *svga, |
| enum pipe_prim_type prim, unsigned start, unsigned count, |
| unsigned start_instance, unsigned instance_count, |
| ubyte vertices_per_patch) |
| { |
| enum pipe_error ret; |
| |
| SVGA_STATS_TIME_PUSH(svga_sws(svga), SVGA_STATS_TIME_DRAWARRAYS); |
| |
| SVGA_RETRY_OOM(svga, ret, svga_hwtnl_draw_arrays(svga->hwtnl, prim, start, |
| count, start_instance, |
| instance_count, |
| vertices_per_patch)); |
| SVGA_STATS_TIME_POP(svga_sws(svga)); |
| return ret; |
| } |
| |
| |
| /** |
| * Auto draw (get vertex count from a transform feedback result). |
| */ |
| static enum pipe_error |
| retry_draw_auto(struct svga_context *svga, |
| const struct pipe_draw_info *info) |
| { |
| assert(svga_have_sm5(svga)); |
| assert(info->count_from_stream_output); |
| assert(info->instance_count == 1); |
| /* SO drawing implies core profile and none of these prim types */ |
| assert(info->mode != PIPE_PRIM_QUADS && |
| info->mode != PIPE_PRIM_QUAD_STRIP && |
| info->mode != PIPE_PRIM_POLYGON); |
| |
| if (info->mode == PIPE_PRIM_LINE_LOOP) { |
| /* XXX need to do a fallback */ |
| assert(!"draw auto fallback not supported yet"); |
| return PIPE_OK; |
| } |
| else { |
| SVGA3dPrimitiveRange range; |
| unsigned hw_count; |
| |
| range.primType = svga_translate_prim(info->mode, 12, &hw_count, |
| info->vertices_per_patch); |
| range.primitiveCount = 0; |
| range.indexArray.surfaceId = SVGA3D_INVALID_ID; |
| range.indexArray.offset = 0; |
| range.indexArray.stride = 0; |
| range.indexWidth = 0; |
| range.indexBias = 0; |
| |
| SVGA_RETRY(svga, svga_hwtnl_prim |
| (svga->hwtnl, &range, |
| 0, /* vertex count comes from SO buffer */ |
| 0, /* don't know min index */ |
| ~0u, /* don't know max index */ |
| NULL, /* no index buffer */ |
| 0, /* start instance */ |
| 1, /* only 1 instance supported */ |
| NULL, /* indirect drawing info */ |
| info->count_from_stream_output)); |
| |
| return PIPE_OK; |
| } |
| } |
| |
| |
| /** |
| * Indirect draw (get vertex count, start index, etc. from a buffer object. |
| */ |
| static enum pipe_error |
| retry_draw_indirect(struct svga_context *svga, |
| const struct pipe_draw_info *info) |
| { |
| assert(svga_have_sm5(svga)); |
| assert(info->indirect); |
| /* indirect drawing implies core profile and none of these prim types */ |
| assert(info->mode != PIPE_PRIM_QUADS && |
| info->mode != PIPE_PRIM_QUAD_STRIP && |
| info->mode != PIPE_PRIM_POLYGON); |
| |
| if (info->mode == PIPE_PRIM_LINE_LOOP) { |
| /* need to do a fallback */ |
| util_draw_indirect(&svga->pipe, info); |
| return PIPE_OK; |
| } |
| else { |
| SVGA3dPrimitiveRange range; |
| unsigned hw_count; |
| |
| range.primType = svga_translate_prim(info->mode, 12, &hw_count, |
| info->vertices_per_patch); |
| range.primitiveCount = 0; /* specified in indirect buffer */ |
| range.indexArray.surfaceId = SVGA3D_INVALID_ID; |
| range.indexArray.offset = 0; |
| range.indexArray.stride = 0; |
| range.indexWidth = info->index_size; |
| range.indexBias = 0; /* specified in indirect buffer */ |
| |
| SVGA_RETRY(svga, svga_hwtnl_prim |
| (svga->hwtnl, &range, |
| 0, /* vertex count is in indirect buffer */ |
| 0, /* don't know min index */ |
| ~0u, /* don't know max index */ |
| info->index.resource, |
| info->start_instance, |
| 0, /* don't know instance count */ |
| info->indirect, |
| NULL)); /* SO vertex count */ |
| |
| return PIPE_OK; |
| } |
| } |
| |
| |
| /** |
| * Determine if we need to implement primitive restart with a fallback |
| * path which breaks the original primitive into sub-primitive at the |
| * restart indexes. |
| */ |
| static boolean |
| need_fallback_prim_restart(const struct svga_context *svga, |
| const struct pipe_draw_info *info) |
| { |
| if (info->primitive_restart && info->index_size) { |
| if (!svga_have_vgpu10(svga)) |
| return TRUE; |
| else if (!svga->state.sw.need_swtnl) { |
| if (info->index_size == 1) |
| return TRUE; /* no device support for 1-byte indexes */ |
| else if (info->index_size == 2) |
| return info->restart_index != 0xffff; |
| else |
| return info->restart_index != 0xffffffff; |
| } |
| } |
| |
| return FALSE; |
| } |
| |
| |
| /** |
| * A helper function to return the vertex count from the primitive count |
| * returned from the stream output statistics query for the specified stream. |
| */ |
| static unsigned |
| get_vcount_from_stream_output(struct svga_context *svga, |
| const struct pipe_draw_info *info, |
| unsigned stream) |
| { |
| unsigned primcount; |
| primcount = svga_get_primcount_from_stream_output(svga, stream); |
| return u_vertices_for_prims(info->mode, primcount); |
| } |
| |
| |
| static void |
| svga_draw_vbo(struct pipe_context *pipe, const struct pipe_draw_info *info) |
| { |
| struct svga_context *svga = svga_context(pipe); |
| enum pipe_prim_type reduced_prim = u_reduced_prim(info->mode); |
| unsigned count = info->count; |
| enum pipe_error ret = 0; |
| boolean needed_swtnl; |
| |
| SVGA_STATS_TIME_PUSH(svga_sws(svga), SVGA_STATS_TIME_DRAWVBO); |
| |
| svga->hud.num_draw_calls++; /* for SVGA_QUERY_NUM_DRAW_CALLS */ |
| |
| if (u_reduced_prim(info->mode) == PIPE_PRIM_TRIANGLES && |
| svga->curr.rast->templ.cull_face == PIPE_FACE_FRONT_AND_BACK) |
| goto done; |
| |
| /* |
| * Mark currently bound target surfaces as dirty |
| * doesn't really matter if it is done before drawing. |
| * |
| * TODO If we ever normaly return something other then |
| * true we should not mark it as dirty then. |
| */ |
| svga_mark_surfaces_dirty(svga_context(pipe)); |
| |
| if (svga->curr.reduced_prim != reduced_prim) { |
| svga->curr.reduced_prim = reduced_prim; |
| svga->dirty |= SVGA_NEW_REDUCED_PRIMITIVE; |
| } |
| |
| /* We need to adjust the vertexID in the vertex shader since SV_VertexID |
| * always start from 0 for DrawArrays and does not include baseVertex for |
| * DrawIndexed. |
| */ |
| if (svga->curr.vertex_id_bias != (info->start + info->index_bias)) { |
| svga->curr.vertex_id_bias = info->start + info->index_bias; |
| svga->dirty |= SVGA_NEW_VS_CONSTS; |
| } |
| |
| if (svga->curr.vertices_per_patch != info->vertices_per_patch) { |
| svga->curr.vertices_per_patch = info->vertices_per_patch; |
| |
| /* If input patch size changes, we need to notifiy the TCS |
| * code to reevaluate the shader variant since the |
| * vertices per patch count is a constant in the control |
| * point count declaration. |
| */ |
| if (svga->curr.tcs || svga->curr.tes) |
| svga->dirty |= SVGA_NEW_TCS_PARAM; |
| } |
| |
| if (need_fallback_prim_restart(svga, info)) { |
| enum pipe_error r; |
| r = util_draw_vbo_without_prim_restart(pipe, info); |
| assert(r == PIPE_OK); |
| (void) r; |
| goto done; |
| } |
| |
| if (!info->indirect && !info->count_from_stream_output && |
| !u_trim_pipe_prim(info->mode, &count)) |
| goto done; |
| |
| needed_swtnl = svga->state.sw.need_swtnl; |
| |
| svga_update_state_retry(svga, SVGA_STATE_NEED_SWTNL); |
| |
| if (svga->state.sw.need_swtnl) { |
| svga->hud.num_fallbacks++; /* for SVGA_QUERY_NUM_FALLBACKS */ |
| if (!needed_swtnl) { |
| /* |
| * We're switching from HW to SW TNL. SW TNL will require mapping all |
| * currently bound vertex buffers, some of which may already be |
| * referenced in the current command buffer as result of previous HW |
| * TNL. So flush now, to prevent the context to flush while a referred |
| * vertex buffer is mapped. |
| */ |
| |
| svga_context_flush(svga, NULL); |
| } |
| |
| /* Avoid leaking the previous hwtnl bias to swtnl */ |
| svga_hwtnl_set_index_bias(svga->hwtnl, 0); |
| ret = svga_swtnl_draw_vbo(svga, info); |
| } |
| else { |
| if (!svga_update_state_retry(svga, SVGA_STATE_HW_DRAW)) { |
| static const char *msg = "State update failed, skipping draw call"; |
| debug_printf("%s\n", msg); |
| pipe_debug_message(&svga->debug.callback, INFO, "%s", msg); |
| goto done; |
| } |
| svga_hwtnl_set_fillmode(svga->hwtnl, svga->curr.rast->hw_fillmode); |
| |
| svga_update_state_retry(svga, SVGA_STATE_HW_DRAW); |
| |
| /** determine if flatshade is to be used after svga_update_state() |
| * in case the fragment shader is changed. |
| */ |
| svga_hwtnl_set_flatshade(svga->hwtnl, |
| svga->curr.rast->templ.flatshade || |
| svga_is_using_flat_shading(svga), |
| svga->curr.rast->templ.flatshade_first); |
| |
| if (info->count_from_stream_output) { |
| unsigned stream = 0; |
| assert(count == 0); |
| |
| /* If the vertex count is from the stream output of a non-zero stream |
| * or the draw info specifies instancing, we will need a workaround |
| * since the draw_auto command does not support stream instancing. |
| * The workaround requires querying the vertex count from the |
| * stream output statistics query for the specified stream and then |
| * fallback to the regular draw function. |
| */ |
| |
| /* Check the stream index of the specified stream output target */ |
| for (unsigned i = 0; i < ARRAY_SIZE(svga->so_targets); i++) { |
| if (svga->vcount_so_targets[i] == info->count_from_stream_output) { |
| stream = (svga->vcount_buffer_stream >> (i * 4)) & 0xf; |
| break; |
| } |
| } |
| if (info->instance_count > 1 || stream > 0) { |
| count = get_vcount_from_stream_output(svga, info, stream); |
| } |
| } |
| |
| if (info->count_from_stream_output && count == 0) { |
| ret = retry_draw_auto(svga, info); |
| } |
| else if (info->indirect) { |
| ret = retry_draw_indirect(svga, info); |
| } |
| else if (info->index_size) { |
| ret = retry_draw_range_elements(svga, info, count); |
| } |
| else { |
| ret = retry_draw_arrays(svga, info->mode, info->start, count, |
| info->start_instance, info->instance_count, |
| info->vertices_per_patch); |
| } |
| } |
| |
| /* XXX: Silence warnings, do something sensible here? */ |
| (void)ret; |
| |
| if (SVGA_DEBUG & DEBUG_FLUSH) { |
| svga_hwtnl_flush_retry(svga); |
| svga_context_flush(svga, NULL); |
| } |
| |
| done: |
| SVGA_STATS_TIME_POP(svga_sws(svga)); |
| } |
| |
| |
| void |
| svga_init_draw_functions(struct svga_context *svga) |
| { |
| svga->pipe.draw_vbo = svga_draw_vbo; |
| } |