2 * Copyright 2011 Joakim Sindholt <opensource@zhasha.com>
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * on the rights to use, copy, modify, merge, publish, distribute, sub
8 * license, and/or sell copies of the Software, and to permit persons to whom
9 * the Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
21 * USE OR OTHER DEALINGS IN THE SOFTWARE. */
23 #include "stateblock9.h"
25 #include "basetexture9.h"
26 #include "nine_helpers.h"
27 #include "vertexdeclaration9.h"
29 #define DBG_CHANNEL DBG_STATEBLOCK
31 /* XXX TODO: handling of lights is broken */
34 NineStateBlock9_ctor( struct NineStateBlock9
*This
,
35 struct NineUnknownParams
*pParams
,
36 enum nine_stateblock_type type
)
38 HRESULT hr
= NineUnknown_ctor(&This
->base
, pParams
);
40 DBG("This=%p pParams=%p type=%d\n", This
, pParams
, type
);
47 This
->state
.vs_const_f
= MALLOC(This
->base
.device
->vs_const_size
);
48 This
->state
.ps_const_f
= MALLOC(This
->base
.device
->ps_const_size
);
49 if (!This
->state
.vs_const_f
|| !This
->state
.ps_const_f
)
56 NineStateBlock9_dtor( struct NineStateBlock9
*This
)
58 struct nine_state
*state
= &This
->state
;
60 struct nine_range_pool
*pool
= &This
->base
.device
->range_pool
;
62 nine_state_clear(state
, FALSE
);
64 FREE(state
->vs_const_f
);
65 FREE(state
->ps_const_f
);
67 FREE(state
->ff
.light
);
69 FREE(state
->ff
.transform
);
71 if (This
->state
.changed
.ps_const_f
) {
72 for (r
= This
->state
.changed
.ps_const_f
; r
->next
; r
= r
->next
);
73 nine_range_pool_put_chain(pool
, This
->state
.changed
.ps_const_f
, r
);
75 if (This
->state
.changed
.vs_const_f
) {
76 for (r
= This
->state
.changed
.vs_const_f
; r
->next
; r
= r
->next
);
77 nine_range_pool_put_chain(pool
, This
->state
.changed
.vs_const_f
, r
);
80 NineUnknown_dtor(&This
->base
);
83 /* Copy state marked changed in @mask from @src to @dst.
84 * If @apply is false, updating dst->changed can be omitted.
88 nine_state_copy_common(struct nine_state
*dst
,
89 struct nine_state
*src
,
90 struct nine_state
*mask
, /* aliases either src or dst */
92 struct nine_range_pool
*pool
)
97 dst
->changed
.group
|= mask
->changed
.group
;
99 if (mask
->changed
.group
& NINE_STATE_VIEWPORT
)
100 dst
->viewport
= src
->viewport
;
101 if (mask
->changed
.group
& NINE_STATE_SCISSOR
)
102 dst
->scissor
= src
->scissor
;
104 if (mask
->changed
.group
& NINE_STATE_VS
)
105 nine_bind(&dst
->vs
, src
->vs
);
106 if (mask
->changed
.group
& NINE_STATE_PS
)
107 nine_bind(&dst
->ps
, src
->ps
);
111 * Various possibilities for optimization here, like creating a per-SB
112 * constant buffer, or memcmp'ing for changes.
113 * Will do that later depending on what works best for specific apps.
115 if (mask
->changed
.group
& NINE_STATE_VS_CONST
) {
116 struct nine_range
*r
;
117 for (r
= mask
->changed
.vs_const_f
; r
; r
= r
->next
) {
118 memcpy(&dst
->vs_const_f
[r
->bgn
* 4],
119 &src
->vs_const_f
[r
->bgn
* 4],
120 (r
->end
- r
->bgn
) * 4 * sizeof(float));
122 nine_ranges_insert(&dst
->changed
.vs_const_f
, r
->bgn
, r
->end
,
125 if (mask
->changed
.vs_const_i
) {
126 uint16_t m
= mask
->changed
.vs_const_i
;
127 for (i
= ffs(m
) - 1, m
>>= i
; m
; ++i
, m
>>= 1)
129 memcpy(dst
->vs_const_i
[i
], src
->vs_const_i
[i
], 4 * sizeof(int));
131 dst
->changed
.vs_const_i
|= mask
->changed
.vs_const_i
;
133 if (mask
->changed
.vs_const_b
) {
134 uint16_t m
= mask
->changed
.vs_const_b
;
135 for (i
= ffs(m
) - 1, m
>>= i
; m
; ++i
, m
>>= 1)
137 dst
->vs_const_b
[i
] = src
->vs_const_b
[i
];
139 dst
->changed
.vs_const_b
|= mask
->changed
.vs_const_b
;
143 /* Pixel constants. */
144 if (mask
->changed
.group
& NINE_STATE_PS_CONST
) {
145 struct nine_range
*r
;
146 for (r
= mask
->changed
.ps_const_f
; r
; r
= r
->next
) {
147 memcpy(&dst
->ps_const_f
[r
->bgn
* 4],
148 &src
->ps_const_f
[r
->bgn
* 4],
149 (r
->end
- r
->bgn
) * 4 * sizeof(float));
151 nine_ranges_insert(&dst
->changed
.ps_const_f
, r
->bgn
, r
->end
,
154 if (mask
->changed
.ps_const_i
) {
155 uint16_t m
= mask
->changed
.ps_const_i
;
156 for (i
= ffs(m
) - 1, m
>>= i
; m
; ++i
, m
>>= 1)
158 memcpy(dst
->ps_const_i
[i
], src
->ps_const_i
[i
], 4 * sizeof(int));
160 dst
->changed
.ps_const_i
|= mask
->changed
.ps_const_i
;
162 if (mask
->changed
.ps_const_b
) {
163 uint16_t m
= mask
->changed
.ps_const_b
;
164 for (i
= ffs(m
) - 1, m
>>= i
; m
; ++i
, m
>>= 1)
166 dst
->ps_const_b
[i
] = src
->ps_const_b
[i
];
168 dst
->changed
.ps_const_b
|= mask
->changed
.ps_const_b
;
173 * TODO: Maybe build a list ?
175 for (i
= 0; i
< ARRAY_SIZE(dst
->changed
.rs
); ++i
) {
176 uint32_t m
= mask
->changed
.rs
[i
];
178 dst
->changed
.rs
[i
] |= m
;
180 const int r
= ffs(m
) - 1;
182 dst
->rs
[i
* 32 + r
] = src
->rs
[i
* 32 + r
];
183 dst
->rs_advertised
[i
* 32 + r
] = src
->rs_advertised
[i
* 32 + r
];
189 if (mask
->changed
.ucp
) {
190 for (i
= 0; i
< PIPE_MAX_CLIP_PLANES
; ++i
)
191 if (mask
->changed
.ucp
& (1 << i
))
192 memcpy(dst
->clip
.ucp
[i
],
193 src
->clip
.ucp
[i
], sizeof(src
->clip
.ucp
[0]));
195 dst
->changed
.ucp
|= mask
->changed
.ucp
;
199 if (mask
->changed
.group
& NINE_STATE_SAMPLER
) {
200 for (s
= 0; s
< NINE_MAX_SAMPLERS
; ++s
) {
201 if (mask
->changed
.sampler
[s
] == 0x3ffe) {
202 memcpy(&dst
->samp
[s
], &src
->samp
[s
], sizeof(dst
->samp
[s
]));
204 uint32_t m
= mask
->changed
.sampler
[s
];
206 const int i
= ffs(m
) - 1;
208 dst
->samp
[s
][i
] = src
->samp
[s
][i
];
212 dst
->changed
.sampler
[s
] |= mask
->changed
.sampler
[s
];
217 if (mask
->changed
.group
& NINE_STATE_IDXBUF
)
218 nine_bind(&dst
->idxbuf
, src
->idxbuf
);
220 /* Vertex streams. */
221 if (mask
->changed
.vtxbuf
| mask
->changed
.stream_freq
) {
222 uint32_t m
= mask
->changed
.vtxbuf
| mask
->changed
.stream_freq
;
223 for (i
= 0; m
; ++i
, m
>>= 1) {
224 if (mask
->changed
.vtxbuf
& (1 << i
)) {
225 nine_bind(&dst
->stream
[i
], src
->stream
[i
]);
226 if (src
->stream
[i
]) {
227 dst
->vtxbuf
[i
].buffer_offset
= src
->vtxbuf
[i
].buffer_offset
;
228 pipe_resource_reference(&dst
->vtxbuf
[i
].buffer
, src
->vtxbuf
[i
].buffer
);
229 dst
->vtxbuf
[i
].stride
= src
->vtxbuf
[i
].stride
;
232 if (mask
->changed
.stream_freq
& (1 << i
))
233 dst
->stream_freq
[i
] = src
->stream_freq
[i
];
235 dst
->stream_instancedata_mask
&= ~mask
->changed
.stream_freq
;
236 dst
->stream_instancedata_mask
|=
237 src
->stream_instancedata_mask
& mask
->changed
.stream_freq
;
239 dst
->changed
.vtxbuf
|= mask
->changed
.vtxbuf
;
240 dst
->changed
.stream_freq
|= mask
->changed
.stream_freq
;
244 if (!(mask
->changed
.group
& NINE_STATE_FF
))
246 WARN_ONCE("Fixed function state not handled properly by StateBlocks.\n");
248 /* Fixed function state. */
250 dst
->ff
.changed
.group
|= src
->ff
.changed
.group
;
252 if (mask
->changed
.group
& NINE_STATE_FF_MATERIAL
)
253 dst
->ff
.material
= src
->ff
.material
;
255 if (mask
->changed
.group
& NINE_STATE_FF_PSSTAGES
) {
256 for (s
= 0; s
< NINE_MAX_TEXTURE_STAGES
; ++s
) {
257 for (i
= 0; i
< NINED3DTSS_COUNT
; ++i
)
258 if (mask
->ff
.changed
.tex_stage
[s
][i
/ 32] & (1 << (i
% 32)))
259 dst
->ff
.tex_stage
[s
][i
] = src
->ff
.tex_stage
[s
][i
];
261 /* TODO: it's 32 exactly, just offset by 1 as 0 is unused */
262 dst
->ff
.changed
.tex_stage
[s
][0] |=
263 mask
->ff
.changed
.tex_stage
[s
][0];
264 dst
->ff
.changed
.tex_stage
[s
][1] |=
265 mask
->ff
.changed
.tex_stage
[s
][1];
269 if (mask
->changed
.group
& NINE_STATE_FF_LIGHTING
) {
270 unsigned num_lights
= MAX2(dst
->ff
.num_lights
, src
->ff
.num_lights
);
271 /* Can happen in Capture() if device state has created new lights after
272 * the stateblock was created.
273 * Can happen in Apply() if the stateblock had recorded the creation of
275 if (dst
->ff
.num_lights
< num_lights
) {
276 dst
->ff
.light
= REALLOC(dst
->ff
.light
,
277 dst
->ff
.num_lights
* sizeof(D3DLIGHT9
),
278 num_lights
* sizeof(D3DLIGHT9
));
279 memset(&dst
->ff
.light
[dst
->ff
.num_lights
], 0, (num_lights
- dst
->ff
.num_lights
) * sizeof(D3DLIGHT9
));
280 /* if mask == dst, a Type of 0 will trigger
281 * "dst->ff.light[i] = src->ff.light[i];" later,
282 * which is what we want in that case. */
284 for (i
= src
->ff
.num_lights
; i
< num_lights
; ++i
)
285 src
->ff
.light
[i
].Type
= (D3DLIGHTTYPE
)NINED3DLIGHT_INVALID
;
287 dst
->ff
.num_lights
= num_lights
;
289 /* Can happen in Capture() if the stateblock had recorded the creation of
291 * Can happen in Apply() if device state has created new lights after
292 * the stateblock was created. */
293 if (src
->ff
.num_lights
< num_lights
) {
294 src
->ff
.light
= REALLOC(src
->ff
.light
,
295 src
->ff
.num_lights
* sizeof(D3DLIGHT9
),
296 num_lights
* sizeof(D3DLIGHT9
));
297 memset(&src
->ff
.light
[src
->ff
.num_lights
], 0, (num_lights
- src
->ff
.num_lights
) * sizeof(D3DLIGHT9
));
298 for (i
= src
->ff
.num_lights
; i
< num_lights
; ++i
)
299 src
->ff
.light
[i
].Type
= (D3DLIGHTTYPE
)NINED3DLIGHT_INVALID
;
300 src
->ff
.num_lights
= num_lights
;
302 /* Note: mask is either src or dst, so at this point src, dst and mask
303 * have num_lights lights. */
304 for (i
= 0; i
< num_lights
; ++i
)
305 if (mask
->ff
.light
[i
].Type
!= NINED3DLIGHT_INVALID
)
306 dst
->ff
.light
[i
] = src
->ff
.light
[i
];
308 memcpy(dst
->ff
.active_light
, src
->ff
.active_light
, sizeof(src
->ff
.active_light
) );
309 dst
->ff
.num_lights_active
= src
->ff
.num_lights_active
;
311 if (mask
->changed
.group
& NINE_STATE_FF_VSTRANSF
) {
312 for (i
= 0; i
< ARRAY_SIZE(mask
->ff
.changed
.transform
); ++i
) {
313 if (!mask
->ff
.changed
.transform
[i
])
315 for (s
= i
* 32; s
< (i
* 32 + 32); ++s
) {
316 if (!(mask
->ff
.changed
.transform
[i
] & (1 << (s
% 32))))
318 *nine_state_access_transform(dst
, s
, TRUE
) =
319 *nine_state_access_transform( /* const because !alloc */
320 (struct nine_state
*)src
, s
, FALSE
);
323 dst
->ff
.changed
.transform
[i
] |= mask
->ff
.changed
.transform
[i
];
329 nine_state_copy_common_all(struct nine_state
*dst
,
330 const struct nine_state
*src
,
331 struct nine_state
*help
,
333 struct nine_range_pool
*pool
,
334 const int MaxStreams
)
339 dst
->changed
.group
|= src
->changed
.group
;
341 dst
->viewport
= src
->viewport
;
342 dst
->scissor
= src
->scissor
;
344 nine_bind(&dst
->vs
, src
->vs
);
345 nine_bind(&dst
->ps
, src
->ps
);
349 * Various possibilities for optimization here, like creating a per-SB
350 * constant buffer, or memcmp'ing for changes.
351 * Will do that later depending on what works best for specific apps.
354 struct nine_range
*r
= help
->changed
.vs_const_f
;
355 memcpy(&dst
->vs_const_f
[0],
356 &src
->vs_const_f
[0], (r
->end
- r
->bgn
) * 4 * sizeof(float));
358 nine_ranges_insert(&dst
->changed
.vs_const_f
, r
->bgn
, r
->end
, pool
);
360 memcpy(dst
->vs_const_i
, src
->vs_const_i
, sizeof(dst
->vs_const_i
));
361 memcpy(dst
->vs_const_b
, src
->vs_const_b
, sizeof(dst
->vs_const_b
));
363 dst
->changed
.vs_const_i
|= src
->changed
.vs_const_i
;
364 dst
->changed
.vs_const_b
|= src
->changed
.vs_const_b
;
368 /* Pixel constants. */
370 struct nine_range
*r
= help
->changed
.ps_const_f
;
371 memcpy(&dst
->ps_const_f
[0],
372 &src
->ps_const_f
[0], (r
->end
- r
->bgn
) * 4 * sizeof(float));
374 nine_ranges_insert(&dst
->changed
.ps_const_f
, r
->bgn
, r
->end
, pool
);
376 memcpy(dst
->ps_const_i
, src
->ps_const_i
, sizeof(dst
->ps_const_i
));
377 memcpy(dst
->ps_const_b
, src
->ps_const_b
, sizeof(dst
->ps_const_b
));
379 dst
->changed
.ps_const_i
|= src
->changed
.ps_const_i
;
380 dst
->changed
.ps_const_b
|= src
->changed
.ps_const_b
;
385 memcpy(dst
->rs
, src
->rs
, sizeof(dst
->rs
));
386 memcpy(dst
->rs_advertised
, src
->rs_advertised
, sizeof(dst
->rs_advertised
));
388 memcpy(dst
->changed
.rs
, src
->changed
.rs
, sizeof(dst
->changed
.rs
));
392 memcpy(&dst
->clip
, &src
->clip
, sizeof(dst
->clip
));
394 dst
->changed
.ucp
= src
->changed
.ucp
;
397 memcpy(dst
->samp
, src
->samp
, sizeof(dst
->samp
));
399 memcpy(dst
->changed
.sampler
,
400 src
->changed
.sampler
, sizeof(dst
->changed
.sampler
));
403 nine_bind(&dst
->idxbuf
, src
->idxbuf
);
405 /* Vertex streams. */
407 for (i
= 0; i
< ARRAY_SIZE(dst
->stream
); ++i
) {
408 nine_bind(&dst
->stream
[i
], src
->stream
[i
]);
409 if (src
->stream
[i
]) {
410 dst
->vtxbuf
[i
].buffer_offset
= src
->vtxbuf
[i
].buffer_offset
;
411 pipe_resource_reference(&dst
->vtxbuf
[i
].buffer
, src
->vtxbuf
[i
].buffer
);
412 dst
->vtxbuf
[i
].stride
= src
->vtxbuf
[i
].stride
;
414 dst
->stream_freq
[i
] = src
->stream_freq
[i
];
416 dst
->stream_instancedata_mask
= src
->stream_instancedata_mask
;
418 dst
->changed
.vtxbuf
= (1ULL << MaxStreams
) - 1;
419 dst
->changed
.stream_freq
= (1ULL << MaxStreams
) - 1;
423 /* keep this check in case we want to disable FF */
424 if (!(help
->changed
.group
& NINE_STATE_FF
))
426 WARN_ONCE("Fixed function state not handled properly by StateBlocks.\n");
428 /* Fixed function state. */
430 dst
->ff
.changed
.group
= src
->ff
.changed
.group
;
432 dst
->ff
.material
= src
->ff
.material
;
434 memcpy(dst
->ff
.tex_stage
, src
->ff
.tex_stage
, sizeof(dst
->ff
.tex_stage
));
435 if (apply
) /* TODO: memset */
436 memcpy(dst
->ff
.changed
.tex_stage
,
437 src
->ff
.changed
.tex_stage
, sizeof(dst
->ff
.changed
.tex_stage
));
441 if (dst
->ff
.num_lights
< src
->ff
.num_lights
) {
442 dst
->ff
.light
= REALLOC(dst
->ff
.light
,
443 dst
->ff
.num_lights
* sizeof(D3DLIGHT9
),
444 src
->ff
.num_lights
* sizeof(D3DLIGHT9
));
445 dst
->ff
.num_lights
= src
->ff
.num_lights
;
447 memcpy(dst
->ff
.light
,
448 src
->ff
.light
, src
->ff
.num_lights
* sizeof(dst
->ff
.light
[0]));
450 memcpy(dst
->ff
.active_light
, src
->ff
.active_light
, sizeof(src
->ff
.active_light
) );
451 dst
->ff
.num_lights_active
= src
->ff
.num_lights_active
;
456 if (dst
->ff
.num_transforms
< src
->ff
.num_transforms
) {
457 dst
->ff
.transform
= REALLOC(dst
->ff
.transform
,
458 dst
->ff
.num_transforms
* sizeof(dst
->ff
.transform
[0]),
459 src
->ff
.num_transforms
* sizeof(src
->ff
.transform
[0]));
460 dst
->ff
.num_transforms
= src
->ff
.num_transforms
;
462 memcpy(dst
->ff
.transform
,
463 src
->ff
.transform
, src
->ff
.num_transforms
* sizeof(D3DMATRIX
));
464 if (apply
) /* TODO: memset */
465 memcpy(dst
->ff
.changed
.transform
,
466 src
->ff
.changed
.transform
, sizeof(dst
->ff
.changed
.transform
));
470 /* Capture those bits of current device state that have been changed between
471 * BeginStateBlock and EndStateBlock.
474 NineStateBlock9_Capture( struct NineStateBlock9
*This
)
476 struct nine_state
*dst
= &This
->state
;
477 struct nine_state
*src
= &This
->base
.device
->state
;
478 const int MaxStreams
= This
->base
.device
->caps
.MaxStreams
;
481 DBG("This=%p\n", This
);
483 if (This
->type
== NINESBT_ALL
)
484 nine_state_copy_common_all(dst
, src
, dst
, FALSE
, NULL
, MaxStreams
);
486 nine_state_copy_common(dst
, src
, dst
, FALSE
, NULL
);
488 if (dst
->changed
.group
& NINE_STATE_VDECL
)
489 nine_bind(&dst
->vdecl
, src
->vdecl
);
492 if (dst
->changed
.texture
) {
493 uint32_t m
= dst
->changed
.texture
;
494 for (s
= 0; m
; ++s
, m
>>= 1)
496 nine_bind(&dst
->texture
[s
], src
->texture
[s
]);
502 /* Set state managed by this StateBlock as current device state. */
504 NineStateBlock9_Apply( struct NineStateBlock9
*This
)
506 struct nine_state
*dst
= &This
->base
.device
->state
;
507 struct nine_state
*src
= &This
->state
;
508 struct nine_range_pool
*pool
= &This
->base
.device
->range_pool
;
509 const int MaxStreams
= This
->base
.device
->caps
.MaxStreams
;
512 DBG("This=%p\n", This
);
514 if (This
->type
== NINESBT_ALL
)
515 nine_state_copy_common_all(dst
, src
, src
, TRUE
, pool
, MaxStreams
);
517 nine_state_copy_common(dst
, src
, src
, TRUE
, pool
);
519 if ((src
->changed
.group
& NINE_STATE_VDECL
) && src
->vdecl
)
520 NineDevice9_SetVertexDeclaration(This
->base
.device
, (IDirect3DVertexDeclaration9
*)src
->vdecl
);
522 /* Recomputing it is needed if we changed vs but not vdecl */
523 dst
->programmable_vs
= dst
->vs
&& !(dst
->vdecl
&& dst
->vdecl
->position_t
);
526 if (src
->changed
.texture
) {
527 uint32_t m
= src
->changed
.texture
;
528 dst
->changed
.texture
|= m
;
530 dst
->samplers_shadow
&= ~m
;
532 for (s
= 0; m
; ++s
, m
>>= 1) {
533 struct NineBaseTexture9
*tex
= src
->texture
[s
];
538 if ((tex
->managed
.dirty
| tex
->dirty_mip
) && LIST_IS_EMPTY(&tex
->list
))
539 list_add(&tex
->list
, &This
->base
.device
->update_textures
);
540 dst
->samplers_shadow
|= tex
->shadow
<< s
;
543 src
->texture
[s
]->bind_count
--;
544 nine_bind(&dst
->texture
[s
], src
->texture
[s
]);
551 IDirect3DStateBlock9Vtbl NineStateBlock9_vtable
= {
552 (void *)NineUnknown_QueryInterface
,
553 (void *)NineUnknown_AddRef
,
554 (void *)NineUnknown_Release
,
555 (void *)NineUnknown_GetDevice
, /* actually part of StateBlock9 iface */
556 (void *)NineStateBlock9_Capture
,
557 (void *)NineStateBlock9_Apply
560 static const GUID
*NineStateBlock9_IIDs
[] = {
561 &IID_IDirect3DStateBlock9
,
567 NineStateBlock9_new( struct NineDevice9
*pDevice
,
568 struct NineStateBlock9
**ppOut
,
569 enum nine_stateblock_type type
)
571 NINE_DEVICE_CHILD_NEW(StateBlock9
, ppOut
, pDevice
, type
);