d3d8/tests: Add a system memory miptree layout test.
[wine.git] / dlls / wined3d / drawprim.c
blobd83b49496839a4bd38874a710e324b248408a044
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);
35 #include <stdio.h>
36 #include <math.h>
38 /* Context activation is done by the caller. */
39 static void draw_primitive_arrays(struct wined3d_context *context, const struct wined3d_state *state,
40 unsigned int count, const void *idx_data, unsigned int idx_size, unsigned int start_idx,
41 unsigned int start_instance, unsigned int instance_count)
43 const struct wined3d_ffp_attrib_ops *ops = &context->d3d_info->ffp_attrib_ops;
44 GLenum idx_type = idx_size == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT;
45 const struct wined3d_stream_info *si = &context->stream_info;
46 unsigned int instanced_elements[ARRAY_SIZE(si->elements)];
47 const struct wined3d_gl_info *gl_info = context->gl_info;
48 unsigned int instanced_element_count = 0;
49 unsigned int i, j;
51 if (!instance_count)
53 if (!idx_size)
55 gl_info->gl_ops.gl.p_glDrawArrays(state->gl_primitive_type, start_idx, count);
56 checkGLcall("glDrawArrays");
57 return;
60 if (gl_info->supported[ARB_DRAW_ELEMENTS_BASE_VERTEX])
62 GL_EXTCALL(glDrawElementsBaseVertex(state->gl_primitive_type, count, idx_type,
63 (const char *)idx_data + (idx_size * start_idx), state->base_vertex_index));
64 checkGLcall("glDrawElementsBaseVertex");
65 return;
68 gl_info->gl_ops.gl.p_glDrawElements(state->gl_primitive_type, count,
69 idx_type, (const char *)idx_data + (idx_size * start_idx));
70 checkGLcall("glDrawElements");
71 return;
74 if (start_instance)
75 FIXME("Start instance (%u) not supported.\n", start_instance);
77 if (gl_info->supported[ARB_INSTANCED_ARRAYS])
79 if (!idx_size)
81 GL_EXTCALL(glDrawArraysInstanced(state->gl_primitive_type, start_idx, count, instance_count));
82 checkGLcall("glDrawArraysInstanced");
83 return;
86 if (gl_info->supported[ARB_DRAW_ELEMENTS_BASE_VERTEX])
88 GL_EXTCALL(glDrawElementsInstancedBaseVertex(state->gl_primitive_type, count, idx_type,
89 (const char *)idx_data + (idx_size * start_idx), instance_count, state->base_vertex_index));
90 checkGLcall("glDrawElementsInstancedBaseVertex");
91 return;
94 GL_EXTCALL(glDrawElementsInstanced(state->gl_primitive_type, count, idx_type,
95 (const char *)idx_data + (idx_size * start_idx), instance_count));
96 checkGLcall("glDrawElementsInstanced");
97 return;
100 /* Instancing emulation by mixing immediate mode and arrays. */
102 /* This is a nasty thing. MSDN says no hardware supports this and
103 * applications have to use software vertex processing. We don't support
104 * this for now.
106 * Shouldn't be too hard to support with OpenGL, in theory just call
107 * glDrawArrays() instead of drawElements(). But the stream fequency value
108 * has a different meaning in that situation. */
109 if (!idx_size)
111 FIXME("Non-indexed instanced drawing is not supported\n");
112 return;
115 for (i = 0; i < ARRAY_SIZE(si->elements); ++i)
117 if (!(si->use_map & (1u << i)))
118 continue;
120 if (state->streams[si->elements[i].stream_idx].flags & WINED3DSTREAMSOURCE_INSTANCEDATA)
121 instanced_elements[instanced_element_count++] = i;
124 for (i = 0; i < instance_count; ++i)
126 /* Specify the instanced attributes using immediate mode calls. */
127 for (j = 0; j < instanced_element_count; ++j)
129 const struct wined3d_stream_info_element *element;
130 unsigned int element_idx;
131 const BYTE *ptr;
133 element_idx = instanced_elements[j];
134 element = &si->elements[element_idx];
135 ptr = element->data.addr + element->stride * i;
136 if (element->data.buffer_object)
137 ptr += (ULONG_PTR)buffer_get_sysmem(state->streams[element->stream_idx].buffer, context);
138 ops->generic[element->format->emit_idx](element_idx, ptr);
141 if (gl_info->supported[ARB_DRAW_ELEMENTS_BASE_VERTEX])
143 GL_EXTCALL(glDrawElementsBaseVertex(state->gl_primitive_type, count, idx_type,
144 (const char *)idx_data + (idx_size * start_idx), state->base_vertex_index));
145 checkGLcall("glDrawElementsBaseVertex");
147 else
149 gl_info->gl_ops.gl.p_glDrawElements(state->gl_primitive_type, count, idx_type,
150 (const char *)idx_data + (idx_size * start_idx));
151 checkGLcall("glDrawElements");
156 static unsigned int get_stride_idx(const void *idx_data, unsigned int idx_size,
157 unsigned int base_vertex_idx, unsigned int start_idx, unsigned int vertex_idx)
159 if (!idx_data)
160 return start_idx + vertex_idx;
161 if (idx_size == 2)
162 return ((const WORD *)idx_data)[start_idx + vertex_idx] + base_vertex_idx;
163 return ((const DWORD *)idx_data)[start_idx + vertex_idx] + base_vertex_idx;
166 /* Context activation is done by the caller. */
167 static void draw_primitive_immediate_mode(struct wined3d_context *context, const struct wined3d_state *state,
168 const struct wined3d_stream_info *si, unsigned int vertex_count, const void *idx_data,
169 unsigned int idx_size, unsigned int start_idx, unsigned int instance_count)
171 const BYTE *position = NULL, *normal = NULL, *diffuse = NULL, *specular = NULL;
172 const struct wined3d_d3d_info *d3d_info = context->d3d_info;
173 unsigned int coord_idx, stride_idx, texture_idx, vertex_idx;
174 const struct wined3d_gl_info *gl_info = context->gl_info;
175 const struct wined3d_stream_info_element *element;
176 const BYTE *tex_coords[WINED3DDP_MAXTEXCOORD];
177 unsigned int texture_unit, texture_stages;
178 const struct wined3d_ffp_attrib_ops *ops;
179 unsigned int untracked_material_count;
180 unsigned int tex_mask = 0;
181 BOOL specular_fog = FALSE;
182 BOOL ps = use_ps(state);
183 const void *ptr;
185 static unsigned int once;
187 if (!once++)
188 FIXME_(d3d_perf)("Drawing using immediate mode.\n");
189 else
190 WARN_(d3d_perf)("Drawing using immediate mode.\n");
192 if (!idx_size && idx_data)
193 ERR("Non-NULL idx_data with 0 idx_size, this should never happen.\n");
195 if (instance_count)
196 FIXME("Instancing not implemented.\n");
198 /* Immediate mode drawing can't make use of indices in a vbo - get the
199 * data from the index buffer. If the index buffer has no vbo (not
200 * supported or other reason), or with user pointer drawing idx_data
201 * will be non-NULL. */
202 if (idx_size && !idx_data)
203 idx_data = buffer_get_sysmem(state->index_buffer, context);
205 ops = &d3d_info->ffp_attrib_ops;
207 gl_info->gl_ops.gl.p_glBegin(state->gl_primitive_type);
209 if (use_vs(state) || d3d_info->ffp_generic_attributes)
211 for (vertex_idx = 0; vertex_idx < vertex_count; ++vertex_idx)
213 unsigned int use_map = si->use_map;
214 unsigned int element_idx;
216 stride_idx = get_stride_idx(idx_data, idx_size, state->base_vertex_index, start_idx, vertex_idx);
217 for (element_idx = 0; use_map; use_map >>= 1, ++element_idx)
219 if (!(use_map & 1u))
220 continue;
222 ptr = si->elements[element_idx].data.addr + si->elements[element_idx].stride * stride_idx;
223 ops->generic[si->elements[element_idx].format->emit_idx](element_idx, ptr);
227 gl_info->gl_ops.gl.p_glEnd();
228 return;
231 if (si->use_map & (1u << WINED3D_FFP_POSITION))
232 position = si->elements[WINED3D_FFP_POSITION].data.addr;
234 if (si->use_map & (1u << WINED3D_FFP_NORMAL))
235 normal = si->elements[WINED3D_FFP_NORMAL].data.addr;
236 else
237 gl_info->gl_ops.gl.p_glNormal3f(0.0f, 0.0f, 0.0f);
239 untracked_material_count = context->num_untracked_materials;
240 if (si->use_map & (1u << WINED3D_FFP_DIFFUSE))
242 element = &si->elements[WINED3D_FFP_DIFFUSE];
243 diffuse = element->data.addr;
245 if (untracked_material_count && element->format->id != WINED3DFMT_B8G8R8A8_UNORM)
246 FIXME("Implement diffuse color tracking from %s.\n", debug_d3dformat(element->format->id));
248 else
250 gl_info->gl_ops.gl.p_glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
253 if (si->use_map & (1u << WINED3D_FFP_SPECULAR))
255 element = &si->elements[WINED3D_FFP_SPECULAR];
256 specular = element->data.addr;
258 /* Special case where the fog density is stored in the specular alpha channel. */
259 if (state->render_states[WINED3D_RS_FOGENABLE]
260 && (state->render_states[WINED3D_RS_FOGVERTEXMODE] == WINED3D_FOG_NONE
261 || si->elements[WINED3D_FFP_POSITION].format->id == WINED3DFMT_R32G32B32A32_FLOAT)
262 && state->render_states[WINED3D_RS_FOGTABLEMODE] == WINED3D_FOG_NONE)
264 if (gl_info->supported[EXT_FOG_COORD])
266 if (element->format->id == WINED3DFMT_B8G8R8A8_UNORM)
267 specular_fog = TRUE;
268 else
269 FIXME("Implement fog coordinates from %s.\n", debug_d3dformat(element->format->id));
271 else
273 static unsigned int once;
275 if (!once++)
276 FIXME("Implement fog for transformed vertices in software.\n");
280 else if (gl_info->supported[EXT_SECONDARY_COLOR])
282 GL_EXTCALL(glSecondaryColor3fEXT)(0.0f, 0.0f, 0.0f);
285 texture_stages = d3d_info->limits.ffp_blend_stages;
286 for (texture_idx = 0; texture_idx < texture_stages; ++texture_idx)
288 if (!gl_info->supported[ARB_MULTITEXTURE] && texture_idx > 0)
290 FIXME("Program using multiple concurrent textures which this OpenGL implementation doesn't support.\n");
291 continue;
294 if (!ps && !state->textures[texture_idx])
295 continue;
297 texture_unit = context->tex_unit_map[texture_idx];
298 if (texture_unit == WINED3D_UNMAPPED_STAGE)
299 continue;
301 coord_idx = state->texture_states[texture_idx][WINED3D_TSS_TEXCOORD_INDEX];
302 if (coord_idx > 7)
304 TRACE("Skipping generated coordinates (%#x) for texture %u.\n", coord_idx, texture_idx);
305 continue;
308 if (si->use_map & (1u << (WINED3D_FFP_TEXCOORD0 + coord_idx)))
310 tex_coords[coord_idx] = si->elements[WINED3D_FFP_TEXCOORD0 + coord_idx].data.addr;
311 tex_mask |= (1u << texture_idx);
313 else
315 TRACE("Setting default coordinates for texture %u.\n", texture_idx);
316 if (gl_info->supported[ARB_MULTITEXTURE])
317 GL_EXTCALL(glMultiTexCoord4fARB(GL_TEXTURE0_ARB + texture_unit, 0.0f, 0.0f, 0.0f, 1.0f));
318 else
319 gl_info->gl_ops.gl.p_glTexCoord4f(0.0f, 0.0f, 0.0f, 1.0f);
323 /* Blending data and point sizes are not supported by this function. They
324 * are not supported by the fixed function pipeline at all. A FIXME for
325 * them is printed after decoding the vertex declaration. */
326 for (vertex_idx = 0; vertex_idx < vertex_count; ++vertex_idx)
328 unsigned int tmp_tex_mask;
330 stride_idx = get_stride_idx(idx_data, idx_size, state->base_vertex_index, start_idx, vertex_idx);
332 if (position)
334 ptr = position + stride_idx * si->elements[WINED3D_FFP_POSITION].stride;
335 ops->position[si->elements[WINED3D_FFP_POSITION].format->emit_idx](ptr);
338 if (normal)
340 ptr = normal + stride_idx * si->elements[WINED3D_FFP_NORMAL].stride;
341 ops->normal[si->elements[WINED3D_FFP_NORMAL].format->emit_idx](ptr);
344 if (diffuse)
346 ptr = diffuse + stride_idx * si->elements[WINED3D_FFP_DIFFUSE].stride;
347 ops->diffuse[si->elements[WINED3D_FFP_DIFFUSE].format->emit_idx](ptr);
349 if (untracked_material_count)
351 struct wined3d_color color;
352 unsigned int i;
354 wined3d_color_from_d3dcolor(&color, *(const DWORD *)ptr);
355 for (i = 0; i < untracked_material_count; ++i)
357 gl_info->gl_ops.gl.p_glMaterialfv(GL_FRONT_AND_BACK, context->untracked_materials[i], &color.r);
362 if (specular)
364 ptr = specular + stride_idx * si->elements[WINED3D_FFP_SPECULAR].stride;
365 ops->specular[si->elements[WINED3D_FFP_SPECULAR].format->emit_idx](ptr);
367 if (specular_fog)
368 GL_EXTCALL(glFogCoordfEXT((float)(*(const DWORD *)ptr >> 24)));
371 tmp_tex_mask = tex_mask;
372 for (texture_idx = 0; tmp_tex_mask; tmp_tex_mask >>= 1, ++texture_idx)
374 if (!(tmp_tex_mask & 1))
375 continue;
377 coord_idx = state->texture_states[texture_idx][WINED3D_TSS_TEXCOORD_INDEX];
378 ptr = tex_coords[coord_idx] + (stride_idx * si->elements[WINED3D_FFP_TEXCOORD0 + coord_idx].stride);
379 ops->texcoord[si->elements[WINED3D_FFP_TEXCOORD0 + coord_idx].format->emit_idx](
380 GL_TEXTURE0_ARB + context->tex_unit_map[texture_idx], ptr);
384 gl_info->gl_ops.gl.p_glEnd();
385 checkGLcall("glEnd and previous calls");
388 static void remove_vbos(struct wined3d_context *context,
389 const struct wined3d_state *state, struct wined3d_stream_info *s)
391 unsigned int i;
393 for (i = 0; i < (sizeof(s->elements) / sizeof(*s->elements)); ++i)
395 struct wined3d_stream_info_element *e;
397 if (!(s->use_map & (1u << i))) 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 = (BYTE *)((ULONG_PTR)e->data.addr + (ULONG_PTR)buffer_get_sysmem(vb, context));
409 /* Routine common to the draw primitive and draw indexed primitive routines */
410 void draw_primitive(struct wined3d_device *device, UINT start_idx, UINT index_count,
411 UINT start_instance, UINT instance_count, BOOL indexed)
413 const struct wined3d_state *state = &device->state;
414 const struct wined3d_stream_info *stream_info;
415 struct wined3d_event_query *ib_query = NULL;
416 struct wined3d_stream_info si_emulated;
417 const struct wined3d_gl_info *gl_info;
418 struct wined3d_context *context;
419 BOOL emulation = FALSE;
420 const void *idx_data = NULL;
421 UINT idx_size = 0;
422 unsigned int i;
424 if (!index_count) return;
426 context = context_acquire(device, wined3d_rendertarget_view_get_surface(device->fb.render_targets[0]));
427 if (!context->valid)
429 context_release(context);
430 WARN("Invalid context, skipping draw.\n");
431 return;
433 gl_info = context->gl_info;
435 for (i = 0; i < device->adapter->gl_info.limits.buffers; ++i)
437 struct wined3d_rendertarget_view *rtv = device->fb.render_targets[i];
438 struct wined3d_surface *target = wined3d_rendertarget_view_get_surface(rtv);
440 if (target && rtv->format->id != WINED3DFMT_NULL)
442 if (state->render_states[WINED3D_RS_COLORWRITEENABLE])
444 surface_load_location(target, context, rtv->resource->draw_binding);
445 wined3d_texture_invalidate_location(target->container,
446 rtv->sub_resource_idx, ~rtv->resource->draw_binding);
448 else
450 wined3d_surface_prepare(target, context, rtv->resource->draw_binding);
455 if (device->fb.depth_stencil)
457 /* Note that this depends on the context_acquire() call above to set
458 * context->render_offscreen properly. We don't currently take the
459 * Z-compare function into account, but we could skip loading the
460 * depthstencil for D3DCMP_NEVER and D3DCMP_ALWAYS as well. Also note
461 * that we never copy the stencil data.*/
462 DWORD location = context->render_offscreen ? device->fb.depth_stencil->resource->draw_binding
463 : WINED3D_LOCATION_DRAWABLE;
464 struct wined3d_surface *ds = wined3d_rendertarget_view_get_surface(device->fb.depth_stencil);
466 if (state->render_states[WINED3D_RS_ZWRITEENABLE] || state->render_states[WINED3D_RS_ZENABLE])
468 RECT current_rect, draw_rect, r;
470 if (!context->render_offscreen && ds != device->onscreen_depth_stencil)
471 device_switch_onscreen_ds(device, context, ds);
473 if (surface_get_sub_resource(ds)->locations & location)
474 SetRect(&current_rect, 0, 0, ds->ds_current_size.cx, ds->ds_current_size.cy);
475 else
476 SetRectEmpty(&current_rect);
478 wined3d_get_draw_rect(state, &draw_rect);
480 IntersectRect(&r, &draw_rect, &current_rect);
481 if (!EqualRect(&r, &draw_rect))
482 surface_load_location(ds, context, location);
483 else
484 wined3d_surface_prepare(ds, context, location);
486 else
487 wined3d_surface_prepare(ds, context, location);
490 if (!context_apply_draw_state(context, device))
492 context_release(context);
493 WARN("Unable to apply draw state, skipping draw.\n");
494 return;
497 if (device->fb.depth_stencil && state->render_states[WINED3D_RS_ZWRITEENABLE])
499 struct wined3d_surface *ds = wined3d_rendertarget_view_get_surface(device->fb.depth_stencil);
500 DWORD location = context->render_offscreen ? ds->container->resource.draw_binding : WINED3D_LOCATION_DRAWABLE;
502 surface_modify_ds_location(ds, location, ds->ds_current_size.cx, ds->ds_current_size.cy);
505 if ((!gl_info->supported[WINED3D_GL_VERSION_2_0]
506 || !gl_info->supported[NV_POINT_SPRITE])
507 && context->render_offscreen
508 && state->render_states[WINED3D_RS_POINTSPRITEENABLE]
509 && state->gl_primitive_type == GL_POINTS)
511 FIXME("Point sprite coordinate origin switching not supported.\n");
514 stream_info = &context->stream_info;
515 if (context->instance_count)
516 instance_count = context->instance_count;
518 if (indexed)
520 struct wined3d_buffer *index_buffer = state->index_buffer;
521 if (!index_buffer->buffer_object || !stream_info->all_vbo)
522 idx_data = index_buffer->resource.heap_memory;
523 else
525 ib_query = index_buffer->query;
526 idx_data = NULL;
529 if (state->index_format == WINED3DFMT_R16_UINT)
530 idx_size = 2;
531 else
532 idx_size = 4;
535 if (!use_vs(state))
537 if (!stream_info->position_transformed && context->num_untracked_materials
538 && state->render_states[WINED3D_RS_LIGHTING])
540 static BOOL warned;
542 if (!warned++)
543 FIXME("Using software emulation because not all material properties could be tracked.\n");
544 else
545 WARN_(d3d_perf)("Using software emulation because not all material properties could be tracked.\n");
546 emulation = TRUE;
548 else if (context->fog_coord && state->render_states[WINED3D_RS_FOGENABLE])
550 static BOOL warned;
552 /* Either write a pipeline replacement shader or convert the
553 * specular alpha from unsigned byte to a float in the vertex
554 * buffer. */
555 if (!warned++)
556 FIXME("Using software emulation because manual fog coordinates are provided.\n");
557 else
558 WARN_(d3d_perf)("Using software emulation because manual fog coordinates are provided.\n");
559 emulation = TRUE;
562 if (emulation)
564 si_emulated = context->stream_info;
565 remove_vbos(context, state, &si_emulated);
566 stream_info = &si_emulated;
570 if (context->use_immediate_mode_draw || emulation)
571 draw_primitive_immediate_mode(context, state, stream_info, index_count,
572 idx_data, idx_size, start_idx, instance_count);
573 else
574 draw_primitive_arrays(context, state, index_count, idx_data,
575 idx_size, start_idx, start_instance, instance_count);
577 if (ib_query)
578 wined3d_event_query_issue(ib_query, device);
579 for (i = 0; i < context->num_buffer_queries; ++i)
581 wined3d_event_query_issue(context->buffer_queries[i], device);
584 if (wined3d_settings.strict_draw_ordering)
585 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
587 context_release(context);
589 TRACE("Done all gl drawing\n");