/********************************************************** * 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 "pipe/p_compiler.h" #include "util/u_inlines.h" #include "pipe/p_defines.h" #include "util/u_helpers.h" #include "util/u_memory.h" #include "util/u_math.h" #include "svga_context.h" #include "svga_draw.h" #include "svga_draw_private.h" #include "svga_debug.h" #include "svga_screen.h" #include "svga_resource.h" #include "svga_resource_buffer.h" #include "svga_resource_texture.h" #include "svga_sampler_view.h" #include "svga_shader.h" #include "svga_surface.h" #include "svga_winsys.h" #include "svga_cmd.h" struct svga_hwtnl * svga_hwtnl_create(struct svga_context *svga) { struct svga_hwtnl *hwtnl = CALLOC_STRUCT(svga_hwtnl); if (!hwtnl) goto fail; hwtnl->svga = svga; hwtnl->cmd.swc = svga->swc; return hwtnl; fail: return NULL; } void svga_hwtnl_destroy(struct svga_hwtnl *hwtnl) { unsigned i, j; for (i = 0; i < PIPE_PRIM_MAX; i++) { for (j = 0; j < IDX_CACHE_MAX; j++) { pipe_resource_reference(&hwtnl->index_cache[i][j].buffer, NULL); } } for (i = 0; i < hwtnl->cmd.vbuf_count; i++) pipe_vertex_buffer_unreference(&hwtnl->cmd.vbufs[i]); for (i = 0; i < hwtnl->cmd.prim_count; i++) pipe_resource_reference(&hwtnl->cmd.prim_ib[i], NULL); FREE(hwtnl); } void svga_hwtnl_set_flatshade(struct svga_hwtnl *hwtnl, boolean flatshade, boolean flatshade_first) { struct svga_screen *svgascreen = svga_screen(hwtnl->svga->pipe.screen); /* User-specified PV */ hwtnl->api_pv = (flatshade && !flatshade_first) ? PV_LAST : PV_FIRST; /* Device supported PV */ if (svgascreen->haveProvokingVertex) { /* use the mode specified by the user */ hwtnl->hw_pv = hwtnl->api_pv; } else { /* the device only support first provoking vertex */ hwtnl->hw_pv = PV_FIRST; } } void svga_hwtnl_set_fillmode(struct svga_hwtnl *hwtnl, unsigned mode) { hwtnl->api_fillmode = mode; } void svga_hwtnl_vertex_decls(struct svga_hwtnl *hwtnl, unsigned count, const SVGA3dVertexDecl * decls, const unsigned *buffer_indexes, SVGA3dElementLayoutId layout_id) { assert(hwtnl->cmd.prim_count == 0); hwtnl->cmd.vdecl_count = count; hwtnl->cmd.vdecl_layout_id = layout_id; memcpy(hwtnl->cmd.vdecl, decls, count * sizeof(*decls)); memcpy(hwtnl->cmd.vdecl_buffer_index, buffer_indexes, count * sizeof(unsigned)); } /** * Specify vertex buffers for hardware drawing. */ void svga_hwtnl_vertex_buffers(struct svga_hwtnl *hwtnl, unsigned count, struct pipe_vertex_buffer *buffers) { struct pipe_vertex_buffer *dst = hwtnl->cmd.vbufs; const struct pipe_vertex_buffer *src = buffers; unsigned i; for (i = 0; i < count; i++) { pipe_vertex_buffer_reference(&dst[i], &src[i]); } /* release old buffer references */ for ( ; i < hwtnl->cmd.vbuf_count; i++) { pipe_vertex_buffer_unreference(&dst[i]); /* don't bother zeroing stride/offset fields */ } hwtnl->cmd.vbuf_count = count; } /** * Determine whether the specified buffer is referred in the primitive queue, * for which no commands have been written yet. */ boolean svga_hwtnl_is_buffer_referred(struct svga_hwtnl *hwtnl, struct pipe_resource *buffer) { unsigned i; if (svga_buffer_is_user_buffer(buffer)) { return FALSE; } if (!hwtnl->cmd.prim_count) { return FALSE; } for (i = 0; i < hwtnl->cmd.vbuf_count; ++i) { if (hwtnl->cmd.vbufs[i].buffer.resource == buffer) { return TRUE; } } for (i = 0; i < hwtnl->cmd.prim_count; ++i) { if (hwtnl->cmd.prim_ib[i] == buffer) { return TRUE; } } return FALSE; } static enum pipe_error draw_vgpu9(struct svga_hwtnl *hwtnl) { struct svga_winsys_context *swc = hwtnl->cmd.swc; struct svga_context *svga = hwtnl->svga; enum pipe_error ret; struct svga_winsys_surface *vb_handle[SVGA3D_INPUTREG_MAX]; struct svga_winsys_surface *ib_handle[QSZ]; struct svga_winsys_surface *handle; SVGA3dVertexDecl *vdecl; SVGA3dPrimitiveRange *prim; unsigned i; /* Re-validate those sampler views with backing copy * of texture whose original copy has been updated. * This is done here at draw time because the texture binding might not * have modified, hence validation is not triggered at state update time, * and yet the texture might have been updated in another context, so * we need to re-validate the sampler view in order to update the backing * copy of the updated texture. */ if (svga->state.hw_draw.num_backed_views) { for (i = 0; i < svga->state.hw_draw.num_views; i++) { struct svga_hw_view_state *view = &svga->state.hw_draw.views[i]; struct svga_texture *tex = svga_texture(view->texture); struct svga_sampler_view *sv = view->v; if (sv && tex && sv->handle != tex->handle && sv->age < tex->age) svga_validate_sampler_view(svga, view->v); } } for (i = 0; i < hwtnl->cmd.vdecl_count; i++) { unsigned j = hwtnl->cmd.vdecl_buffer_index[i]; handle = svga_buffer_handle(svga, hwtnl->cmd.vbufs[j].buffer.resource, PIPE_BIND_VERTEX_BUFFER); if (!handle) return PIPE_ERROR_OUT_OF_MEMORY; vb_handle[i] = handle; } for (i = 0; i < hwtnl->cmd.prim_count; i++) { if (hwtnl->cmd.prim_ib[i]) { handle = svga_buffer_handle(svga, hwtnl->cmd.prim_ib[i], PIPE_BIND_INDEX_BUFFER); if (!handle) return PIPE_ERROR_OUT_OF_MEMORY; } else handle = NULL; ib_handle[i] = handle; } if (svga->rebind.flags.rendertargets) { ret = svga_reemit_framebuffer_bindings(svga); if (ret != PIPE_OK) { return ret; } } if (svga->rebind.flags.texture_samplers) { ret = svga_reemit_tss_bindings(svga); if (ret != PIPE_OK) { return ret; } } if (svga->rebind.flags.vs) { ret = svga_reemit_vs_bindings(svga); if (ret != PIPE_OK) { return ret; } } if (svga->rebind.flags.fs) { ret = svga_reemit_fs_bindings(svga); if (ret != PIPE_OK) { return ret; } } SVGA_DBG(DEBUG_DMA, "draw to sid %p, %d prims\n", svga->curr.framebuffer.cbufs[0] ? svga_surface(svga->curr.framebuffer.cbufs[0])->handle : NULL, hwtnl->cmd.prim_count); ret = SVGA3D_BeginDrawPrimitives(swc, &vdecl, hwtnl->cmd.vdecl_count, &prim, hwtnl->cmd.prim_count); if (ret != PIPE_OK) return ret; memcpy(vdecl, hwtnl->cmd.vdecl, hwtnl->cmd.vdecl_count * sizeof hwtnl->cmd.vdecl[0]); for (i = 0; i < hwtnl->cmd.vdecl_count; i++) { /* check for 4-byte alignment */ assert(vdecl[i].array.offset % 4 == 0); assert(vdecl[i].array.stride % 4 == 0); /* Given rangeHint is considered to be relative to indexBias, and * indexBias varies per primitive, we cannot accurately supply an * rangeHint when emitting more than one primitive per draw command. */ if (hwtnl->cmd.prim_count == 1) { vdecl[i].rangeHint.first = hwtnl->cmd.min_index[0]; vdecl[i].rangeHint.last = hwtnl->cmd.max_index[0] + 1; } else { vdecl[i].rangeHint.first = 0; vdecl[i].rangeHint.last = 0; } swc->surface_relocation(swc, &vdecl[i].array.surfaceId, NULL, vb_handle[i], SVGA_RELOC_READ); } memcpy(prim, hwtnl->cmd.prim, hwtnl->cmd.prim_count * sizeof hwtnl->cmd.prim[0]); for (i = 0; i < hwtnl->cmd.prim_count; i++) { swc->surface_relocation(swc, &prim[i].indexArray.surfaceId, NULL, ib_handle[i], SVGA_RELOC_READ); pipe_resource_reference(&hwtnl->cmd.prim_ib[i], NULL); } SVGA_FIFOCommitAll(swc); hwtnl->cmd.prim_count = 0; return PIPE_OK; } static SVGA3dSurfaceFormat xlate_index_format(unsigned indexWidth) { if (indexWidth == 2) { return SVGA3D_R16_UINT; } else if (indexWidth == 4) { return SVGA3D_R32_UINT; } else { assert(!"Bad indexWidth"); return SVGA3D_R32_UINT; } } static enum pipe_error validate_sampler_resources(struct svga_context *svga) { enum pipe_shader_type shader; assert(svga_have_vgpu10(svga)); for (shader = PIPE_SHADER_VERTEX; shader <= PIPE_SHADER_COMPUTE; shader++) { unsigned count = svga->curr.num_sampler_views[shader]; unsigned i; struct svga_winsys_surface *surfaces[PIPE_MAX_SAMPLERS]; enum pipe_error ret; /* * Reference bound sampler resources to ensure pending updates are * noticed by the device. */ for (i = 0; i < count; i++) { struct svga_pipe_sampler_view *sv = svga_pipe_sampler_view(svga->curr.sampler_views[shader][i]); if (sv) { if (sv->base.texture->target == PIPE_BUFFER) { surfaces[i] = svga_buffer_handle(svga, sv->base.texture, PIPE_BIND_SAMPLER_VIEW); } else { surfaces[i] = svga_texture(sv->base.texture)->handle; } } else { surfaces[i] = NULL; } } if (shader == PIPE_SHADER_FRAGMENT && svga->curr.rast->templ.poly_stipple_enable) { const unsigned unit = svga_fs_variant(svga->state.hw_draw.fs)->pstipple_sampler_unit; struct svga_pipe_sampler_view *sv = svga->polygon_stipple.sampler_view; assert(sv); surfaces[unit] = svga_texture(sv->base.texture)->handle; count = MAX2(count, unit+1); } /* rebind the shader resources if needed */ if (svga->rebind.flags.texture_samplers) { for (i = 0; i < count; i++) { if (surfaces[i]) { ret = svga->swc->resource_rebind(svga->swc, surfaces[i], NULL, SVGA_RELOC_READ); if (ret != PIPE_OK) return ret; } } } } svga->rebind.flags.texture_samplers = FALSE; return PIPE_OK; } static enum pipe_error validate_constant_buffers(struct svga_context *svga) { enum pipe_shader_type shader; assert(svga_have_vgpu10(svga)); for (shader = PIPE_SHADER_VERTEX; shader <= PIPE_SHADER_COMPUTE; shader++) { enum pipe_error ret; struct svga_buffer *buffer; /* Rebind the default constant buffer if needed */ if (svga->rebind.flags.constbufs) { buffer = svga_buffer(svga->state.hw_draw.constbuf[shader][0]); if (buffer) { ret = svga->swc->resource_rebind(svga->swc, buffer->handle, NULL, SVGA_RELOC_READ); if (ret != PIPE_OK) return ret; } } struct svga_winsys_surface *handle; unsigned enabled_constbufs; /* * Reference other bound constant buffers to ensure pending updates are * noticed by the device. */ enabled_constbufs = svga->state.hw_draw.enabled_constbufs[shader] & ~1u; while (enabled_constbufs) { unsigned i = u_bit_scan(&enabled_constbufs); buffer = svga_buffer(svga->curr.constbufs[shader][i].buffer); /* If the constant buffer has hw storage, get the buffer winsys handle. * Rebind the resource if needed. */ if (buffer && !buffer->use_swbuf) handle = svga_buffer_handle(svga, &buffer->b.b, PIPE_BIND_CONSTANT_BUFFER); else handle = svga->state.hw_draw.constbufoffsets[shader][i].handle; if (svga->rebind.flags.constbufs && handle) { ret = svga->swc->resource_rebind(svga->swc, handle, NULL, SVGA_RELOC_READ); if (ret != PIPE_OK) return ret; } } } svga->rebind.flags.constbufs = FALSE; return PIPE_OK; } /** * Was the last command put into the command buffer a drawing command? * We use this to determine if we can skip emitting buffer re-bind * commands when we have a sequence of drawing commands that use the * same vertex/index buffers with no intervening commands. * * The first drawing command will bind the vertex/index buffers. If * the immediately following command is also a drawing command using the * same buffers, we shouldn't have to rebind them. */ static bool last_command_was_draw(const struct svga_context *svga) { switch (SVGA3D_GetLastCommand(svga->swc)) { case SVGA_3D_CMD_DX_DRAW: case SVGA_3D_CMD_DX_DRAW_INDEXED: case SVGA_3D_CMD_DX_DRAW_INSTANCED: case SVGA_3D_CMD_DX_DRAW_INDEXED_INSTANCED: case SVGA_3D_CMD_DX_DRAW_AUTO: case SVGA_3D_CMD_DX_DRAW_INDEXED_INSTANCED_INDIRECT: case SVGA_3D_CMD_DX_DRAW_INSTANCED_INDIRECT: return true; default: return false; } } /** * A helper function to compare vertex buffers. * They are equal if the vertex buffer attributes and the vertex buffer * resources are identical. */ static boolean vertex_buffers_equal(unsigned count, SVGA3dVertexBuffer *pVBufAttr1, struct pipe_resource **pVBuf1, SVGA3dVertexBuffer *pVBufAttr2, struct pipe_resource **pVBuf2) { return (memcmp(pVBufAttr1, pVBufAttr2, count * sizeof(*pVBufAttr1)) == 0) && (memcmp(pVBuf1, pVBuf2, count * sizeof(*pVBuf1)) == 0); } /* * Prepare the vertex buffers for a drawing command. */ static enum pipe_error validate_vertex_buffers(struct svga_hwtnl *hwtnl, const struct pipe_stream_output_target *so_vertex_count) { struct svga_context *svga = hwtnl->svga; struct pipe_resource *vbuffers[SVGA3D_INPUTREG_MAX]; struct svga_winsys_surface *vbuffer_handles[SVGA3D_INPUTREG_MAX]; struct svga_winsys_surface *so_vertex_count_handle; const unsigned vbuf_count = so_vertex_count ? 1 : hwtnl->cmd.vbuf_count; int last_vbuf = -1; unsigned i; assert(svga_have_vgpu10(svga)); /* Get handle for each referenced vertex buffer, unless we're using a * stream-out buffer to specify the drawing information (DrawAuto). */ if (so_vertex_count) { i = 0; } else { for (i = 0; i < vbuf_count; i++) { struct svga_buffer *sbuf = svga_buffer(hwtnl->cmd.vbufs[i].buffer.resource); if (sbuf) { vbuffer_handles[i] = svga_buffer_handle(svga, &sbuf->b.b, PIPE_BIND_VERTEX_BUFFER); assert(sbuf->key.flags & SVGA3D_SURFACE_BIND_VERTEX_BUFFER); if (vbuffer_handles[i] == NULL) return PIPE_ERROR_OUT_OF_MEMORY; vbuffers[i] = &sbuf->b.b; last_vbuf = i; } else { vbuffers[i] = NULL; vbuffer_handles[i] = NULL; } } } for (; i < svga->state.hw_draw.num_vbuffers; i++) { vbuffers[i] = NULL; vbuffer_handles[i] = NULL; } /* Get handle for each referenced vertex buffer */ for (i = 0; i < vbuf_count; i++) { struct svga_buffer *sbuf = svga_buffer(hwtnl->cmd.vbufs[i].buffer.resource); if (sbuf) { vbuffer_handles[i] = svga_buffer_handle(svga, &sbuf->b.b, PIPE_BIND_VERTEX_BUFFER); assert(sbuf->key.flags & SVGA3D_SURFACE_BIND_VERTEX_BUFFER); if (vbuffer_handles[i] == NULL) return PIPE_ERROR_OUT_OF_MEMORY; vbuffers[i] = &sbuf->b.b; last_vbuf = i; } else { vbuffers[i] = NULL; vbuffer_handles[i] = NULL; } } for (; i < svga->state.hw_draw.num_vbuffers; i++) { vbuffers[i] = NULL; vbuffer_handles[i] = NULL; } /* setup vertex attribute input layout */ if (svga->state.hw_draw.layout_id != hwtnl->cmd.vdecl_layout_id) { enum pipe_error ret = SVGA3D_vgpu10_SetInputLayout(svga->swc, hwtnl->cmd.vdecl_layout_id); if (ret != PIPE_OK) return ret; svga->state.hw_draw.layout_id = hwtnl->cmd.vdecl_layout_id; } /* Get handle for the stream out buffer */ if (so_vertex_count) { so_vertex_count_handle = svga_buffer_handle(svga, so_vertex_count->buffer, (PIPE_BIND_VERTEX_BUFFER | PIPE_BIND_STREAM_OUTPUT)); if (!so_vertex_count_handle) return PIPE_ERROR_OUT_OF_MEMORY; } else { so_vertex_count_handle = NULL; } /* setup vertex buffers */ { SVGA3dVertexBuffer vbuffer_attrs[PIPE_MAX_ATTRIBS]; if (so_vertex_count) { /* Set IA slot0 input buffer to the SO buffer */ assert(vbuf_count == 1); vbuffer_attrs[0].stride = hwtnl->cmd.vbufs[0].stride; vbuffer_attrs[0].offset = hwtnl->cmd.vbufs[0].buffer_offset; vbuffer_attrs[0].sid = 0; vbuffers[0] = so_vertex_count->buffer; vbuffer_handles[0] = so_vertex_count_handle; } else { for (i = 0; i < vbuf_count; i++) { vbuffer_attrs[i].stride = hwtnl->cmd.vbufs[i].stride; vbuffer_attrs[i].offset = hwtnl->cmd.vbufs[i].buffer_offset; vbuffer_attrs[i].sid = 0; } } /* If any of the vertex buffer state has changed, issue * the SetVertexBuffers command. Otherwise, we will just * need to rebind the resources. */ if (vbuf_count != svga->state.hw_draw.num_vbuffers || !vertex_buffers_equal(vbuf_count, vbuffer_attrs, vbuffers, svga->state.hw_draw.vbuffer_attrs, svga->state.hw_draw.vbuffers)) { unsigned num_vbuffers; /* get the max of the current bound vertex buffers count and * the to-be-bound vertex buffers count, so as to unbind * the unused vertex buffers. */ num_vbuffers = MAX2(vbuf_count, svga->state.hw_draw.num_vbuffers); /* Zero-out the old buffers we want to unbind (the number of loop * iterations here is typically very small, and often zero.) */ for (i = vbuf_count; i < num_vbuffers; i++) { vbuffer_attrs[i].sid = 0; vbuffer_attrs[i].stride = 0; vbuffer_attrs[i].offset = 0; vbuffer_handles[i] = NULL; } if (num_vbuffers > 0) { SVGA3dVertexBuffer *pbufAttrs = vbuffer_attrs; struct svga_winsys_surface **pbufHandles = vbuffer_handles; unsigned numVBuf = 0; /* Loop through the vertex buffer lists to only emit * those vertex buffers that are not already in the * corresponding entries in the device's vertex buffer list. */ for (i = 0; i < num_vbuffers; i++) { boolean emit = vertex_buffers_equal(1, &vbuffer_attrs[i], &vbuffers[i], &svga->state.hw_draw.vbuffer_attrs[i], &svga->state.hw_draw.vbuffers[i]); if (!emit && i == num_vbuffers-1) { /* Include the last vertex buffer in the next emit * if it is different. */ emit = TRUE; numVBuf++; i++; } if (emit) { /* numVBuf can only be 0 if the first vertex buffer * is the same as the one in the device's list. * In this case, there is nothing to send yet. */ if (numVBuf) { enum pipe_error ret = SVGA3D_vgpu10_SetVertexBuffers(svga->swc, numVBuf, i - numVBuf, pbufAttrs, pbufHandles); if (ret != PIPE_OK) return ret; } pbufAttrs += (numVBuf + 1); pbufHandles += (numVBuf + 1); numVBuf = 0; } else numVBuf++; } /* save the number of vertex buffers sent to the device, not * including trailing unbound vertex buffers. */ svga->state.hw_draw.num_vbuffers = last_vbuf + 1; memcpy(svga->state.hw_draw.vbuffer_attrs, vbuffer_attrs, num_vbuffers * sizeof(vbuffer_attrs[0])); for (i = 0; i < num_vbuffers; i++) { pipe_resource_reference(&svga->state.hw_draw.vbuffers[i], vbuffers[i]); } } } else { /* Even though we can avoid emitting the redundant SetVertexBuffers * command, we still need to reference the vertex buffers surfaces. */ for (i = 0; i < vbuf_count; i++) { if (vbuffer_handles[i] && !last_command_was_draw(svga)) { enum pipe_error ret = svga->swc->resource_rebind(svga->swc, vbuffer_handles[i], NULL, SVGA_RELOC_READ); if (ret != PIPE_OK) return ret; } } } } return PIPE_OK; } /* * Prepare the index buffer for a drawing command. */ static enum pipe_error validate_index_buffer(struct svga_hwtnl *hwtnl, const SVGA3dPrimitiveRange *range, struct pipe_resource *ib) { struct svga_context *svga = hwtnl->svga; struct svga_winsys_surface *ib_handle = svga_buffer_handle(svga, ib, PIPE_BIND_INDEX_BUFFER); if (!ib_handle) return PIPE_ERROR_OUT_OF_MEMORY; struct svga_buffer *sbuf = svga_buffer(ib); assert(sbuf->key.flags & SVGA3D_SURFACE_BIND_INDEX_BUFFER); (void) sbuf; /* silence unused var warning */ SVGA3dSurfaceFormat indexFormat = xlate_index_format(range->indexWidth); if (ib != svga->state.hw_draw.ib || indexFormat != svga->state.hw_draw.ib_format || range->indexArray.offset != svga->state.hw_draw.ib_offset) { assert(indexFormat != SVGA3D_FORMAT_INVALID); enum pipe_error ret = SVGA3D_vgpu10_SetIndexBuffer(svga->swc, ib_handle, indexFormat, range->indexArray.offset); if (ret != PIPE_OK) return ret; pipe_resource_reference(&svga->state.hw_draw.ib, ib); svga->state.hw_draw.ib_format = indexFormat; svga->state.hw_draw.ib_offset = range->indexArray.offset; } else { /* Even though we can avoid emitting the redundant SetIndexBuffer * command, we still need to reference the index buffer surface. */ if (!last_command_was_draw(svga)) { enum pipe_error ret = svga->swc->resource_rebind(svga->swc, ib_handle, NULL, SVGA_RELOC_READ); if (ret != PIPE_OK) return ret; } } return PIPE_OK; } static enum pipe_error draw_vgpu10(struct svga_hwtnl *hwtnl, const SVGA3dPrimitiveRange *range, unsigned vcount, unsigned min_index, unsigned max_index, struct pipe_resource *ib, unsigned start_instance, unsigned instance_count, const struct pipe_draw_indirect_info *indirect, const struct pipe_stream_output_target *so_vertex_count) { struct svga_context *svga = hwtnl->svga; struct svga_winsys_surface *indirect_handle; enum pipe_error ret; assert(svga_have_vgpu10(svga)); assert(hwtnl->cmd.prim_count == 0); /* We need to reemit all the current resource bindings along with the Draw * command to be sure that the referenced resources are available for the * Draw command, just in case the surfaces associated with the resources * are paged out. */ if (svga->rebind.val) { ret = svga_rebind_framebuffer_bindings(svga); if (ret != PIPE_OK) return ret; ret = svga_rebind_shaders(svga); if (ret != PIPE_OK) return ret; /* Rebind stream output targets */ ret = svga_rebind_stream_output_targets(svga); if (ret != PIPE_OK) return ret; /* No need to explicitly rebind index buffer and vertex buffers here. * Even if the same index buffer or vertex buffers are referenced for this * draw and we skip emitting the redundant set command, we will still * reference the associated resources. */ } ret = validate_sampler_resources(svga); if (ret != PIPE_OK) return ret; ret = validate_constant_buffers(svga); if (ret != PIPE_OK) return ret; ret = validate_vertex_buffers(hwtnl, so_vertex_count); if (ret != PIPE_OK) return ret; if (ib) { ret = validate_index_buffer(hwtnl, range, ib); if (ret != PIPE_OK) return ret; } if (indirect) { indirect_handle = svga_buffer_handle(svga, indirect->buffer, PIPE_BIND_COMMAND_ARGS_BUFFER); if (!indirect_handle) return PIPE_ERROR_OUT_OF_MEMORY; } else { indirect_handle = NULL; } /* Set primitive type (line, tri, etc) */ if (svga->state.hw_draw.topology != range->primType) { ret = SVGA3D_vgpu10_SetTopology(svga->swc, range->primType); if (ret != PIPE_OK) return ret; svga->state.hw_draw.topology = range->primType; } if (ib) { /* indexed drawing */ if (indirect) { ret = SVGA3D_sm5_DrawIndexedInstancedIndirect(svga->swc, indirect_handle, indirect->offset); } else if (instance_count > 1) { ret = SVGA3D_vgpu10_DrawIndexedInstanced(svga->swc, vcount, instance_count, 0, /* startIndexLocation */ range->indexBias, start_instance); } else { /* non-instanced drawing */ ret = SVGA3D_vgpu10_DrawIndexed(svga->swc, vcount, 0, /* startIndexLocation */ range->indexBias); } if (ret != PIPE_OK) { return ret; } } else { /* non-indexed drawing */ if (svga->state.hw_draw.ib_format != SVGA3D_FORMAT_INVALID || svga->state.hw_draw.ib != NULL) { /* Unbind previously bound index buffer */ ret = SVGA3D_vgpu10_SetIndexBuffer(svga->swc, NULL, SVGA3D_FORMAT_INVALID, 0); if (ret != PIPE_OK) return ret; pipe_resource_reference(&svga->state.hw_draw.ib, NULL); svga->state.hw_draw.ib_format = SVGA3D_FORMAT_INVALID; } assert(svga->state.hw_draw.ib == NULL); if (so_vertex_count) { /* Stream-output drawing */ ret = SVGA3D_vgpu10_DrawAuto(svga->swc); } else if (indirect) { ret = SVGA3D_sm5_DrawInstancedIndirect(svga->swc, indirect_handle, indirect->offset); } else if (instance_count > 1) { ret = SVGA3D_vgpu10_DrawInstanced(svga->swc, vcount, instance_count, range->indexBias, start_instance); } else { /* non-instanced */ ret = SVGA3D_vgpu10_Draw(svga->swc, vcount, range->indexBias); } if (ret != PIPE_OK) { return ret; } } hwtnl->cmd.prim_count = 0; return PIPE_OK; } /** * Emit any pending drawing commands to the command buffer. * When we receive VGPU9 drawing commands we accumulate them and don't * immediately emit them into the command buffer. * This function needs to be called before we change state that could * effect those pending draws. */ enum pipe_error svga_hwtnl_flush(struct svga_hwtnl *hwtnl) { enum pipe_error ret = PIPE_OK; SVGA_STATS_TIME_PUSH(svga_sws(hwtnl->svga), SVGA_STATS_TIME_HWTNLFLUSH); if (!svga_have_vgpu10(hwtnl->svga) && hwtnl->cmd.prim_count) { /* we only queue up primitive for VGPU9 */ ret = draw_vgpu9(hwtnl); } SVGA_STATS_TIME_POP(svga_screen(hwtnl->svga->pipe.screen)->sws); return ret; } void svga_hwtnl_set_index_bias(struct svga_hwtnl *hwtnl, int index_bias) { hwtnl->index_bias = index_bias; } /*********************************************************************** * Internal functions: */ /** * For debugging only. */ static void check_draw_params(struct svga_hwtnl *hwtnl, const SVGA3dPrimitiveRange *range, unsigned min_index, unsigned max_index, struct pipe_resource *ib) { unsigned i; assert(!svga_have_vgpu10(hwtnl->svga)); for (i = 0; i < hwtnl->cmd.vdecl_count; i++) { unsigned j = hwtnl->cmd.vdecl_buffer_index[i]; const struct pipe_vertex_buffer *vb = &hwtnl->cmd.vbufs[j]; unsigned size = vb->buffer.resource ? vb->buffer.resource->width0 : 0; unsigned offset = hwtnl->cmd.vdecl[i].array.offset; unsigned stride = hwtnl->cmd.vdecl[i].array.stride; int index_bias = (int) range->indexBias + hwtnl->index_bias; unsigned width; if (size == 0) continue; assert(vb); assert(size); assert(offset < size); assert(min_index <= max_index); (void) width; (void) stride; (void) offset; (void) size; switch (hwtnl->cmd.vdecl[i].identity.type) { case SVGA3D_DECLTYPE_FLOAT1: width = 4; break; case SVGA3D_DECLTYPE_FLOAT2: width = 4 * 2; break; case SVGA3D_DECLTYPE_FLOAT3: width = 4 * 3; break; case SVGA3D_DECLTYPE_FLOAT4: width = 4 * 4; break; case SVGA3D_DECLTYPE_D3DCOLOR: width = 4; break; case SVGA3D_DECLTYPE_UBYTE4: width = 1 * 4; break; case SVGA3D_DECLTYPE_SHORT2: width = 2 * 2; break; case SVGA3D_DECLTYPE_SHORT4: width = 2 * 4; break; case SVGA3D_DECLTYPE_UBYTE4N: width = 1 * 4; break; case SVGA3D_DECLTYPE_SHORT2N: width = 2 * 2; break; case SVGA3D_DECLTYPE_SHORT4N: width = 2 * 4; break; case SVGA3D_DECLTYPE_USHORT2N: width = 2 * 2; break; case SVGA3D_DECLTYPE_USHORT4N: width = 2 * 4; break; case SVGA3D_DECLTYPE_UDEC3: width = 4; break; case SVGA3D_DECLTYPE_DEC3N: width = 4; break; case SVGA3D_DECLTYPE_FLOAT16_2: width = 2 * 2; break; case SVGA3D_DECLTYPE_FLOAT16_4: width = 2 * 4; break; default: assert(0); width = 0; break; } if (index_bias >= 0) { assert(offset + index_bias * stride + width <= size); } /* * min_index/max_index are merely conservative guesses, so we can't * make buffer overflow detection based on their values. */ } assert(range->indexWidth == range->indexArray.stride); if (ib) { ASSERTED unsigned size = ib->width0; ASSERTED unsigned offset = range->indexArray.offset; ASSERTED unsigned stride = range->indexArray.stride; ASSERTED unsigned count; assert(size); assert(offset < size); assert(stride); switch (range->primType) { case SVGA3D_PRIMITIVE_POINTLIST: count = range->primitiveCount; break; case SVGA3D_PRIMITIVE_LINELIST: count = range->primitiveCount * 2; break; case SVGA3D_PRIMITIVE_LINESTRIP: count = range->primitiveCount + 1; break; case SVGA3D_PRIMITIVE_TRIANGLELIST: count = range->primitiveCount * 3; break; case SVGA3D_PRIMITIVE_TRIANGLESTRIP: count = range->primitiveCount + 2; break; case SVGA3D_PRIMITIVE_TRIANGLEFAN: count = range->primitiveCount + 2; break; default: assert(0); count = 0; break; } assert(offset + count * stride <= size); } } /** * All drawing filters down into this function, either directly * on the hardware path or after doing software vertex processing. * \param indirect if non-null, get the vertex count, first vertex, etc. * from a buffer. * \param so_vertex_count if non-null, get the vertex count from a * stream-output target. */ enum pipe_error svga_hwtnl_prim(struct svga_hwtnl *hwtnl, const SVGA3dPrimitiveRange *range, unsigned vcount, unsigned min_index, unsigned max_index, struct pipe_resource *ib, unsigned start_instance, unsigned instance_count, const struct pipe_draw_indirect_info *indirect, const struct pipe_stream_output_target *so_vertex_count) { enum pipe_error ret = PIPE_OK; SVGA_STATS_TIME_PUSH(svga_sws(hwtnl->svga), SVGA_STATS_TIME_HWTNLPRIM); if (svga_have_vgpu10(hwtnl->svga)) { /* draw immediately */ SVGA_RETRY(hwtnl->svga, draw_vgpu10(hwtnl, range, vcount, min_index, max_index, ib, start_instance, instance_count, indirect, so_vertex_count)); } else { /* batch up drawing commands */ assert(indirect == NULL); #ifdef DEBUG check_draw_params(hwtnl, range, min_index, max_index, ib); assert(start_instance == 0); assert(instance_count <= 1); #else (void) check_draw_params; #endif if (hwtnl->cmd.prim_count + 1 >= QSZ) { ret = svga_hwtnl_flush(hwtnl); if (ret != PIPE_OK) goto done; } /* min/max indices are relative to bias */ hwtnl->cmd.min_index[hwtnl->cmd.prim_count] = min_index; hwtnl->cmd.max_index[hwtnl->cmd.prim_count] = max_index; hwtnl->cmd.prim[hwtnl->cmd.prim_count] = *range; hwtnl->cmd.prim[hwtnl->cmd.prim_count].indexBias += hwtnl->index_bias; pipe_resource_reference(&hwtnl->cmd.prim_ib[hwtnl->cmd.prim_count], ib); hwtnl->cmd.prim_count++; } done: SVGA_STATS_TIME_POP(svga_screen(hwtnl->svga->pipe.screen)->sws); return ret; } /** * Return TRUE if there are pending primitives. */ boolean svga_hwtnl_has_pending_prim(struct svga_hwtnl *hwtnl) { return hwtnl->cmd.prim_count > 0; }