1 /**************************************************************************
2  *
3  * Copyright 2012-2021 VMware, Inc.
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sub license, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
17  * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
18  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20  * USE OR OTHER DEALINGS IN THE SOFTWARE.
21  *
22  * The above copyright notice and this permission notice (including the
23  * next paragraph) shall be included in all copies or substantial portions
24  * of the Software.
25  *
26  **************************************************************************/
27 
28 /*
29  * Draw.h --
30  *    Functions that render 3D primitives.
31  */
32 
33 
34 #include "Draw.h"
35 #include "State.h"
36 #include "Shader.h"
37 
38 #include "Debug.h"
39 
40 #include "util/u_draw.h"
41 #include "util/u_memory.h"
42 
43 static unsigned
ClampedUAdd(unsigned a,unsigned b)44 ClampedUAdd(unsigned a,
45             unsigned b)
46 {
47    unsigned c = a + b;
48    if (c < a) {
49       return 0xffffffff;
50    }
51    return c;
52 }
53 
54 
55 /*
56  * We have to resolve the stream output state for empty geometry shaders.
57  * In particular we've remapped the output indices when translating the
58  * shaders so now the register_index variables in the stream output
59  * state are incorrect and we need to remap them back to the correct
60  * state.
61  */
62 static void
ResolveState(Device * pDevice)63 ResolveState(Device *pDevice)
64 {
65    if (pDevice->bound_empty_gs && pDevice->bound_vs &&
66        pDevice->bound_vs->state.tokens) {
67       Shader *gs = pDevice->bound_empty_gs;
68       Shader *vs = pDevice->bound_vs;
69       boolean remapped = FALSE;
70       struct pipe_context *pipe = pDevice->pipe;
71       if (!gs->output_resolved) {
72          for (unsigned i = 0; i < gs->state.stream_output.num_outputs; ++i) {
73             unsigned mapping =
74                ShaderFindOutputMapping(vs, gs->state.stream_output.output[i].register_index);
75             if (mapping != gs->state.stream_output.output[i].register_index) {
76                gs->state.stream_output.output[i].register_index = mapping;
77                remapped = TRUE;
78             }
79          }
80          if (remapped) {
81             pipe->delete_gs_state(pipe, gs->handle);
82             gs->handle = pipe->create_gs_state(pipe, &gs->state);
83          }
84          gs->output_resolved = TRUE;
85       }
86       pipe->bind_gs_state(pipe, gs->handle);
87    }
88 }
89 
90 
91 static struct pipe_resource *
create_null_index_buffer(struct pipe_context * ctx,uint num_indices,unsigned * restart_index,unsigned * index_size,unsigned * ib_offset)92 create_null_index_buffer(struct pipe_context *ctx, uint num_indices,
93                          unsigned *restart_index, unsigned *index_size,
94                          unsigned *ib_offset)
95 {
96    unsigned buf_size = num_indices * sizeof(unsigned);
97    unsigned *buf = (unsigned*)MALLOC(buf_size);
98    struct pipe_resource *ibuf;
99 
100    memset(buf, 0, buf_size);
101 
102    ibuf = pipe_buffer_create_with_data(ctx,
103                                        PIPE_BIND_INDEX_BUFFER,
104                                        PIPE_USAGE_IMMUTABLE,
105                                        buf_size, buf);
106    *index_size = 4;
107    *restart_index = 0xffffffff;
108    *ib_offset = 0;
109 
110    FREE(buf);
111 
112    return ibuf;
113 }
114 
115 /*
116  * ----------------------------------------------------------------------
117  *
118  * Draw --
119  *
120  *    The Draw function draws nonindexed primitives.
121  *
122  * ----------------------------------------------------------------------
123  */
124 
125 void APIENTRY
Draw(D3D10DDI_HDEVICE hDevice,UINT VertexCount,UINT StartVertexLocation)126 Draw(D3D10DDI_HDEVICE hDevice,   // IN
127      UINT VertexCount,           // IN
128      UINT StartVertexLocation)   // IN
129 {
130    LOG_ENTRYPOINT();
131 
132    Device *pDevice = CastDevice(hDevice);
133 
134    ResolveState(pDevice);
135 
136    assert(pDevice->primitive < PIPE_PRIM_MAX);
137    util_draw_arrays(pDevice->pipe,
138                     pDevice->primitive,
139                     StartVertexLocation,
140                     VertexCount);
141 }
142 
143 
144 /*
145  * ----------------------------------------------------------------------
146  *
147  * DrawIndexed --
148  *
149  *    The DrawIndexed function draws indexed primitives.
150  *
151  * ----------------------------------------------------------------------
152  */
153 
154 void APIENTRY
DrawIndexed(D3D10DDI_HDEVICE hDevice,UINT IndexCount,UINT StartIndexLocation,INT BaseVertexLocation)155 DrawIndexed(D3D10DDI_HDEVICE hDevice,  // IN
156             UINT IndexCount,           // IN
157             UINT StartIndexLocation,   // IN
158             INT BaseVertexLocation)    // IN
159 {
160    LOG_ENTRYPOINT();
161 
162    Device *pDevice = CastDevice(hDevice);
163    struct pipe_draw_info info;
164    struct pipe_draw_start_count_bias draw;
165    struct pipe_resource *null_ib = NULL;
166    unsigned restart_index = pDevice->restart_index;
167    unsigned index_size = pDevice->index_size;
168    unsigned ib_offset = pDevice->ib_offset;
169 
170    assert(pDevice->primitive < PIPE_PRIM_MAX);
171 
172    /* XXX I don't think draw still needs this? */
173    if (!pDevice->index_buffer) {
174       null_ib =
175          create_null_index_buffer(pDevice->pipe,
176                                   StartIndexLocation + IndexCount,
177                                   &restart_index, &index_size, &ib_offset);
178    }
179 
180    ResolveState(pDevice);
181 
182    util_draw_init_info(&info);
183    info.index_size = index_size;
184    info.mode = pDevice->primitive;
185    draw.start = ClampedUAdd(StartIndexLocation, ib_offset / index_size);
186    draw.count = IndexCount;
187    info.index.resource = null_ib ? null_ib : pDevice->index_buffer;
188    draw.index_bias = BaseVertexLocation;
189    info.primitive_restart = TRUE;
190    info.restart_index = restart_index;
191 
192    pDevice->pipe->draw_vbo(pDevice->pipe, &info, 0, NULL, &draw, 1);
193 
194    if (null_ib) {
195       pipe_resource_reference(&null_ib, NULL);
196    }
197 }
198 
199 
200 /*
201  * ----------------------------------------------------------------------
202  *
203  * DrawInstanced --
204  *
205  *    The DrawInstanced function draws particular instances
206  *    of nonindexed primitives.
207  *
208  * ----------------------------------------------------------------------
209  */
210 
211 void APIENTRY
DrawInstanced(D3D10DDI_HDEVICE hDevice,UINT VertexCountPerInstance,UINT InstanceCount,UINT StartVertexLocation,UINT StartInstanceLocation)212 DrawInstanced(D3D10DDI_HDEVICE hDevice,      // IN
213               UINT VertexCountPerInstance,   // IN
214               UINT InstanceCount,            // IN
215               UINT StartVertexLocation,      // IN
216               UINT StartInstanceLocation)    // IN
217 {
218    LOG_ENTRYPOINT();
219 
220    Device *pDevice = CastDevice(hDevice);
221 
222    if (!InstanceCount) {
223       return;
224    }
225 
226    ResolveState(pDevice);
227 
228    assert(pDevice->primitive < PIPE_PRIM_MAX);
229    util_draw_arrays_instanced(pDevice->pipe,
230                               pDevice->primitive,
231                               StartVertexLocation,
232                               VertexCountPerInstance,
233                               StartInstanceLocation,
234                               InstanceCount);
235 }
236 
237 
238 /*
239  * ----------------------------------------------------------------------
240  *
241  * DrawIndexedInstanced --
242  *
243  *    The DrawIndexedInstanced function draws particular
244  *    instances of indexed primitives.
245  *
246  * ----------------------------------------------------------------------
247  */
248 
249 void APIENTRY
DrawIndexedInstanced(D3D10DDI_HDEVICE hDevice,UINT IndexCountPerInstance,UINT InstanceCount,UINT StartIndexLocation,INT BaseVertexLocation,UINT StartInstanceLocation)250 DrawIndexedInstanced(D3D10DDI_HDEVICE hDevice,   // IN
251                      UINT IndexCountPerInstance, // IN
252                      UINT InstanceCount,         // IN
253                      UINT StartIndexLocation,    // IN
254                      INT BaseVertexLocation,     // IN
255                      UINT StartInstanceLocation) // IN
256 {
257    LOG_ENTRYPOINT();
258 
259    Device *pDevice = CastDevice(hDevice);
260    struct pipe_draw_info info;
261    struct pipe_draw_start_count_bias draw;
262    struct pipe_resource *null_ib = NULL;
263    unsigned restart_index = pDevice->restart_index;
264    unsigned index_size = pDevice->index_size;
265    unsigned ib_offset = pDevice->ib_offset;
266 
267    assert(pDevice->primitive < PIPE_PRIM_MAX);
268 
269    if (!InstanceCount) {
270       return;
271    }
272 
273    /* XXX I don't think draw still needs this? */
274    if (!pDevice->index_buffer) {
275       null_ib =
276          create_null_index_buffer(pDevice->pipe,
277                                   StartIndexLocation + IndexCountPerInstance,
278                                   &restart_index, &index_size, &ib_offset);
279    }
280 
281    ResolveState(pDevice);
282 
283    util_draw_init_info(&info);
284    info.index_size = index_size;
285    info.mode = pDevice->primitive;
286    draw.start = ClampedUAdd(StartIndexLocation, ib_offset / index_size);
287    draw.count = IndexCountPerInstance;
288    info.index.resource = null_ib ? null_ib : pDevice->index_buffer;
289    draw.index_bias = BaseVertexLocation;
290    info.start_instance = StartInstanceLocation;
291    info.instance_count = InstanceCount;
292    info.primitive_restart = TRUE;
293    info.restart_index = restart_index;
294 
295    pDevice->pipe->draw_vbo(pDevice->pipe, &info, 0, NULL, &draw, 1);
296 
297    if (null_ib) {
298       pipe_resource_reference(&null_ib, NULL);
299    }
300 }
301 
302 
303 /*
304  * ----------------------------------------------------------------------
305  *
306  * DrawAuto --
307  *
308  *    The DrawAuto function works similarly to the Draw function,
309  *    except DrawAuto is used for the special case where vertex
310  *    data is written through the stream-output unit and then
311  *    recycled as a vertex buffer. The driver determines the number
312  *    of primitives, in part, by how much data was written to the
313  *    buffer through stream output.
314  *
315  * ----------------------------------------------------------------------
316  */
317 
318 void APIENTRY
DrawAuto(D3D10DDI_HDEVICE hDevice)319 DrawAuto(D3D10DDI_HDEVICE hDevice)  // IN
320 {
321    LOG_ENTRYPOINT();
322 
323    Device *pDevice = CastDevice(hDevice);
324    struct pipe_draw_info info;
325    struct pipe_draw_indirect_info indirect;
326 
327 
328    if (!pDevice->draw_so_target) {
329       LOG_UNSUPPORTED("DrawAuto without a set source buffer!");
330       return;
331    }
332 
333    assert(pDevice->primitive < PIPE_PRIM_MAX);
334 
335    ResolveState(pDevice);
336 
337    util_draw_init_info(&info);
338    info.mode = pDevice->primitive;
339    memset(&indirect, 0, sizeof indirect);
340    indirect.count_from_stream_output = pDevice->draw_so_target;
341 
342    pDevice->pipe->draw_vbo(pDevice->pipe, &info, 0, &indirect, NULL, 1);
343 }
344