2 * Context and render target management in wined3d
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-2011, 2013 Stefan Dösinger for CodeWeavers
10 * Copyright 2009-2011 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
29 #include "wined3d_private.h"
31 WINE_DEFAULT_DEBUG_CHANNEL(d3d
);
32 WINE_DECLARE_DEBUG_CHANNEL(d3d_perf
);
34 void context_resource_released(const struct wined3d_device
*device
, struct wined3d_resource
*resource
)
38 for (i
= 0; i
< device
->context_count
; ++i
)
40 struct wined3d_context
*context
= device
->contexts
[i
];
42 if (&context
->current_rt
.texture
->resource
== resource
)
44 context
->current_rt
.texture
= NULL
;
45 context
->current_rt
.sub_resource_idx
= 0;
50 void wined3d_context_cleanup(struct wined3d_context
*context
)
54 /* This is used when a context for render target A is active, but a separate context is
55 * needed to access the WGL framebuffer for render target B. Re-acquire a context for rt
56 * A to avoid breaking caller code. */
57 void context_restore(struct wined3d_context
*context
, struct wined3d_texture
*texture
, unsigned int sub_resource_idx
)
59 if (context
->current_rt
.texture
!= texture
|| context
->current_rt
.sub_resource_idx
!= sub_resource_idx
)
61 context_release(context
);
62 context
= context_acquire(texture
->resource
.device
, texture
, sub_resource_idx
);
65 context_release(context
);
68 void context_invalidate_compute_state(struct wined3d_context
*context
, DWORD state_id
)
70 DWORD representative
= context
->state_table
[state_id
].representative
- STATE_COMPUTE_OFFSET
;
71 unsigned int index
, shift
;
73 index
= representative
/ (sizeof(*context
->dirty_compute_states
) * CHAR_BIT
);
74 shift
= representative
& (sizeof(*context
->dirty_compute_states
) * CHAR_BIT
- 1);
75 context
->dirty_compute_states
[index
] |= (1u << shift
);
78 void context_invalidate_state(struct wined3d_context
*context
, unsigned int state_id
)
80 unsigned int representative
= context
->state_table
[state_id
].representative
;
81 unsigned int index
, shift
;
83 index
= representative
/ (sizeof(*context
->dirty_graphics_states
) * CHAR_BIT
);
84 shift
= representative
& ((sizeof(*context
->dirty_graphics_states
) * CHAR_BIT
) - 1);
85 context
->dirty_graphics_states
[index
] |= (1u << shift
);
88 void wined3d_context_init(struct wined3d_context
*context
, struct wined3d_swapchain
*swapchain
)
90 struct wined3d_device
*device
= swapchain
->device
;
93 context
->d3d_info
= &device
->adapter
->d3d_info
;
94 context
->state_table
= device
->state_table
;
96 /* Mark all states dirty to force a proper initialization of the states on
97 * the first use of the context. Compute states do not need initialization. */
98 for (state
= 0; state
<= STATE_HIGHEST
; ++state
)
100 if (context
->state_table
[state
].representative
&& !STATE_IS_COMPUTE(state
))
101 context_invalidate_state(context
, state
);
104 context
->device
= device
;
105 context
->swapchain
= swapchain
;
106 context
->current_rt
.texture
= swapchain
->front_buffer
;
107 context
->current_rt
.sub_resource_idx
= 0;
109 context
->shader_update_mask
= (1u << WINED3D_SHADER_TYPE_PIXEL
)
110 | (1u << WINED3D_SHADER_TYPE_VERTEX
)
111 | (1u << WINED3D_SHADER_TYPE_GEOMETRY
)
112 | (1u << WINED3D_SHADER_TYPE_HULL
)
113 | (1u << WINED3D_SHADER_TYPE_DOMAIN
)
114 | (1u << WINED3D_SHADER_TYPE_COMPUTE
);
117 HRESULT
wined3d_context_no3d_init(struct wined3d_context
*context_no3d
, struct wined3d_swapchain
*swapchain
)
119 TRACE("context_no3d %p, swapchain %p.\n", context_no3d
, swapchain
);
121 wined3d_context_init(context_no3d
, swapchain
);
126 static BOOL
fixed_get_input(BYTE usage
, BYTE usage_idx
, unsigned int *regnum
)
128 if ((usage
== WINED3D_DECL_USAGE_POSITION
|| usage
== WINED3D_DECL_USAGE_POSITIONT
) && !usage_idx
)
129 *regnum
= WINED3D_FFP_POSITION
;
130 else if (usage
== WINED3D_DECL_USAGE_BLEND_WEIGHT
&& !usage_idx
)
131 *regnum
= WINED3D_FFP_BLENDWEIGHT
;
132 else if (usage
== WINED3D_DECL_USAGE_BLEND_INDICES
&& !usage_idx
)
133 *regnum
= WINED3D_FFP_BLENDINDICES
;
134 else if (usage
== WINED3D_DECL_USAGE_NORMAL
&& !usage_idx
)
135 *regnum
= WINED3D_FFP_NORMAL
;
136 else if (usage
== WINED3D_DECL_USAGE_PSIZE
&& !usage_idx
)
137 *regnum
= WINED3D_FFP_PSIZE
;
138 else if (usage
== WINED3D_DECL_USAGE_COLOR
&& !usage_idx
)
139 *regnum
= WINED3D_FFP_DIFFUSE
;
140 else if (usage
== WINED3D_DECL_USAGE_COLOR
&& usage_idx
== 1)
141 *regnum
= WINED3D_FFP_SPECULAR
;
142 else if (usage
== WINED3D_DECL_USAGE_TEXCOORD
&& usage_idx
< WINED3DDP_MAXTEXCOORD
)
143 *regnum
= WINED3D_FFP_TEXCOORD0
+ usage_idx
;
146 WARN("Unsupported input stream [usage=%s, usage_idx=%u].\n", debug_d3ddeclusage(usage
), usage_idx
);
154 /* Context activation is done by the caller. */
155 void wined3d_stream_info_from_declaration(struct wined3d_stream_info
*stream_info
,
156 const struct wined3d_state
*state
, const struct wined3d_d3d_info
*d3d_info
)
158 /* We need to deal with frequency data! */
159 struct wined3d_vertex_declaration
*declaration
= state
->vertex_declaration
;
160 BOOL generic_attributes
= d3d_info
->ffp_generic_attributes
;
161 BOOL use_vshader
= use_vs(state
);
164 stream_info
->use_map
= 0;
165 stream_info
->swizzle_map
= 0;
166 stream_info
->position_transformed
= 0;
171 stream_info
->position_transformed
= declaration
->position_transformed
;
173 /* Translate the declaration into strided data. */
174 for (i
= 0; i
< declaration
->element_count
; ++i
)
176 const struct wined3d_vertex_declaration_element
*element
= &declaration
->elements
[i
];
177 const struct wined3d_stream_state
*stream
= &state
->streams
[element
->input_slot
];
181 TRACE("%p Element %p (%u of %u).\n", declaration
->elements
,
182 element
, i
+ 1, declaration
->element_count
);
187 TRACE("offset %u input_slot %u usage_idx %d.\n", element
->offset
, element
->input_slot
, element
->usage_idx
);
191 if (element
->output_slot
== WINED3D_OUTPUT_SLOT_UNUSED
)
195 else if (element
->output_slot
== WINED3D_OUTPUT_SLOT_SEMANTIC
)
197 /* TODO: Assuming vertexdeclarations are usually used with the
198 * same or a similar shader, it might be worth it to store the
199 * last used output slot and try that one first. */
200 stride_used
= vshader_get_input(state
->shader
[WINED3D_SHADER_TYPE_VERTEX
],
201 element
->usage
, element
->usage_idx
, &idx
);
205 idx
= element
->output_slot
;
211 if (!generic_attributes
&& !element
->ffp_valid
)
213 WARN("Skipping unsupported fixed function element of format %s and usage %s.\n",
214 debug_d3dformat(element
->format
->id
), debug_d3ddeclusage(element
->usage
));
219 stride_used
= fixed_get_input(element
->usage
, element
->usage_idx
, &idx
);
225 TRACE("Load %s array %u [usage %s, usage_idx %u, "
226 "input_slot %u, offset %u, stride %u, format %s, class %s, step_rate %u].\n",
227 use_vshader
? "shader": "fixed function", idx
,
228 debug_d3ddeclusage(element
->usage
), element
->usage_idx
, element
->input_slot
,
229 element
->offset
, stream
->stride
, debug_d3dformat(element
->format
->id
),
230 debug_d3dinput_classification(element
->input_slot_class
), element
->instance_data_step_rate
);
232 stream_info
->elements
[idx
].format
= element
->format
;
233 stream_info
->elements
[idx
].data
.buffer_object
= 0;
234 stream_info
->elements
[idx
].data
.addr
= (BYTE
*)NULL
+ stream
->offset
+ element
->offset
;
235 stream_info
->elements
[idx
].stride
= stream
->stride
;
236 stream_info
->elements
[idx
].stream_idx
= element
->input_slot
;
237 if (stream
->flags
& WINED3DSTREAMSOURCE_INSTANCEDATA
)
239 stream_info
->elements
[idx
].divisor
= 1;
240 stream_info
->elements
[idx
].instanced
= true;
242 else if (element
->input_slot_class
== WINED3D_INPUT_PER_INSTANCE_DATA
)
244 stream_info
->elements
[idx
].divisor
= element
->instance_data_step_rate
;
245 stream_info
->elements
[idx
].instanced
= true;
249 stream_info
->elements
[idx
].divisor
= 0;
250 stream_info
->elements
[idx
].instanced
= false;
253 if (!d3d_info
->vertex_bgra
&& element
->format
->id
== WINED3DFMT_B8G8R8A8_UNORM
)
255 stream_info
->swizzle_map
|= 1u << idx
;
257 stream_info
->use_map
|= 1u << idx
;
262 /* Context activation is done by the caller. */
263 void context_update_stream_info(struct wined3d_context
*context
, const struct wined3d_state
*state
)
265 struct wined3d_stream_info
*stream_info
= &context
->stream_info
;
266 const struct wined3d_d3d_info
*d3d_info
= context
->d3d_info
;
267 DWORD prev_all_vbo
= stream_info
->all_vbo
;
271 wined3d_stream_info_from_declaration(stream_info
, state
, d3d_info
);
273 stream_info
->all_vbo
= 1;
274 for (i
= 0, map
= stream_info
->use_map
; map
; map
>>= 1, ++i
)
276 struct wined3d_stream_info_element
*element
;
277 struct wined3d_bo_address data
;
278 struct wined3d_buffer
*buffer
;
283 element
= &stream_info
->elements
[i
];
284 buffer
= state
->streams
[element
->stream_idx
].buffer
;
286 /* We can't use VBOs if the base vertex index is negative. OpenGL
287 * doesn't accept negative offsets (or rather offsets bigger than the
288 * VBO, because the pointer is unsigned), so use system memory
289 * sources. In most sane cases the pointer - offset will still be > 0,
290 * otherwise it will wrap around to some big value. Hope that with the
291 * indices the driver wraps it back internally. If not,
292 * draw_primitive_immediate_mode() is needed, including a vertex buffer
294 if (state
->load_base_vertex_index
< 0)
296 WARN_(d3d_perf
)("load_base_vertex_index is < 0 (%d), not using VBOs.\n",
297 state
->load_base_vertex_index
);
298 element
->data
.buffer_object
= 0;
299 element
->data
.addr
+= (ULONG_PTR
)wined3d_buffer_load_sysmem(buffer
, context
);
300 if ((UINT_PTR
)element
->data
.addr
< -state
->load_base_vertex_index
* element
->stride
)
301 FIXME("System memory vertex data load offset is negative!\n");
305 wined3d_buffer_load(buffer
, context
, state
);
306 wined3d_buffer_get_memory(buffer
, context
, &data
);
307 element
->data
.buffer_object
= data
.buffer_object
;
308 element
->data
.addr
+= (ULONG_PTR
)data
.addr
;
311 if (!element
->data
.buffer_object
)
312 stream_info
->all_vbo
= 0;
314 TRACE("Load array %u %s.\n", i
, debug_bo_address(&element
->data
));
317 if (prev_all_vbo
!= stream_info
->all_vbo
)
318 context_invalidate_state(context
, STATE_INDEXBUFFER
);
320 context
->use_immediate_mode_draw
= FALSE
;
322 if (stream_info
->all_vbo
)
327 WORD slow_mask
= -!d3d_info
->ffp_generic_attributes
& (1u << WINED3D_FFP_PSIZE
);
328 slow_mask
|= -(!d3d_info
->vertex_bgra
&& !d3d_info
->ffp_generic_attributes
)
329 & ((1u << WINED3D_FFP_DIFFUSE
) | (1u << WINED3D_FFP_SPECULAR
) | (1u << WINED3D_FFP_BLENDWEIGHT
));
331 if ((stream_info
->position_transformed
&& !d3d_info
->xyzrhw
)
332 || (stream_info
->use_map
& slow_mask
))
333 context
->use_immediate_mode_draw
= TRUE
;
337 static bool is_resource_rtv_bound(const struct wined3d_state
*state
,
338 const struct wined3d_resource
*resource
)
342 if (!resource
->rtv_bind_count_device
)
345 for (i
= 0; i
< ARRAY_SIZE(state
->fb
.render_targets
); ++i
)
347 if (state
->fb
.render_targets
[i
] && state
->fb
.render_targets
[i
]->resource
== resource
)
354 /* Context activation is done by the caller. */
355 static void context_preload_texture(struct wined3d_context
*context
,
356 const struct wined3d_state
*state
, unsigned int idx
)
358 struct wined3d_texture
*texture
;
360 if (!(texture
= state
->textures
[idx
]))
363 if (is_resource_rtv_bound(state
, &texture
->resource
)
364 || (state
->fb
.depth_stencil
&& state
->fb
.depth_stencil
->resource
== &texture
->resource
))
365 context
->uses_fbo_attached_resources
= 1;
367 wined3d_texture_load(texture
, context
, is_srgb_enabled(state
->sampler_states
[idx
]));
370 /* Context activation is done by the caller. */
371 void context_preload_textures(struct wined3d_context
*context
, const struct wined3d_state
*state
)
377 for (i
= 0; i
< WINED3D_MAX_VERTEX_SAMPLERS
; ++i
)
379 if (state
->shader
[WINED3D_SHADER_TYPE_VERTEX
]->reg_maps
.resource_info
[i
].type
)
380 context_preload_texture(context
, state
, WINED3D_MAX_FRAGMENT_SAMPLERS
+ i
);
386 for (i
= 0; i
< WINED3D_MAX_FRAGMENT_SAMPLERS
; ++i
)
388 if (state
->shader
[WINED3D_SHADER_TYPE_PIXEL
]->reg_maps
.resource_info
[i
].type
)
389 context_preload_texture(context
, state
, i
);
394 WORD ffu_map
= context
->fixed_function_usage_map
;
396 for (i
= 0; ffu_map
; ffu_map
>>= 1, ++i
)
399 context_preload_texture(context
, state
, i
);