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
28 #include "wine/port.h"
30 #include "wined3d_private.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(d3d
);
33 WINE_DECLARE_DEBUG_CHANNEL(d3d_perf
);
35 void context_resource_released(const struct wined3d_device
*device
, struct wined3d_resource
*resource
)
39 for (i
= 0; i
< device
->context_count
; ++i
)
41 struct wined3d_context
*context
= device
->contexts
[i
];
43 if (&context
->current_rt
.texture
->resource
== resource
)
45 context
->current_rt
.texture
= NULL
;
46 context
->current_rt
.sub_resource_idx
= 0;
51 void wined3d_context_cleanup(struct wined3d_context
*context
)
55 /* This is used when a context for render target A is active, but a separate context is
56 * needed to access the WGL framebuffer for render target B. Re-acquire a context for rt
57 * A to avoid breaking caller code. */
58 void context_restore(struct wined3d_context
*context
, struct wined3d_texture
*texture
, unsigned int sub_resource_idx
)
60 if (context
->current_rt
.texture
!= texture
|| context
->current_rt
.sub_resource_idx
!= sub_resource_idx
)
62 context_release(context
);
63 context
= context_acquire(texture
->resource
.device
, texture
, sub_resource_idx
);
66 context_release(context
);
69 void context_invalidate_compute_state(struct wined3d_context
*context
, DWORD state_id
)
71 DWORD representative
= context
->state_table
[state_id
].representative
- STATE_COMPUTE_OFFSET
;
72 unsigned int index
, shift
;
74 index
= representative
/ (sizeof(*context
->dirty_compute_states
) * CHAR_BIT
);
75 shift
= representative
& (sizeof(*context
->dirty_compute_states
) * CHAR_BIT
- 1);
76 context
->dirty_compute_states
[index
] |= (1u << shift
);
79 void context_invalidate_state(struct wined3d_context
*context
, unsigned int state_id
)
81 unsigned int representative
= context
->state_table
[state_id
].representative
;
82 unsigned int index
, shift
;
84 index
= representative
/ (sizeof(*context
->dirty_graphics_states
) * CHAR_BIT
);
85 shift
= representative
& ((sizeof(*context
->dirty_graphics_states
) * CHAR_BIT
) - 1);
86 context
->dirty_graphics_states
[index
] |= (1u << shift
);
89 void wined3d_context_init(struct wined3d_context
*context
, struct wined3d_swapchain
*swapchain
)
91 struct wined3d_device
*device
= swapchain
->device
;
94 context
->d3d_info
= &device
->adapter
->d3d_info
;
95 context
->state_table
= device
->state_table
;
97 /* Mark all states dirty to force a proper initialization of the states on
98 * the first use of the context. Compute states do not need initialization. */
99 for (state
= 0; state
<= STATE_HIGHEST
; ++state
)
101 if (context
->state_table
[state
].representative
&& !STATE_IS_COMPUTE(state
))
102 context_invalidate_state(context
, state
);
105 context
->device
= device
;
106 context
->swapchain
= swapchain
;
107 context
->current_rt
.texture
= swapchain
->front_buffer
;
108 context
->current_rt
.sub_resource_idx
= 0;
110 context
->shader_update_mask
= (1u << WINED3D_SHADER_TYPE_PIXEL
)
111 | (1u << WINED3D_SHADER_TYPE_VERTEX
)
112 | (1u << WINED3D_SHADER_TYPE_GEOMETRY
)
113 | (1u << WINED3D_SHADER_TYPE_HULL
)
114 | (1u << WINED3D_SHADER_TYPE_DOMAIN
)
115 | (1u << WINED3D_SHADER_TYPE_COMPUTE
);
118 HRESULT
wined3d_context_no3d_init(struct wined3d_context
*context_no3d
, struct wined3d_swapchain
*swapchain
)
120 TRACE("context_no3d %p, swapchain %p.\n", context_no3d
, swapchain
);
122 wined3d_context_init(context_no3d
, swapchain
);
127 static BOOL
fixed_get_input(BYTE usage
, BYTE usage_idx
, unsigned int *regnum
)
129 if ((usage
== WINED3D_DECL_USAGE_POSITION
|| usage
== WINED3D_DECL_USAGE_POSITIONT
) && !usage_idx
)
130 *regnum
= WINED3D_FFP_POSITION
;
131 else if (usage
== WINED3D_DECL_USAGE_BLEND_WEIGHT
&& !usage_idx
)
132 *regnum
= WINED3D_FFP_BLENDWEIGHT
;
133 else if (usage
== WINED3D_DECL_USAGE_BLEND_INDICES
&& !usage_idx
)
134 *regnum
= WINED3D_FFP_BLENDINDICES
;
135 else if (usage
== WINED3D_DECL_USAGE_NORMAL
&& !usage_idx
)
136 *regnum
= WINED3D_FFP_NORMAL
;
137 else if (usage
== WINED3D_DECL_USAGE_PSIZE
&& !usage_idx
)
138 *regnum
= WINED3D_FFP_PSIZE
;
139 else if (usage
== WINED3D_DECL_USAGE_COLOR
&& !usage_idx
)
140 *regnum
= WINED3D_FFP_DIFFUSE
;
141 else if (usage
== WINED3D_DECL_USAGE_COLOR
&& usage_idx
== 1)
142 *regnum
= WINED3D_FFP_SPECULAR
;
143 else if (usage
== WINED3D_DECL_USAGE_TEXCOORD
&& usage_idx
< WINED3DDP_MAXTEXCOORD
)
144 *regnum
= WINED3D_FFP_TEXCOORD0
+ usage_idx
;
147 WARN("Unsupported input stream [usage=%s, usage_idx=%u].\n", debug_d3ddeclusage(usage
), usage_idx
);
155 /* Context activation is done by the caller. */
156 void wined3d_stream_info_from_declaration(struct wined3d_stream_info
*stream_info
,
157 const struct wined3d_state
*state
, const struct wined3d_d3d_info
*d3d_info
)
159 /* We need to deal with frequency data! */
160 struct wined3d_vertex_declaration
*declaration
= state
->vertex_declaration
;
161 BOOL generic_attributes
= d3d_info
->ffp_generic_attributes
;
162 BOOL use_vshader
= use_vs(state
);
165 stream_info
->use_map
= 0;
166 stream_info
->swizzle_map
= 0;
167 stream_info
->position_transformed
= 0;
172 stream_info
->position_transformed
= declaration
->position_transformed
;
174 /* Translate the declaration into strided data. */
175 for (i
= 0; i
< declaration
->element_count
; ++i
)
177 const struct wined3d_vertex_declaration_element
*element
= &declaration
->elements
[i
];
178 const struct wined3d_stream_state
*stream
= &state
->streams
[element
->input_slot
];
182 TRACE("%p Element %p (%u of %u).\n", declaration
->elements
,
183 element
, i
+ 1, declaration
->element_count
);
188 TRACE("offset %u input_slot %u usage_idx %d.\n", element
->offset
, element
->input_slot
, element
->usage_idx
);
192 if (element
->output_slot
== WINED3D_OUTPUT_SLOT_UNUSED
)
196 else if (element
->output_slot
== WINED3D_OUTPUT_SLOT_SEMANTIC
)
198 /* TODO: Assuming vertexdeclarations are usually used with the
199 * same or a similar shader, it might be worth it to store the
200 * last used output slot and try that one first. */
201 stride_used
= vshader_get_input(state
->shader
[WINED3D_SHADER_TYPE_VERTEX
],
202 element
->usage
, element
->usage_idx
, &idx
);
206 idx
= element
->output_slot
;
212 if (!generic_attributes
&& !element
->ffp_valid
)
214 WARN("Skipping unsupported fixed function element of format %s and usage %s.\n",
215 debug_d3dformat(element
->format
->id
), debug_d3ddeclusage(element
->usage
));
220 stride_used
= fixed_get_input(element
->usage
, element
->usage_idx
, &idx
);
226 TRACE("Load %s array %u [usage %s, usage_idx %u, "
227 "input_slot %u, offset %u, stride %u, format %s, class %s, step_rate %u].\n",
228 use_vshader
? "shader": "fixed function", idx
,
229 debug_d3ddeclusage(element
->usage
), element
->usage_idx
, element
->input_slot
,
230 element
->offset
, stream
->stride
, debug_d3dformat(element
->format
->id
),
231 debug_d3dinput_classification(element
->input_slot_class
), element
->instance_data_step_rate
);
233 stream_info
->elements
[idx
].format
= element
->format
;
234 stream_info
->elements
[idx
].data
.buffer_object
= 0;
235 stream_info
->elements
[idx
].data
.addr
= (BYTE
*)NULL
+ stream
->offset
+ element
->offset
;
236 stream_info
->elements
[idx
].stride
= stream
->stride
;
237 stream_info
->elements
[idx
].stream_idx
= element
->input_slot
;
238 if (stream
->flags
& WINED3DSTREAMSOURCE_INSTANCEDATA
)
240 stream_info
->elements
[idx
].divisor
= 1;
241 stream_info
->elements
[idx
].instanced
= true;
243 else if (element
->input_slot_class
== WINED3D_INPUT_PER_INSTANCE_DATA
)
245 stream_info
->elements
[idx
].divisor
= element
->instance_data_step_rate
;
246 stream_info
->elements
[idx
].instanced
= true;
250 stream_info
->elements
[idx
].divisor
= 0;
251 stream_info
->elements
[idx
].instanced
= false;
254 if (!d3d_info
->vertex_bgra
&& element
->format
->id
== WINED3DFMT_B8G8R8A8_UNORM
)
256 stream_info
->swizzle_map
|= 1u << idx
;
258 stream_info
->use_map
|= 1u << idx
;
263 /* Context activation is done by the caller. */
264 void context_update_stream_info(struct wined3d_context
*context
, const struct wined3d_state
*state
)
266 struct wined3d_stream_info
*stream_info
= &context
->stream_info
;
267 const struct wined3d_d3d_info
*d3d_info
= context
->d3d_info
;
268 DWORD prev_all_vbo
= stream_info
->all_vbo
;
272 wined3d_stream_info_from_declaration(stream_info
, state
, d3d_info
);
274 stream_info
->all_vbo
= 1;
275 for (i
= 0, map
= stream_info
->use_map
; map
; map
>>= 1, ++i
)
277 struct wined3d_stream_info_element
*element
;
278 struct wined3d_bo_address data
;
279 struct wined3d_buffer
*buffer
;
284 element
= &stream_info
->elements
[i
];
285 buffer
= state
->streams
[element
->stream_idx
].buffer
;
287 /* We can't use VBOs if the base vertex index is negative. OpenGL
288 * doesn't accept negative offsets (or rather offsets bigger than the
289 * VBO, because the pointer is unsigned), so use system memory
290 * sources. In most sane cases the pointer - offset will still be > 0,
291 * otherwise it will wrap around to some big value. Hope that with the
292 * indices the driver wraps it back internally. If not,
293 * draw_primitive_immediate_mode() is needed, including a vertex buffer
295 if (state
->load_base_vertex_index
< 0)
297 WARN_(d3d_perf
)("load_base_vertex_index is < 0 (%d), not using VBOs.\n",
298 state
->load_base_vertex_index
);
299 element
->data
.buffer_object
= 0;
300 element
->data
.addr
+= (ULONG_PTR
)wined3d_buffer_load_sysmem(buffer
, context
);
301 if ((UINT_PTR
)element
->data
.addr
< -state
->load_base_vertex_index
* element
->stride
)
302 FIXME("System memory vertex data load offset is negative!\n");
306 wined3d_buffer_load(buffer
, context
, state
);
307 wined3d_buffer_get_memory(buffer
, context
, &data
);
308 element
->data
.buffer_object
= data
.buffer_object
;
309 element
->data
.addr
+= (ULONG_PTR
)data
.addr
;
312 if (!element
->data
.buffer_object
)
313 stream_info
->all_vbo
= 0;
315 TRACE("Load array %u %s.\n", i
, debug_bo_address(&element
->data
));
318 if (prev_all_vbo
!= stream_info
->all_vbo
)
319 context_invalidate_state(context
, STATE_INDEXBUFFER
);
321 context
->use_immediate_mode_draw
= FALSE
;
323 if (stream_info
->all_vbo
)
328 WORD slow_mask
= -!d3d_info
->ffp_generic_attributes
& (1u << WINED3D_FFP_PSIZE
);
329 slow_mask
|= -(!d3d_info
->vertex_bgra
&& !d3d_info
->ffp_generic_attributes
)
330 & ((1u << WINED3D_FFP_DIFFUSE
) | (1u << WINED3D_FFP_SPECULAR
) | (1u << WINED3D_FFP_BLENDWEIGHT
));
332 if ((stream_info
->position_transformed
&& !d3d_info
->xyzrhw
)
333 || (stream_info
->use_map
& slow_mask
))
334 context
->use_immediate_mode_draw
= TRUE
;
338 static bool is_resource_rtv_bound(const struct wined3d_state
*state
,
339 const struct wined3d_resource
*resource
)
343 if (!resource
->rtv_bind_count_device
)
346 for (i
= 0; i
< ARRAY_SIZE(state
->fb
.render_targets
); ++i
)
348 if (state
->fb
.render_targets
[i
] && state
->fb
.render_targets
[i
]->resource
== resource
)
355 /* Context activation is done by the caller. */
356 static void context_preload_texture(struct wined3d_context
*context
,
357 const struct wined3d_state
*state
, unsigned int idx
)
359 struct wined3d_texture
*texture
;
361 if (!(texture
= state
->textures
[idx
]))
364 if (is_resource_rtv_bound(state
, &texture
->resource
)
365 || (state
->fb
.depth_stencil
&& state
->fb
.depth_stencil
->resource
== &texture
->resource
))
366 context
->uses_fbo_attached_resources
= 1;
368 wined3d_texture_load(texture
, context
, is_srgb_enabled(state
->sampler_states
[idx
]));
371 /* Context activation is done by the caller. */
372 void context_preload_textures(struct wined3d_context
*context
, const struct wined3d_state
*state
)
378 for (i
= 0; i
< WINED3D_MAX_VERTEX_SAMPLERS
; ++i
)
380 if (state
->shader
[WINED3D_SHADER_TYPE_VERTEX
]->reg_maps
.resource_info
[i
].type
)
381 context_preload_texture(context
, state
, WINED3D_MAX_FRAGMENT_SAMPLERS
+ i
);
387 for (i
= 0; i
< WINED3D_MAX_FRAGMENT_SAMPLERS
; ++i
)
389 if (state
->shader
[WINED3D_SHADER_TYPE_PIXEL
]->reg_maps
.resource_info
[i
].type
)
390 context_preload_texture(context
, state
, i
);
395 WORD ffu_map
= context
->fixed_function_usage_map
;
397 for (i
= 0; ffu_map
; ffu_map
>>= 1, ++i
)
400 context_preload_texture(context
, state
, i
);