Brian Paul | ff85bcd | 2015-08-07 15:21:46 -0600 | [diff] [blame] | 1 | /********************************************************** |
| 2 | * Copyright 2014 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 | #include "util/u_memory.h" |
| 27 | #include "util/u_bitmask.h" |
| 28 | |
| 29 | #include "svga_cmd.h" |
| 30 | #include "svga_context.h" |
| 31 | #include "svga_resource_buffer.h" |
| 32 | #include "svga_shader.h" |
| 33 | #include "svga_debug.h" |
| 34 | #include "svga_streamout.h" |
| 35 | |
| 36 | struct svga_stream_output_target { |
| 37 | struct pipe_stream_output_target base; |
| 38 | }; |
| 39 | |
| 40 | /** cast wrapper */ |
Brian Paul | e054251 | 2015-08-13 11:00:58 -0700 | [diff] [blame] | 41 | static inline struct svga_stream_output_target * |
Brian Paul | ff85bcd | 2015-08-07 15:21:46 -0600 | [diff] [blame] | 42 | svga_stream_output_target(struct pipe_stream_output_target *s) |
| 43 | { |
| 44 | return (struct svga_stream_output_target *)s; |
| 45 | } |
| 46 | |
| 47 | struct svga_stream_output * |
| 48 | svga_create_stream_output(struct svga_context *svga, |
| 49 | struct svga_shader *shader, |
| 50 | const struct pipe_stream_output_info *info) |
| 51 | { |
| 52 | struct svga_stream_output *streamout; |
| 53 | SVGA3dStreamOutputDeclarationEntry decls[SVGA3D_MAX_STREAMOUT_DECLS]; |
| 54 | unsigned strides[SVGA3D_DX_MAX_SOTARGETS]; |
| 55 | unsigned i; |
| 56 | enum pipe_error ret; |
| 57 | unsigned id; |
| 58 | |
| 59 | assert(info->num_outputs <= PIPE_MAX_SO_OUTPUTS); |
| 60 | |
| 61 | /* Gallium utility creates shaders with stream output. |
| 62 | * For non-DX10, just return NULL. |
| 63 | */ |
| 64 | if (!svga_have_vgpu10(svga)) |
| 65 | return NULL; |
| 66 | |
| 67 | assert(info->num_outputs <= SVGA3D_MAX_STREAMOUT_DECLS); |
| 68 | |
| 69 | /* Allocate an integer ID for the stream output */ |
| 70 | id = util_bitmask_add(svga->stream_output_id_bm); |
| 71 | if (id == UTIL_BITMASK_INVALID_INDEX) { |
| 72 | return NULL; |
| 73 | } |
| 74 | |
| 75 | /* Allocate the streamout data structure */ |
| 76 | streamout = CALLOC_STRUCT(svga_stream_output); |
| 77 | |
Edward O'Callaghan | 13eb5f5 | 2015-12-04 22:08:22 +1100 | [diff] [blame] | 78 | if (!streamout) |
Brian Paul | ff85bcd | 2015-08-07 15:21:46 -0600 | [diff] [blame] | 79 | return NULL; |
| 80 | |
| 81 | streamout->info = *info; |
| 82 | streamout->id = id; |
| 83 | streamout->pos_out_index = -1; |
| 84 | |
| 85 | SVGA_DBG(DEBUG_STREAMOUT, "%s, num_outputs=%d id=%d\n", __FUNCTION__, |
| 86 | info->num_outputs, id); |
| 87 | |
| 88 | /* init whole decls and stride arrays to zero to avoid garbage values */ |
| 89 | memset(decls, 0, sizeof(decls)); |
| 90 | memset(strides, 0, sizeof(strides)); |
| 91 | |
| 92 | for (i = 0; i < info->num_outputs; i++) { |
| 93 | unsigned reg_idx = info->output[i].register_index; |
| 94 | unsigned buf_idx = info->output[i].output_buffer; |
Brian Paul | 1b5e88b | 2017-07-10 14:03:48 -0600 | [diff] [blame] | 95 | const enum tgsi_semantic sem_name = |
| 96 | shader->info.output_semantic_name[reg_idx]; |
Brian Paul | ff85bcd | 2015-08-07 15:21:46 -0600 | [diff] [blame] | 97 | |
| 98 | assert(buf_idx <= PIPE_MAX_SO_BUFFERS); |
| 99 | |
| 100 | if (sem_name == TGSI_SEMANTIC_POSITION) { |
| 101 | /** |
| 102 | * Check if streaming out POSITION. If so, replace the |
| 103 | * register index with the index for NON_ADJUSTED POSITION. |
| 104 | */ |
| 105 | decls[i].registerIndex = shader->info.num_outputs; |
| 106 | |
| 107 | /* Save this output index, so we can tell later if this stream output |
| 108 | * includes an output of a vertex position |
| 109 | */ |
| 110 | streamout->pos_out_index = i; |
| 111 | } |
| 112 | else if (sem_name == TGSI_SEMANTIC_CLIPDIST) { |
| 113 | /** |
| 114 | * Use the shadow copy for clip distance because |
| 115 | * CLIPDIST instruction is only emitted for enabled clip planes. |
| 116 | * It's valid to write to ClipDistance variable for non-enabled |
| 117 | * clip planes. |
| 118 | */ |
| 119 | decls[i].registerIndex = shader->info.num_outputs + 1 + |
| 120 | shader->info.output_semantic_index[reg_idx]; |
| 121 | } |
| 122 | else { |
| 123 | decls[i].registerIndex = reg_idx; |
| 124 | } |
| 125 | |
| 126 | decls[i].outputSlot = buf_idx; |
| 127 | decls[i].registerMask = |
| 128 | ((1 << info->output[i].num_components) - 1) |
| 129 | << info->output[i].start_component; |
| 130 | |
| 131 | SVGA_DBG(DEBUG_STREAMOUT, "%d slot=%d regIdx=%d regMask=0x%x\n", |
| 132 | i, decls[i].outputSlot, decls[i].registerIndex, |
| 133 | decls[i].registerMask); |
| 134 | |
| 135 | strides[buf_idx] = info->stride[buf_idx] * sizeof(float); |
| 136 | } |
| 137 | |
| 138 | ret = SVGA3D_vgpu10_DefineStreamOutput(svga->swc, id, |
| 139 | info->num_outputs, |
| 140 | strides, |
| 141 | decls); |
| 142 | if (ret != PIPE_OK) { |
| 143 | svga_context_flush(svga, NULL); |
| 144 | ret = SVGA3D_vgpu10_DefineStreamOutput(svga->swc, id, |
| 145 | info->num_outputs, |
| 146 | strides, |
| 147 | decls); |
| 148 | if (ret != PIPE_OK) { |
| 149 | util_bitmask_clear(svga->stream_output_id_bm, id); |
| 150 | FREE(streamout); |
| 151 | streamout = NULL; |
| 152 | } |
| 153 | } |
| 154 | return streamout; |
| 155 | } |
| 156 | |
| 157 | enum pipe_error |
| 158 | svga_set_stream_output(struct svga_context *svga, |
| 159 | struct svga_stream_output *streamout) |
| 160 | { |
Brian Paul | ff85bcd | 2015-08-07 15:21:46 -0600 | [diff] [blame] | 161 | unsigned id = streamout ? streamout->id : SVGA3D_INVALID_ID; |
| 162 | |
| 163 | if (!svga_have_vgpu10(svga)) { |
| 164 | return PIPE_OK; |
| 165 | } |
| 166 | |
| 167 | SVGA_DBG(DEBUG_STREAMOUT, "%s streamout=0x%x id=%d\n", __FUNCTION__, |
| 168 | streamout, id); |
| 169 | |
| 170 | if (svga->current_so != streamout) { |
Brian Paul | 4f3974d | 2017-06-22 14:45:07 -0600 | [diff] [blame] | 171 | enum pipe_error ret = SVGA3D_vgpu10_SetStreamOutput(svga->swc, id); |
Brian Paul | ff85bcd | 2015-08-07 15:21:46 -0600 | [diff] [blame] | 172 | if (ret != PIPE_OK) { |
Brian Paul | 4f3974d | 2017-06-22 14:45:07 -0600 | [diff] [blame] | 173 | return ret; |
Brian Paul | ff85bcd | 2015-08-07 15:21:46 -0600 | [diff] [blame] | 174 | } |
Brian Paul | 4f3974d | 2017-06-22 14:45:07 -0600 | [diff] [blame] | 175 | |
| 176 | svga->current_so = streamout; |
Brian Paul | ff85bcd | 2015-08-07 15:21:46 -0600 | [diff] [blame] | 177 | } |
| 178 | |
Brian Paul | 4f3974d | 2017-06-22 14:45:07 -0600 | [diff] [blame] | 179 | return PIPE_OK; |
Brian Paul | ff85bcd | 2015-08-07 15:21:46 -0600 | [diff] [blame] | 180 | } |
| 181 | |
| 182 | void |
| 183 | svga_delete_stream_output(struct svga_context *svga, |
| 184 | struct svga_stream_output *streamout) |
| 185 | { |
| 186 | enum pipe_error ret; |
| 187 | |
| 188 | SVGA_DBG(DEBUG_STREAMOUT, "%s streamout=0x%x\n", __FUNCTION__, streamout); |
| 189 | |
| 190 | assert(svga_have_vgpu10(svga)); |
| 191 | assert(streamout != NULL); |
| 192 | |
| 193 | ret = SVGA3D_vgpu10_DestroyStreamOutput(svga->swc, streamout->id); |
| 194 | if (ret != PIPE_OK) { |
| 195 | svga_context_flush(svga, NULL); |
| 196 | ret = SVGA3D_vgpu10_DestroyStreamOutput(svga->swc, streamout->id); |
| 197 | } |
| 198 | |
| 199 | /* Release the ID */ |
| 200 | util_bitmask_clear(svga->stream_output_id_bm, streamout->id); |
| 201 | |
| 202 | /* Free streamout structure */ |
| 203 | FREE(streamout); |
| 204 | } |
| 205 | |
| 206 | static struct pipe_stream_output_target * |
| 207 | svga_create_stream_output_target(struct pipe_context *pipe, |
| 208 | struct pipe_resource *buffer, |
| 209 | unsigned buffer_offset, |
| 210 | unsigned buffer_size) |
| 211 | { |
| 212 | struct svga_context *svga = svga_context(pipe); |
| 213 | struct svga_stream_output_target *sot; |
| 214 | |
| 215 | SVGA_DBG(DEBUG_STREAMOUT, "%s offset=%d size=%d\n", __FUNCTION__, |
| 216 | buffer_offset, buffer_size); |
| 217 | |
| 218 | assert(svga_have_vgpu10(svga)); |
| 219 | (void) svga; |
| 220 | |
| 221 | sot = CALLOC_STRUCT(svga_stream_output_target); |
| 222 | if (!sot) |
| 223 | return NULL; |
| 224 | |
| 225 | pipe_reference_init(&sot->base.reference, 1); |
| 226 | pipe_resource_reference(&sot->base.buffer, buffer); |
| 227 | sot->base.context = pipe; |
| 228 | sot->base.buffer = buffer; |
| 229 | sot->base.buffer_offset = buffer_offset; |
| 230 | sot->base.buffer_size = buffer_size; |
| 231 | |
| 232 | return &sot->base; |
| 233 | } |
| 234 | |
| 235 | static void |
| 236 | svga_destroy_stream_output_target(struct pipe_context *pipe, |
| 237 | struct pipe_stream_output_target *target) |
| 238 | { |
| 239 | struct svga_stream_output_target *sot = svga_stream_output_target(target); |
| 240 | |
| 241 | SVGA_DBG(DEBUG_STREAMOUT, "%s\n", __FUNCTION__); |
| 242 | |
| 243 | pipe_resource_reference(&sot->base.buffer, NULL); |
| 244 | FREE(sot); |
| 245 | } |
| 246 | |
| 247 | static void |
| 248 | svga_set_stream_output_targets(struct pipe_context *pipe, |
| 249 | unsigned num_targets, |
| 250 | struct pipe_stream_output_target **targets, |
| 251 | const unsigned *offsets) |
| 252 | { |
| 253 | struct svga_context *svga = svga_context(pipe); |
| 254 | struct SVGA3dSoTarget soBindings[SVGA3D_DX_MAX_SOTARGETS]; |
| 255 | enum pipe_error ret; |
| 256 | unsigned i; |
| 257 | unsigned num_so_targets; |
| 258 | |
| 259 | SVGA_DBG(DEBUG_STREAMOUT, "%s num_targets=%d\n", __FUNCTION__, |
| 260 | num_targets); |
| 261 | |
| 262 | assert(svga_have_vgpu10(svga)); |
| 263 | |
| 264 | /* Mark the streamout buffers as dirty so that we'll issue readbacks |
| 265 | * before mapping. |
| 266 | */ |
| 267 | for (i = 0; i < svga->num_so_targets; i++) { |
| 268 | struct svga_buffer *sbuf = svga_buffer(svga->so_targets[i]->buffer); |
| 269 | sbuf->dirty = TRUE; |
| 270 | } |
| 271 | |
| 272 | assert(num_targets <= SVGA3D_DX_MAX_SOTARGETS); |
| 273 | |
| 274 | for (i = 0; i < num_targets; i++) { |
| 275 | struct svga_stream_output_target *sot |
| 276 | = svga_stream_output_target(targets[i]); |
Brian Paul | ff85bcd | 2015-08-07 15:21:46 -0600 | [diff] [blame] | 277 | unsigned size; |
| 278 | |
Charmaine Lee | b549f5e | 2017-06-26 17:24:15 -0600 | [diff] [blame] | 279 | svga->so_surfaces[i] = svga_buffer_handle(svga, sot->base.buffer, |
| 280 | PIPE_BIND_STREAM_OUTPUT); |
Charmaine Lee | 7abfb0b | 2016-11-15 10:15:46 -0800 | [diff] [blame] | 281 | |
| 282 | assert(svga_buffer(sot->base.buffer)->key.flags |
| 283 | & SVGA3D_SURFACE_BIND_STREAM_OUTPUT); |
| 284 | |
Brian Paul | ff85bcd | 2015-08-07 15:21:46 -0600 | [diff] [blame] | 285 | svga->so_targets[i] = &sot->base; |
| 286 | soBindings[i].offset = sot->base.buffer_offset; |
| 287 | |
| 288 | /* The size cannot extend beyond the end of the buffer. Clamp it. */ |
| 289 | size = MIN2(sot->base.buffer_size, |
| 290 | sot->base.buffer->width0 - sot->base.buffer_offset); |
| 291 | |
| 292 | soBindings[i].sizeInBytes = size; |
| 293 | } |
| 294 | |
| 295 | /* unbind any previously bound stream output buffers */ |
| 296 | for (; i < svga->num_so_targets; i++) { |
| 297 | svga->so_surfaces[i] = NULL; |
| 298 | svga->so_targets[i] = NULL; |
| 299 | } |
| 300 | |
| 301 | num_so_targets = MAX2(svga->num_so_targets, num_targets); |
| 302 | ret = SVGA3D_vgpu10_SetSOTargets(svga->swc, num_so_targets, |
| 303 | soBindings, svga->so_surfaces); |
| 304 | if (ret != PIPE_OK) { |
| 305 | svga_context_flush(svga, NULL); |
| 306 | ret = SVGA3D_vgpu10_SetSOTargets(svga->swc, num_so_targets, |
| 307 | soBindings, svga->so_surfaces); |
| 308 | } |
| 309 | |
| 310 | svga->num_so_targets = num_targets; |
| 311 | } |
| 312 | |
Charmaine Lee | 47856e5 | 2016-01-20 10:35:56 -0800 | [diff] [blame] | 313 | /** |
| 314 | * Rebind stream output target surfaces |
| 315 | */ |
| 316 | enum pipe_error |
| 317 | svga_rebind_stream_output_targets(struct svga_context *svga) |
| 318 | { |
| 319 | struct svga_winsys_context *swc = svga->swc; |
| 320 | enum pipe_error ret; |
| 321 | unsigned i; |
| 322 | |
| 323 | for (i = 0; i < svga->num_so_targets; i++) { |
| 324 | ret = swc->resource_rebind(swc, svga->so_surfaces[i], NULL, SVGA_RELOC_WRITE); |
| 325 | if (ret != PIPE_OK) |
| 326 | return ret; |
| 327 | } |
| 328 | |
| 329 | return PIPE_OK; |
| 330 | } |
| 331 | |
Brian Paul | ff85bcd | 2015-08-07 15:21:46 -0600 | [diff] [blame] | 332 | void |
| 333 | svga_init_stream_output_functions(struct svga_context *svga) |
| 334 | { |
| 335 | svga->pipe.create_stream_output_target = svga_create_stream_output_target; |
| 336 | svga->pipe.stream_output_target_destroy = svga_destroy_stream_output_target; |
| 337 | svga->pipe.set_stream_output_targets = svga_set_stream_output_targets; |
| 338 | } |