wined3d: Synchronize shader memory accesses after each draw call.
[wine.git] / dlls / wined3d / drawprim.c
blobebccb1d9a8af1188d237904510ad80529e50ced7
1 /*
2 * WINED3D draw functions
4 * Copyright 2002-2004 Jason Edmeades
5 * Copyright 2002-2004 Raphael Junqueira
6 * Copyright 2004 Christian Costa
7 * Copyright 2005 Oliver Stieber
8 * Copyright 2006, 2008 Henri Verbeet
9 * Copyright 2007-2008 Stefan Dösinger for CodeWeavers
10 * Copyright 2009 Henri Verbeet for CodeWeavers
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation; either
15 * version 2.1 of the License, or (at your option) any later version.
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #include "config.h"
28 #include "wine/port.h"
30 #include "wined3d_private.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(d3d_draw);
33 WINE_DECLARE_DEBUG_CHANNEL(d3d_perf);
34 WINE_DECLARE_DEBUG_CHANNEL(d3d);
36 #include <stdio.h>
37 #include <math.h>
39 /* Context activation is done by the caller. */
40 static void draw_primitive_arrays(struct wined3d_context *context, const struct wined3d_state *state,
41 const void *idx_data, unsigned int idx_size, int base_vertex_idx, unsigned int start_idx,
42 unsigned int count, unsigned int start_instance, unsigned int instance_count)
44 const struct wined3d_ffp_attrib_ops *ops = &context->d3d_info->ffp_attrib_ops;
45 GLenum idx_type = idx_size == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT;
46 const struct wined3d_stream_info *si = &context->stream_info;
47 unsigned int instanced_elements[ARRAY_SIZE(si->elements)];
48 const struct wined3d_gl_info *gl_info = context->gl_info;
49 unsigned int instanced_element_count = 0;
50 unsigned int i, j;
52 if (!instance_count)
54 if (!idx_size)
56 gl_info->gl_ops.gl.p_glDrawArrays(state->gl_primitive_type, start_idx, count);
57 checkGLcall("glDrawArrays");
58 return;
61 if (gl_info->supported[ARB_DRAW_ELEMENTS_BASE_VERTEX])
63 GL_EXTCALL(glDrawElementsBaseVertex(state->gl_primitive_type, count, idx_type,
64 (const char *)idx_data + (idx_size * start_idx), base_vertex_idx));
65 checkGLcall("glDrawElementsBaseVertex");
66 return;
69 gl_info->gl_ops.gl.p_glDrawElements(state->gl_primitive_type, count,
70 idx_type, (const char *)idx_data + (idx_size * start_idx));
71 checkGLcall("glDrawElements");
72 return;
75 if (start_instance)
76 FIXME("Start instance (%u) not supported.\n", start_instance);
78 if (gl_info->supported[ARB_INSTANCED_ARRAYS])
80 if (!idx_size)
82 GL_EXTCALL(glDrawArraysInstanced(state->gl_primitive_type, start_idx, count, instance_count));
83 checkGLcall("glDrawArraysInstanced");
84 return;
87 if (gl_info->supported[ARB_DRAW_ELEMENTS_BASE_VERTEX])
89 GL_EXTCALL(glDrawElementsInstancedBaseVertex(state->gl_primitive_type, count, idx_type,
90 (const char *)idx_data + (idx_size * start_idx), instance_count, base_vertex_idx));
91 checkGLcall("glDrawElementsInstancedBaseVertex");
92 return;
95 GL_EXTCALL(glDrawElementsInstanced(state->gl_primitive_type, count, idx_type,
96 (const char *)idx_data + (idx_size * start_idx), instance_count));
97 checkGLcall("glDrawElementsInstanced");
98 return;
101 /* Instancing emulation by mixing immediate mode and arrays. */
103 /* This is a nasty thing. MSDN says no hardware supports this and
104 * applications have to use software vertex processing. We don't support
105 * this for now.
107 * Shouldn't be too hard to support with OpenGL, in theory just call
108 * glDrawArrays() instead of drawElements(). But the stream fequency value
109 * has a different meaning in that situation. */
110 if (!idx_size)
112 FIXME("Non-indexed instanced drawing is not supported\n");
113 return;
116 for (i = 0; i < ARRAY_SIZE(si->elements); ++i)
118 if (!(si->use_map & (1u << i)))
119 continue;
121 if (state->streams[si->elements[i].stream_idx].flags & WINED3DSTREAMSOURCE_INSTANCEDATA)
122 instanced_elements[instanced_element_count++] = i;
125 for (i = 0; i < instance_count; ++i)
127 /* Specify the instanced attributes using immediate mode calls. */
128 for (j = 0; j < instanced_element_count; ++j)
130 const struct wined3d_stream_info_element *element;
131 unsigned int element_idx;
132 const BYTE *ptr;
134 element_idx = instanced_elements[j];
135 element = &si->elements[element_idx];
136 ptr = element->data.addr + element->stride * i;
137 if (element->data.buffer_object)
138 ptr += (ULONG_PTR)wined3d_buffer_load_sysmem(state->streams[element->stream_idx].buffer, context);
139 ops->generic[element->format->emit_idx](element_idx, ptr);
142 if (gl_info->supported[ARB_DRAW_ELEMENTS_BASE_VERTEX])
144 GL_EXTCALL(glDrawElementsBaseVertex(state->gl_primitive_type, count, idx_type,
145 (const char *)idx_data + (idx_size * start_idx), base_vertex_idx));
146 checkGLcall("glDrawElementsBaseVertex");
148 else
150 gl_info->gl_ops.gl.p_glDrawElements(state->gl_primitive_type, count, idx_type,
151 (const char *)idx_data + (idx_size * start_idx));
152 checkGLcall("glDrawElements");
157 static unsigned int get_stride_idx(const void *idx_data, unsigned int idx_size,
158 unsigned int base_vertex_idx, unsigned int start_idx, unsigned int vertex_idx)
160 if (!idx_data)
161 return start_idx + vertex_idx;
162 if (idx_size == 2)
163 return ((const WORD *)idx_data)[start_idx + vertex_idx] + base_vertex_idx;
164 return ((const DWORD *)idx_data)[start_idx + vertex_idx] + base_vertex_idx;
167 /* Context activation is done by the caller. */
168 static void draw_primitive_immediate_mode(struct wined3d_context *context, const struct wined3d_state *state,
169 const struct wined3d_stream_info *si, const void *idx_data, unsigned int idx_size,
170 int base_vertex_idx, unsigned int start_idx, unsigned int vertex_count, unsigned int instance_count)
172 const BYTE *position = NULL, *normal = NULL, *diffuse = NULL, *specular = NULL;
173 const struct wined3d_d3d_info *d3d_info = context->d3d_info;
174 unsigned int coord_idx, stride_idx, texture_idx, vertex_idx;
175 const struct wined3d_gl_info *gl_info = context->gl_info;
176 const struct wined3d_stream_info_element *element;
177 const BYTE *tex_coords[WINED3DDP_MAXTEXCOORD];
178 unsigned int texture_unit, texture_stages;
179 const struct wined3d_ffp_attrib_ops *ops;
180 unsigned int untracked_material_count;
181 unsigned int tex_mask = 0;
182 BOOL specular_fog = FALSE;
183 BOOL ps = use_ps(state);
184 const void *ptr;
186 static unsigned int once;
188 if (!once++)
189 FIXME_(d3d_perf)("Drawing using immediate mode.\n");
190 else
191 WARN_(d3d_perf)("Drawing using immediate mode.\n");
193 if (!idx_size && idx_data)
194 ERR("Non-NULL idx_data with 0 idx_size, this should never happen.\n");
196 if (instance_count)
197 FIXME("Instancing not implemented.\n");
199 /* Immediate mode drawing can't make use of indices in a VBO - get the
200 * data from the index buffer. */
201 if (idx_size)
202 idx_data = wined3d_buffer_load_sysmem(state->index_buffer, context) + state->index_offset;
204 ops = &d3d_info->ffp_attrib_ops;
206 gl_info->gl_ops.gl.p_glBegin(state->gl_primitive_type);
208 if (use_vs(state) || d3d_info->ffp_generic_attributes)
210 for (vertex_idx = 0; vertex_idx < vertex_count; ++vertex_idx)
212 unsigned int use_map = si->use_map;
213 unsigned int element_idx;
215 stride_idx = get_stride_idx(idx_data, idx_size, base_vertex_idx, start_idx, vertex_idx);
216 for (element_idx = MAX_ATTRIBS - 1; use_map; use_map &= ~(1u << element_idx), --element_idx)
218 if (!(use_map & 1u << element_idx))
219 continue;
221 ptr = si->elements[element_idx].data.addr + si->elements[element_idx].stride * stride_idx;
222 ops->generic[si->elements[element_idx].format->emit_idx](element_idx, ptr);
226 gl_info->gl_ops.gl.p_glEnd();
227 return;
230 if (si->use_map & (1u << WINED3D_FFP_POSITION))
231 position = si->elements[WINED3D_FFP_POSITION].data.addr;
233 if (si->use_map & (1u << WINED3D_FFP_NORMAL))
234 normal = si->elements[WINED3D_FFP_NORMAL].data.addr;
235 else
236 gl_info->gl_ops.gl.p_glNormal3f(0.0f, 0.0f, 0.0f);
238 untracked_material_count = context->num_untracked_materials;
239 if (si->use_map & (1u << WINED3D_FFP_DIFFUSE))
241 element = &si->elements[WINED3D_FFP_DIFFUSE];
242 diffuse = element->data.addr;
244 if (untracked_material_count && element->format->id != WINED3DFMT_B8G8R8A8_UNORM)
245 FIXME("Implement diffuse color tracking from %s.\n", debug_d3dformat(element->format->id));
247 else
249 gl_info->gl_ops.gl.p_glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
252 if (si->use_map & (1u << WINED3D_FFP_SPECULAR))
254 element = &si->elements[WINED3D_FFP_SPECULAR];
255 specular = element->data.addr;
257 /* Special case where the fog density is stored in the specular alpha channel. */
258 if (state->render_states[WINED3D_RS_FOGENABLE]
259 && (state->render_states[WINED3D_RS_FOGVERTEXMODE] == WINED3D_FOG_NONE
260 || si->elements[WINED3D_FFP_POSITION].format->id == WINED3DFMT_R32G32B32A32_FLOAT)
261 && state->render_states[WINED3D_RS_FOGTABLEMODE] == WINED3D_FOG_NONE)
263 if (gl_info->supported[EXT_FOG_COORD])
265 if (element->format->id == WINED3DFMT_B8G8R8A8_UNORM)
266 specular_fog = TRUE;
267 else
268 FIXME("Implement fog coordinates from %s.\n", debug_d3dformat(element->format->id));
270 else
272 static unsigned int once;
274 if (!once++)
275 FIXME("Implement fog for transformed vertices in software.\n");
279 else if (gl_info->supported[EXT_SECONDARY_COLOR])
281 GL_EXTCALL(glSecondaryColor3fEXT)(0.0f, 0.0f, 0.0f);
284 texture_stages = d3d_info->limits.ffp_blend_stages;
285 for (texture_idx = 0; texture_idx < texture_stages; ++texture_idx)
287 if (!gl_info->supported[ARB_MULTITEXTURE] && texture_idx > 0)
289 FIXME("Program using multiple concurrent textures which this OpenGL implementation doesn't support.\n");
290 continue;
293 if (!ps && !state->textures[texture_idx])
294 continue;
296 texture_unit = context->tex_unit_map[texture_idx];
297 if (texture_unit == WINED3D_UNMAPPED_STAGE)
298 continue;
300 coord_idx = state->texture_states[texture_idx][WINED3D_TSS_TEXCOORD_INDEX];
301 if (coord_idx > 7)
303 TRACE("Skipping generated coordinates (%#x) for texture %u.\n", coord_idx, texture_idx);
304 continue;
307 if (si->use_map & (1u << (WINED3D_FFP_TEXCOORD0 + coord_idx)))
309 tex_coords[coord_idx] = si->elements[WINED3D_FFP_TEXCOORD0 + coord_idx].data.addr;
310 tex_mask |= (1u << texture_idx);
312 else
314 TRACE("Setting default coordinates for texture %u.\n", texture_idx);
315 if (gl_info->supported[ARB_MULTITEXTURE])
316 GL_EXTCALL(glMultiTexCoord4fARB(GL_TEXTURE0_ARB + texture_unit, 0.0f, 0.0f, 0.0f, 1.0f));
317 else
318 gl_info->gl_ops.gl.p_glTexCoord4f(0.0f, 0.0f, 0.0f, 1.0f);
322 /* Blending data and point sizes are not supported by this function. They
323 * are not supported by the fixed function pipeline at all. A FIXME for
324 * them is printed after decoding the vertex declaration. */
325 for (vertex_idx = 0; vertex_idx < vertex_count; ++vertex_idx)
327 unsigned int tmp_tex_mask;
329 stride_idx = get_stride_idx(idx_data, idx_size, base_vertex_idx, start_idx, vertex_idx);
331 if (normal)
333 ptr = normal + stride_idx * si->elements[WINED3D_FFP_NORMAL].stride;
334 ops->normal[si->elements[WINED3D_FFP_NORMAL].format->emit_idx](ptr);
337 if (diffuse)
339 ptr = diffuse + stride_idx * si->elements[WINED3D_FFP_DIFFUSE].stride;
340 ops->diffuse[si->elements[WINED3D_FFP_DIFFUSE].format->emit_idx](ptr);
342 if (untracked_material_count)
344 struct wined3d_color color;
345 unsigned int i;
347 wined3d_color_from_d3dcolor(&color, *(const DWORD *)ptr);
348 for (i = 0; i < untracked_material_count; ++i)
350 gl_info->gl_ops.gl.p_glMaterialfv(GL_FRONT_AND_BACK, context->untracked_materials[i], &color.r);
355 if (specular)
357 ptr = specular + stride_idx * si->elements[WINED3D_FFP_SPECULAR].stride;
358 ops->specular[si->elements[WINED3D_FFP_SPECULAR].format->emit_idx](ptr);
360 if (specular_fog)
361 GL_EXTCALL(glFogCoordfEXT((float)(*(const DWORD *)ptr >> 24)));
364 tmp_tex_mask = tex_mask;
365 for (texture_idx = 0; tmp_tex_mask; tmp_tex_mask >>= 1, ++texture_idx)
367 if (!(tmp_tex_mask & 1))
368 continue;
370 coord_idx = state->texture_states[texture_idx][WINED3D_TSS_TEXCOORD_INDEX];
371 ptr = tex_coords[coord_idx] + (stride_idx * si->elements[WINED3D_FFP_TEXCOORD0 + coord_idx].stride);
372 ops->texcoord[si->elements[WINED3D_FFP_TEXCOORD0 + coord_idx].format->emit_idx](
373 GL_TEXTURE0_ARB + context->tex_unit_map[texture_idx], ptr);
376 if (position)
378 ptr = position + stride_idx * si->elements[WINED3D_FFP_POSITION].stride;
379 ops->position[si->elements[WINED3D_FFP_POSITION].format->emit_idx](ptr);
383 gl_info->gl_ops.gl.p_glEnd();
384 checkGLcall("glEnd and previous calls");
387 static void remove_vbos(struct wined3d_context *context,
388 const struct wined3d_state *state, struct wined3d_stream_info *s)
390 unsigned int i;
392 for (i = 0; i < ARRAY_SIZE(s->elements); ++i)
394 struct wined3d_stream_info_element *e;
396 if (!(s->use_map & (1u << i)))
397 continue;
399 e = &s->elements[i];
400 if (e->data.buffer_object)
402 struct wined3d_buffer *vb = state->streams[e->stream_idx].buffer;
403 e->data.buffer_object = 0;
404 e->data.addr += (ULONG_PTR)wined3d_buffer_load_sysmem(vb, context);
409 /* Routine common to the draw primitive and draw indexed primitive routines */
410 void draw_primitive(struct wined3d_device *device, const struct wined3d_state *state,
411 int base_vertex_idx, unsigned int start_idx, unsigned int index_count,
412 unsigned int start_instance, unsigned int instance_count, BOOL indexed)
414 const struct wined3d_fb_state *fb = state->fb;
415 const struct wined3d_stream_info *stream_info;
416 struct wined3d_event_query *ib_query = NULL;
417 struct wined3d_stream_info si_emulated;
418 struct wined3d_rendertarget_view *dsv;
419 const struct wined3d_gl_info *gl_info;
420 struct wined3d_context *context;
421 unsigned int i, idx_size = 0;
422 const void *idx_data = NULL;
423 BOOL emulation = FALSE;
425 if (!index_count)
426 return;
428 context = context_acquire(device, wined3d_rendertarget_view_get_surface(fb->render_targets[0]));
429 if (!context->valid)
431 context_release(context);
432 WARN("Invalid context, skipping draw.\n");
433 return;
435 gl_info = context->gl_info;
437 for (i = 0; i < gl_info->limits.buffers; ++i)
439 struct wined3d_rendertarget_view *rtv = fb->render_targets[i];
440 struct wined3d_texture *rt;
442 if (!rtv || rtv->format->id == WINED3DFMT_NULL)
443 continue;
445 rt = wined3d_texture_from_resource(rtv->resource);
446 if (state->render_states[WINED3D_RS_COLORWRITEENABLE])
448 wined3d_texture_load_location(rt, rtv->sub_resource_idx, context, rtv->resource->draw_binding);
449 wined3d_texture_invalidate_location(rt, rtv->sub_resource_idx, ~rtv->resource->draw_binding);
451 else
453 wined3d_texture_prepare_location(rt, rtv->sub_resource_idx, context, rtv->resource->draw_binding);
457 if ((dsv = fb->depth_stencil))
459 /* Note that this depends on the context_acquire() call above to set
460 * context->render_offscreen properly. We don't currently take the
461 * Z-compare function into account, but we could skip loading the
462 * depthstencil for D3DCMP_NEVER and D3DCMP_ALWAYS as well. Also note
463 * that we never copy the stencil data.*/
464 DWORD location = context->render_offscreen ? dsv->resource->draw_binding : WINED3D_LOCATION_DRAWABLE;
465 struct wined3d_surface *ds = wined3d_rendertarget_view_get_surface(dsv);
467 if (state->render_states[WINED3D_RS_ZWRITEENABLE] || state->render_states[WINED3D_RS_ZENABLE])
469 RECT current_rect, draw_rect, r;
471 if (!context->render_offscreen && ds != device->onscreen_depth_stencil)
472 device_switch_onscreen_ds(device, context, ds);
474 if (surface_get_sub_resource(ds)->locations & location)
475 SetRect(&current_rect, 0, 0, ds->ds_current_size.cx, ds->ds_current_size.cy);
476 else
477 SetRectEmpty(&current_rect);
479 wined3d_get_draw_rect(state, &draw_rect);
481 IntersectRect(&r, &draw_rect, &current_rect);
482 if (!EqualRect(&r, &draw_rect))
483 wined3d_texture_load_location(ds->container, dsv->sub_resource_idx, context, location);
484 else
485 wined3d_texture_prepare_location(ds->container, dsv->sub_resource_idx, context, location);
487 else
488 wined3d_texture_prepare_location(ds->container, dsv->sub_resource_idx, context, location);
491 if (!context_apply_draw_state(context, device, state))
493 context_release(context);
494 WARN("Unable to apply draw state, skipping draw.\n");
495 return;
498 if (fb->depth_stencil && state->render_states[WINED3D_RS_ZWRITEENABLE])
500 struct wined3d_surface *ds = wined3d_rendertarget_view_get_surface(fb->depth_stencil);
501 DWORD location = context->render_offscreen ? ds->container->resource.draw_binding : WINED3D_LOCATION_DRAWABLE;
503 surface_modify_ds_location(ds, location, ds->ds_current_size.cx, ds->ds_current_size.cy);
506 if ((!gl_info->supported[WINED3D_GL_VERSION_2_0]
507 || !gl_info->supported[NV_POINT_SPRITE])
508 && context->render_offscreen
509 && state->render_states[WINED3D_RS_POINTSPRITEENABLE]
510 && state->gl_primitive_type == GL_POINTS)
512 FIXME("Point sprite coordinate origin switching not supported.\n");
515 stream_info = &context->stream_info;
516 if (context->instance_count)
517 instance_count = context->instance_count;
519 if (indexed)
521 struct wined3d_buffer *index_buffer = state->index_buffer;
522 if (!index_buffer->buffer_object || !stream_info->all_vbo)
524 idx_data = index_buffer->resource.heap_memory;
526 else
528 ib_query = index_buffer->query;
529 idx_data = NULL;
531 idx_data = (const BYTE *)idx_data + state->index_offset;
533 if (state->index_format == WINED3DFMT_R16_UINT)
534 idx_size = 2;
535 else
536 idx_size = 4;
539 if (!use_vs(state))
541 if (!stream_info->position_transformed && context->num_untracked_materials
542 && state->render_states[WINED3D_RS_LIGHTING])
544 static BOOL warned;
546 if (!warned++)
547 FIXME("Using software emulation because not all material properties could be tracked.\n");
548 else
549 WARN_(d3d_perf)("Using software emulation because not all material properties could be tracked.\n");
550 emulation = TRUE;
552 else if (context->fog_coord && state->render_states[WINED3D_RS_FOGENABLE])
554 static BOOL warned;
556 /* Either write a pipeline replacement shader or convert the
557 * specular alpha from unsigned byte to a float in the vertex
558 * buffer. */
559 if (!warned++)
560 FIXME("Using software emulation because manual fog coordinates are provided.\n");
561 else
562 WARN_(d3d_perf)("Using software emulation because manual fog coordinates are provided.\n");
563 emulation = TRUE;
566 if (emulation)
568 si_emulated = context->stream_info;
569 remove_vbos(context, state, &si_emulated);
570 stream_info = &si_emulated;
574 if (context->use_immediate_mode_draw || emulation)
575 draw_primitive_immediate_mode(context, state, stream_info, idx_data,
576 idx_size, base_vertex_idx, start_idx, index_count, instance_count);
577 else
578 draw_primitive_arrays(context, state, idx_data, idx_size, base_vertex_idx,
579 start_idx, index_count, start_instance, instance_count);
581 if (context->uses_uavs)
583 GL_EXTCALL(glMemoryBarrier(GL_ALL_BARRIER_BITS));
584 checkGLcall("glMemoryBarrier");
587 if (ib_query)
588 wined3d_event_query_issue(ib_query, device);
589 for (i = 0; i < context->num_buffer_queries; ++i)
590 wined3d_event_query_issue(context->buffer_queries[i], device);
592 if (wined3d_settings.strict_draw_ordering)
593 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
595 context_release(context);
597 TRACE("Done all gl drawing.\n");