| /* |
| * WINED3D draw functions |
| * |
| * Copyright 2002-2004 Jason Edmeades |
| * Copyright 2002-2004 Raphael Junqueira |
| * Copyright 2004 Christian Costa |
| * Copyright 2005 Oliver Stieber |
| * Copyright 2006, 2008 Henri Verbeet |
| * Copyright 2007-2008 Stefan Dösinger for CodeWeavers |
| * Copyright 2009 Henri Verbeet for CodeWeavers |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include "config.h" |
| #include "wine/port.h" |
| |
| #include "wined3d_private.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(d3d_draw); |
| WINE_DECLARE_DEBUG_CHANNEL(d3d_perf); |
| WINE_DECLARE_DEBUG_CHANNEL(d3d); |
| |
| #include <stdio.h> |
| #include <math.h> |
| |
| /* Context activation is done by the caller. */ |
| static void draw_primitive_arrays(struct wined3d_context *context, const struct wined3d_state *state, |
| const void *idx_data, unsigned int idx_size, int base_vertex_idx, unsigned int start_idx, |
| unsigned int count, unsigned int start_instance, unsigned int instance_count) |
| { |
| const struct wined3d_ffp_attrib_ops *ops = &context->d3d_info->ffp_attrib_ops; |
| GLenum idx_type = idx_size == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT; |
| const struct wined3d_stream_info *si = &context->stream_info; |
| unsigned int instanced_elements[ARRAY_SIZE(si->elements)]; |
| const struct wined3d_gl_info *gl_info = context->gl_info; |
| unsigned int instanced_element_count = 0; |
| unsigned int i, j; |
| |
| if (!instance_count) |
| { |
| if (!idx_size) |
| { |
| gl_info->gl_ops.gl.p_glDrawArrays(state->gl_primitive_type, start_idx, count); |
| checkGLcall("glDrawArrays"); |
| return; |
| } |
| |
| if (gl_info->supported[ARB_DRAW_ELEMENTS_BASE_VERTEX]) |
| { |
| GL_EXTCALL(glDrawElementsBaseVertex(state->gl_primitive_type, count, idx_type, |
| (const char *)idx_data + (idx_size * start_idx), base_vertex_idx)); |
| checkGLcall("glDrawElementsBaseVertex"); |
| return; |
| } |
| |
| gl_info->gl_ops.gl.p_glDrawElements(state->gl_primitive_type, count, |
| idx_type, (const char *)idx_data + (idx_size * start_idx)); |
| checkGLcall("glDrawElements"); |
| return; |
| } |
| |
| if (start_instance) |
| FIXME("Start instance (%u) not supported.\n", start_instance); |
| |
| if (gl_info->supported[ARB_INSTANCED_ARRAYS]) |
| { |
| if (!idx_size) |
| { |
| GL_EXTCALL(glDrawArraysInstanced(state->gl_primitive_type, start_idx, count, instance_count)); |
| checkGLcall("glDrawArraysInstanced"); |
| return; |
| } |
| |
| if (gl_info->supported[ARB_DRAW_ELEMENTS_BASE_VERTEX]) |
| { |
| GL_EXTCALL(glDrawElementsInstancedBaseVertex(state->gl_primitive_type, count, idx_type, |
| (const char *)idx_data + (idx_size * start_idx), instance_count, base_vertex_idx)); |
| checkGLcall("glDrawElementsInstancedBaseVertex"); |
| return; |
| } |
| |
| GL_EXTCALL(glDrawElementsInstanced(state->gl_primitive_type, count, idx_type, |
| (const char *)idx_data + (idx_size * start_idx), instance_count)); |
| checkGLcall("glDrawElementsInstanced"); |
| return; |
| } |
| |
| /* Instancing emulation by mixing immediate mode and arrays. */ |
| |
| /* This is a nasty thing. MSDN says no hardware supports this and |
| * applications have to use software vertex processing. We don't support |
| * this for now. |
| * |
| * Shouldn't be too hard to support with OpenGL, in theory just call |
| * glDrawArrays() instead of drawElements(). But the stream fequency value |
| * has a different meaning in that situation. */ |
| if (!idx_size) |
| { |
| FIXME("Non-indexed instanced drawing is not supported\n"); |
| return; |
| } |
| |
| for (i = 0; i < ARRAY_SIZE(si->elements); ++i) |
| { |
| if (!(si->use_map & (1u << i))) |
| continue; |
| |
| if (state->streams[si->elements[i].stream_idx].flags & WINED3DSTREAMSOURCE_INSTANCEDATA) |
| instanced_elements[instanced_element_count++] = i; |
| } |
| |
| for (i = 0; i < instance_count; ++i) |
| { |
| /* Specify the instanced attributes using immediate mode calls. */ |
| for (j = 0; j < instanced_element_count; ++j) |
| { |
| const struct wined3d_stream_info_element *element; |
| unsigned int element_idx; |
| const BYTE *ptr; |
| |
| element_idx = instanced_elements[j]; |
| element = &si->elements[element_idx]; |
| ptr = element->data.addr + element->stride * i; |
| if (element->data.buffer_object) |
| ptr += (ULONG_PTR)wined3d_buffer_load_sysmem(state->streams[element->stream_idx].buffer, context); |
| ops->generic[element->format->emit_idx](element_idx, ptr); |
| } |
| |
| if (gl_info->supported[ARB_DRAW_ELEMENTS_BASE_VERTEX]) |
| { |
| GL_EXTCALL(glDrawElementsBaseVertex(state->gl_primitive_type, count, idx_type, |
| (const char *)idx_data + (idx_size * start_idx), base_vertex_idx)); |
| checkGLcall("glDrawElementsBaseVertex"); |
| } |
| else |
| { |
| gl_info->gl_ops.gl.p_glDrawElements(state->gl_primitive_type, count, idx_type, |
| (const char *)idx_data + (idx_size * start_idx)); |
| checkGLcall("glDrawElements"); |
| } |
| } |
| } |
| |
| static unsigned int get_stride_idx(const void *idx_data, unsigned int idx_size, |
| unsigned int base_vertex_idx, unsigned int start_idx, unsigned int vertex_idx) |
| { |
| if (!idx_data) |
| return start_idx + vertex_idx; |
| if (idx_size == 2) |
| return ((const WORD *)idx_data)[start_idx + vertex_idx] + base_vertex_idx; |
| return ((const DWORD *)idx_data)[start_idx + vertex_idx] + base_vertex_idx; |
| } |
| |
| /* Context activation is done by the caller. */ |
| static void draw_primitive_immediate_mode(struct wined3d_context *context, const struct wined3d_state *state, |
| const struct wined3d_stream_info *si, const void *idx_data, unsigned int idx_size, |
| int base_vertex_idx, unsigned int start_idx, unsigned int vertex_count, unsigned int instance_count) |
| { |
| const BYTE *position = NULL, *normal = NULL, *diffuse = NULL, *specular = NULL; |
| const struct wined3d_d3d_info *d3d_info = context->d3d_info; |
| unsigned int coord_idx, stride_idx, texture_idx, vertex_idx; |
| const struct wined3d_gl_info *gl_info = context->gl_info; |
| const struct wined3d_stream_info_element *element; |
| const BYTE *tex_coords[WINED3DDP_MAXTEXCOORD]; |
| unsigned int texture_unit, texture_stages; |
| const struct wined3d_ffp_attrib_ops *ops; |
| unsigned int untracked_material_count; |
| unsigned int tex_mask = 0; |
| BOOL specular_fog = FALSE; |
| BOOL ps = use_ps(state); |
| const void *ptr; |
| |
| static unsigned int once; |
| |
| if (!once++) |
| FIXME_(d3d_perf)("Drawing using immediate mode.\n"); |
| else |
| WARN_(d3d_perf)("Drawing using immediate mode.\n"); |
| |
| if (!idx_size && idx_data) |
| ERR("Non-NULL idx_data with 0 idx_size, this should never happen.\n"); |
| |
| if (instance_count) |
| FIXME("Instancing not implemented.\n"); |
| |
| /* Immediate mode drawing can't make use of indices in a VBO - get the |
| * data from the index buffer. */ |
| if (idx_size) |
| idx_data = wined3d_buffer_load_sysmem(state->index_buffer, context) + state->index_offset; |
| |
| ops = &d3d_info->ffp_attrib_ops; |
| |
| gl_info->gl_ops.gl.p_glBegin(state->gl_primitive_type); |
| |
| if (use_vs(state) || d3d_info->ffp_generic_attributes) |
| { |
| for (vertex_idx = 0; vertex_idx < vertex_count; ++vertex_idx) |
| { |
| unsigned int use_map = si->use_map; |
| unsigned int element_idx; |
| |
| stride_idx = get_stride_idx(idx_data, idx_size, base_vertex_idx, start_idx, vertex_idx); |
| for (element_idx = MAX_ATTRIBS - 1; use_map; use_map &= ~(1u << element_idx), --element_idx) |
| { |
| if (!(use_map & 1u << element_idx)) |
| continue; |
| |
| ptr = si->elements[element_idx].data.addr + si->elements[element_idx].stride * stride_idx; |
| ops->generic[si->elements[element_idx].format->emit_idx](element_idx, ptr); |
| } |
| } |
| |
| gl_info->gl_ops.gl.p_glEnd(); |
| return; |
| } |
| |
| if (si->use_map & (1u << WINED3D_FFP_POSITION)) |
| position = si->elements[WINED3D_FFP_POSITION].data.addr; |
| |
| if (si->use_map & (1u << WINED3D_FFP_NORMAL)) |
| normal = si->elements[WINED3D_FFP_NORMAL].data.addr; |
| else |
| gl_info->gl_ops.gl.p_glNormal3f(0.0f, 0.0f, 0.0f); |
| |
| untracked_material_count = context->num_untracked_materials; |
| if (si->use_map & (1u << WINED3D_FFP_DIFFUSE)) |
| { |
| element = &si->elements[WINED3D_FFP_DIFFUSE]; |
| diffuse = element->data.addr; |
| |
| if (untracked_material_count && element->format->id != WINED3DFMT_B8G8R8A8_UNORM) |
| FIXME("Implement diffuse color tracking from %s.\n", debug_d3dformat(element->format->id)); |
| } |
| else |
| { |
| gl_info->gl_ops.gl.p_glColor4f(1.0f, 1.0f, 1.0f, 1.0f); |
| } |
| |
| if (si->use_map & (1u << WINED3D_FFP_SPECULAR)) |
| { |
| element = &si->elements[WINED3D_FFP_SPECULAR]; |
| specular = element->data.addr; |
| |
| /* Special case where the fog density is stored in the specular alpha channel. */ |
| if (state->render_states[WINED3D_RS_FOGENABLE] |
| && (state->render_states[WINED3D_RS_FOGVERTEXMODE] == WINED3D_FOG_NONE |
| || si->elements[WINED3D_FFP_POSITION].format->id == WINED3DFMT_R32G32B32A32_FLOAT) |
| && state->render_states[WINED3D_RS_FOGTABLEMODE] == WINED3D_FOG_NONE) |
| { |
| if (gl_info->supported[EXT_FOG_COORD]) |
| { |
| if (element->format->id == WINED3DFMT_B8G8R8A8_UNORM) |
| specular_fog = TRUE; |
| else |
| FIXME("Implement fog coordinates from %s.\n", debug_d3dformat(element->format->id)); |
| } |
| else |
| { |
| static unsigned int once; |
| |
| if (!once++) |
| FIXME("Implement fog for transformed vertices in software.\n"); |
| } |
| } |
| } |
| else if (gl_info->supported[EXT_SECONDARY_COLOR]) |
| { |
| GL_EXTCALL(glSecondaryColor3fEXT)(0.0f, 0.0f, 0.0f); |
| } |
| |
| texture_stages = d3d_info->limits.ffp_blend_stages; |
| for (texture_idx = 0; texture_idx < texture_stages; ++texture_idx) |
| { |
| if (!gl_info->supported[ARB_MULTITEXTURE] && texture_idx > 0) |
| { |
| FIXME("Program using multiple concurrent textures which this OpenGL implementation doesn't support.\n"); |
| continue; |
| } |
| |
| if (!ps && !state->textures[texture_idx]) |
| continue; |
| |
| texture_unit = context->tex_unit_map[texture_idx]; |
| if (texture_unit == WINED3D_UNMAPPED_STAGE) |
| continue; |
| |
| coord_idx = state->texture_states[texture_idx][WINED3D_TSS_TEXCOORD_INDEX]; |
| if (coord_idx > 7) |
| { |
| TRACE("Skipping generated coordinates (%#x) for texture %u.\n", coord_idx, texture_idx); |
| continue; |
| } |
| |
| if (si->use_map & (1u << (WINED3D_FFP_TEXCOORD0 + coord_idx))) |
| { |
| tex_coords[coord_idx] = si->elements[WINED3D_FFP_TEXCOORD0 + coord_idx].data.addr; |
| tex_mask |= (1u << texture_idx); |
| } |
| else |
| { |
| TRACE("Setting default coordinates for texture %u.\n", texture_idx); |
| if (gl_info->supported[ARB_MULTITEXTURE]) |
| GL_EXTCALL(glMultiTexCoord4fARB(GL_TEXTURE0_ARB + texture_unit, 0.0f, 0.0f, 0.0f, 1.0f)); |
| else |
| gl_info->gl_ops.gl.p_glTexCoord4f(0.0f, 0.0f, 0.0f, 1.0f); |
| } |
| } |
| |
| /* Blending data and point sizes are not supported by this function. They |
| * are not supported by the fixed function pipeline at all. A FIXME for |
| * them is printed after decoding the vertex declaration. */ |
| for (vertex_idx = 0; vertex_idx < vertex_count; ++vertex_idx) |
| { |
| unsigned int tmp_tex_mask; |
| |
| stride_idx = get_stride_idx(idx_data, idx_size, base_vertex_idx, start_idx, vertex_idx); |
| |
| if (normal) |
| { |
| ptr = normal + stride_idx * si->elements[WINED3D_FFP_NORMAL].stride; |
| ops->normal[si->elements[WINED3D_FFP_NORMAL].format->emit_idx](ptr); |
| } |
| |
| if (diffuse) |
| { |
| ptr = diffuse + stride_idx * si->elements[WINED3D_FFP_DIFFUSE].stride; |
| ops->diffuse[si->elements[WINED3D_FFP_DIFFUSE].format->emit_idx](ptr); |
| |
| if (untracked_material_count) |
| { |
| struct wined3d_color color; |
| unsigned int i; |
| |
| wined3d_color_from_d3dcolor(&color, *(const DWORD *)ptr); |
| for (i = 0; i < untracked_material_count; ++i) |
| { |
| gl_info->gl_ops.gl.p_glMaterialfv(GL_FRONT_AND_BACK, context->untracked_materials[i], &color.r); |
| } |
| } |
| } |
| |
| if (specular) |
| { |
| ptr = specular + stride_idx * si->elements[WINED3D_FFP_SPECULAR].stride; |
| ops->specular[si->elements[WINED3D_FFP_SPECULAR].format->emit_idx](ptr); |
| |
| if (specular_fog) |
| GL_EXTCALL(glFogCoordfEXT((float)(*(const DWORD *)ptr >> 24))); |
| } |
| |
| tmp_tex_mask = tex_mask; |
| for (texture_idx = 0; tmp_tex_mask; tmp_tex_mask >>= 1, ++texture_idx) |
| { |
| if (!(tmp_tex_mask & 1)) |
| continue; |
| |
| coord_idx = state->texture_states[texture_idx][WINED3D_TSS_TEXCOORD_INDEX]; |
| ptr = tex_coords[coord_idx] + (stride_idx * si->elements[WINED3D_FFP_TEXCOORD0 + coord_idx].stride); |
| ops->texcoord[si->elements[WINED3D_FFP_TEXCOORD0 + coord_idx].format->emit_idx]( |
| GL_TEXTURE0_ARB + context->tex_unit_map[texture_idx], ptr); |
| } |
| |
| if (position) |
| { |
| ptr = position + stride_idx * si->elements[WINED3D_FFP_POSITION].stride; |
| ops->position[si->elements[WINED3D_FFP_POSITION].format->emit_idx](ptr); |
| } |
| } |
| |
| gl_info->gl_ops.gl.p_glEnd(); |
| checkGLcall("glEnd and previous calls"); |
| } |
| |
| static void remove_vbos(struct wined3d_context *context, |
| const struct wined3d_state *state, struct wined3d_stream_info *s) |
| { |
| unsigned int i; |
| |
| for (i = 0; i < ARRAY_SIZE(s->elements); ++i) |
| { |
| struct wined3d_stream_info_element *e; |
| |
| if (!(s->use_map & (1u << i))) |
| continue; |
| |
| e = &s->elements[i]; |
| if (e->data.buffer_object) |
| { |
| struct wined3d_buffer *vb = state->streams[e->stream_idx].buffer; |
| e->data.buffer_object = 0; |
| e->data.addr += (ULONG_PTR)wined3d_buffer_load_sysmem(vb, context); |
| } |
| } |
| } |
| |
| static BOOL use_transform_feedback(const struct wined3d_state *state) |
| { |
| const struct wined3d_shader *shader; |
| if (!(shader = state->shader[WINED3D_SHADER_TYPE_GEOMETRY])) |
| return FALSE; |
| return shader->u.gs.so_desc.element_count; |
| } |
| |
| static void context_pause_transform_feedback(struct wined3d_context *context, BOOL force) |
| { |
| const struct wined3d_gl_info *gl_info = context->gl_info; |
| |
| if (!context->transform_feedback_active || context->transform_feedback_paused) |
| return; |
| |
| if (gl_info->supported[ARB_TRANSFORM_FEEDBACK2]) |
| { |
| GL_EXTCALL(glPauseTransformFeedback()); |
| checkGLcall("glPauseTransformFeedback"); |
| context->transform_feedback_paused = 1; |
| return; |
| } |
| |
| WARN("Cannot pause transform feedback operations.\n"); |
| |
| if (force) |
| context_end_transform_feedback(context); |
| } |
| |
| static GLenum gl_tfb_primitive_type_from_d3d(enum wined3d_primitive_type primitive_type) |
| { |
| GLenum gl_primitive_type = gl_primitive_type_from_d3d(primitive_type); |
| switch (gl_primitive_type) |
| { |
| case GL_POINTS: |
| return GL_POINTS; |
| |
| case GL_LINE_STRIP: |
| case GL_LINE_STRIP_ADJACENCY: |
| case GL_LINES_ADJACENCY: |
| case GL_LINES: |
| return GL_LINES; |
| |
| case GL_TRIANGLE_FAN: |
| case GL_TRIANGLE_STRIP: |
| case GL_TRIANGLE_STRIP_ADJACENCY: |
| case GL_TRIANGLES_ADJACENCY: |
| case GL_TRIANGLES: |
| return GL_TRIANGLES; |
| |
| default: |
| return gl_primitive_type; |
| } |
| } |
| |
| /* Routine common to the draw primitive and draw indexed primitive routines */ |
| void draw_primitive(struct wined3d_device *device, const struct wined3d_state *state, |
| int base_vertex_idx, unsigned int start_idx, unsigned int index_count, |
| unsigned int start_instance, unsigned int instance_count, BOOL indexed) |
| { |
| BOOL emulation = FALSE, rasterizer_discard = FALSE; |
| const struct wined3d_fb_state *fb = state->fb; |
| const struct wined3d_stream_info *stream_info; |
| struct wined3d_rendertarget_view *dsv, *rtv; |
| struct wined3d_stream_info si_emulated; |
| struct wined3d_fence *ib_fence = NULL; |
| const struct wined3d_gl_info *gl_info; |
| struct wined3d_context *context; |
| unsigned int i, idx_size = 0; |
| const void *idx_data = NULL; |
| |
| if (!index_count) |
| return; |
| |
| if (!(rtv = fb->render_targets[0])) |
| rtv = fb->depth_stencil; |
| if (rtv) |
| context = context_acquire(device, wined3d_texture_from_resource(rtv->resource), rtv->sub_resource_idx); |
| else |
| context = context_acquire(device, NULL, 0); |
| if (!context->valid) |
| { |
| context_release(context); |
| WARN("Invalid context, skipping draw.\n"); |
| return; |
| } |
| gl_info = context->gl_info; |
| |
| if (!use_transform_feedback(state)) |
| context_pause_transform_feedback(context, TRUE); |
| |
| for (i = 0; i < gl_info->limits.buffers; ++i) |
| { |
| if (!(rtv = fb->render_targets[i]) || rtv->format->id == WINED3DFMT_NULL) |
| continue; |
| |
| if (state->render_states[WINED3D_RS_COLORWRITEENABLE]) |
| { |
| wined3d_rendertarget_view_load_location(rtv, context, rtv->resource->draw_binding); |
| wined3d_rendertarget_view_invalidate_location(rtv, ~rtv->resource->draw_binding); |
| } |
| else |
| { |
| wined3d_rendertarget_view_prepare_location(rtv, context, rtv->resource->draw_binding); |
| } |
| } |
| |
| if ((dsv = fb->depth_stencil)) |
| { |
| /* Note that this depends on the context_acquire() call above to set |
| * context->render_offscreen properly. We don't currently take the |
| * Z-compare function into account, but we could skip loading the |
| * depthstencil for D3DCMP_NEVER and D3DCMP_ALWAYS as well. Also note |
| * that we never copy the stencil data.*/ |
| DWORD location = context->render_offscreen ? dsv->resource->draw_binding : WINED3D_LOCATION_DRAWABLE; |
| |
| if (state->render_states[WINED3D_RS_ZWRITEENABLE] || state->render_states[WINED3D_RS_ZENABLE]) |
| wined3d_rendertarget_view_load_location(dsv, context, location); |
| else |
| wined3d_rendertarget_view_prepare_location(dsv, context, location); |
| } |
| |
| if (!context_apply_draw_state(context, device, state)) |
| { |
| context_release(context); |
| WARN("Unable to apply draw state, skipping draw.\n"); |
| return; |
| } |
| |
| if (dsv && state->render_states[WINED3D_RS_ZWRITEENABLE]) |
| { |
| DWORD location = context->render_offscreen ? dsv->resource->draw_binding : WINED3D_LOCATION_DRAWABLE; |
| |
| wined3d_rendertarget_view_validate_location(dsv, location); |
| wined3d_rendertarget_view_invalidate_location(dsv, ~location); |
| } |
| |
| stream_info = &context->stream_info; |
| if (context->instance_count) |
| instance_count = context->instance_count; |
| |
| if (indexed) |
| { |
| struct wined3d_buffer *index_buffer = state->index_buffer; |
| if (!index_buffer->buffer_object || !stream_info->all_vbo) |
| { |
| idx_data = index_buffer->resource.heap_memory; |
| } |
| else |
| { |
| ib_fence = index_buffer->fence; |
| idx_data = NULL; |
| } |
| idx_data = (const BYTE *)idx_data + state->index_offset; |
| |
| if (state->index_format == WINED3DFMT_R16_UINT) |
| idx_size = 2; |
| else |
| idx_size = 4; |
| } |
| |
| if (!use_vs(state)) |
| { |
| if (!stream_info->position_transformed && context->num_untracked_materials |
| && state->render_states[WINED3D_RS_LIGHTING]) |
| { |
| static BOOL warned; |
| |
| if (!warned++) |
| FIXME("Using software emulation because not all material properties could be tracked.\n"); |
| else |
| WARN_(d3d_perf)("Using software emulation because not all material properties could be tracked.\n"); |
| emulation = TRUE; |
| } |
| else if (context->fog_coord && state->render_states[WINED3D_RS_FOGENABLE]) |
| { |
| static BOOL warned; |
| |
| /* Either write a pipeline replacement shader or convert the |
| * specular alpha from unsigned byte to a float in the vertex |
| * buffer. */ |
| if (!warned++) |
| FIXME("Using software emulation because manual fog coordinates are provided.\n"); |
| else |
| WARN_(d3d_perf)("Using software emulation because manual fog coordinates are provided.\n"); |
| emulation = TRUE; |
| } |
| |
| if (emulation) |
| { |
| si_emulated = context->stream_info; |
| remove_vbos(context, state, &si_emulated); |
| stream_info = &si_emulated; |
| } |
| } |
| |
| if (use_transform_feedback(state)) |
| { |
| const struct wined3d_shader *shader = state->shader[WINED3D_SHADER_TYPE_GEOMETRY]; |
| |
| if (shader->u.gs.so_desc.rasterizer_stream_idx == WINED3D_NO_RASTERIZER_STREAM) |
| { |
| glEnable(GL_RASTERIZER_DISCARD); |
| checkGLcall("enable rasterizer discard"); |
| rasterizer_discard = TRUE; |
| } |
| |
| if (context->transform_feedback_paused) |
| { |
| GL_EXTCALL(glResumeTransformFeedback()); |
| checkGLcall("glResumeTransformFeedback"); |
| context->transform_feedback_paused = 0; |
| } |
| else if (!context->transform_feedback_active) |
| { |
| GLenum mode = gl_tfb_primitive_type_from_d3d(shader->u.gs.output_type); |
| GL_EXTCALL(glBeginTransformFeedback(mode)); |
| checkGLcall("glBeginTransformFeedback"); |
| context->transform_feedback_active = 1; |
| } |
| } |
| |
| if (state->gl_primitive_type == GL_PATCHES) |
| { |
| GL_EXTCALL(glPatchParameteri(GL_PATCH_VERTICES, state->gl_patch_vertices)); |
| checkGLcall("glPatchParameteri"); |
| } |
| |
| if (context->use_immediate_mode_draw || emulation) |
| draw_primitive_immediate_mode(context, state, stream_info, idx_data, |
| idx_size, base_vertex_idx, start_idx, index_count, instance_count); |
| else |
| draw_primitive_arrays(context, state, idx_data, idx_size, base_vertex_idx, |
| start_idx, index_count, start_instance, instance_count); |
| |
| if (context->uses_uavs) |
| { |
| GL_EXTCALL(glMemoryBarrier(GL_ALL_BARRIER_BITS)); |
| checkGLcall("glMemoryBarrier"); |
| } |
| |
| context_pause_transform_feedback(context, FALSE); |
| |
| if (rasterizer_discard) |
| { |
| glDisable(GL_RASTERIZER_DISCARD); |
| checkGLcall("disable rasterizer discard"); |
| } |
| |
| if (ib_fence) |
| wined3d_fence_issue(ib_fence, device); |
| for (i = 0; i < context->buffer_fence_count; ++i) |
| wined3d_fence_issue(context->buffer_fences[i], device); |
| |
| if (wined3d_settings.strict_draw_ordering) |
| gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */ |
| |
| context_release(context); |
| |
| TRACE("Done all gl drawing.\n"); |
| } |
| |
| void dispatch_compute(struct wined3d_device *device, const struct wined3d_state *state, |
| const struct wined3d_dispatch_parameters *parameters) |
| { |
| const struct wined3d_gl_info *gl_info; |
| struct wined3d_context *context; |
| |
| context = context_acquire(device, NULL, 0); |
| if (!context->valid) |
| { |
| context_release(context); |
| WARN("Invalid context, skipping dispatch.\n"); |
| return; |
| } |
| gl_info = context->gl_info; |
| |
| if (!gl_info->supported[ARB_COMPUTE_SHADER]) |
| { |
| context_release(context); |
| FIXME("OpenGL implementation does not support compute shaders.\n"); |
| return; |
| } |
| |
| context_apply_compute_state(context, device, state); |
| |
| if (!state->shader[WINED3D_SHADER_TYPE_COMPUTE]) |
| { |
| context_release(context); |
| WARN("No compute shader bound, skipping dispatch.\n"); |
| return; |
| } |
| |
| if (parameters->indirect) |
| { |
| const struct wined3d_indirect_dispatch_parameters *indirect = ¶meters->u.indirect; |
| struct wined3d_buffer *buffer = indirect->buffer; |
| |
| wined3d_buffer_load(buffer, context, state); |
| GL_EXTCALL(glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, buffer->buffer_object)); |
| GL_EXTCALL(glDispatchComputeIndirect((GLintptr)indirect->offset)); |
| GL_EXTCALL(glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, 0)); |
| } |
| else |
| { |
| const struct wined3d_direct_dispatch_parameters *direct = ¶meters->u.direct; |
| GL_EXTCALL(glDispatchCompute(direct->group_count_x, direct->group_count_y, direct->group_count_z)); |
| } |
| checkGLcall("dispatch compute"); |
| |
| GL_EXTCALL(glMemoryBarrier(GL_ALL_BARRIER_BITS)); |
| checkGLcall("glMemoryBarrier"); |
| |
| if (wined3d_settings.strict_draw_ordering) |
| gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */ |
| |
| context_release(context); |
| } |