+ /* 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);