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"
28 #include "vertexbuffer9.h"
29 #include "indexbuffer9.h"
31 #define DBG_CHANNEL DBG_STATEBLOCK
33 /* XXX TODO: handling of lights is broken */
36 NineStateBlock9_ctor( struct NineStateBlock9
*This
,
37 struct NineUnknownParams
*pParams
,
38 enum nine_stateblock_type type
)
40 HRESULT hr
= NineUnknown_ctor(&This
->base
, pParams
);
42 DBG("This=%p pParams=%p type=%d\n", This
, pParams
, type
);
49 This
->state
.vs_const_f
= MALLOC(VS_CONST_F_SIZE(This
->base
.device
));
50 This
->state
.ps_const_f
= MALLOC(This
->base
.device
->ps_const_size
);
51 This
->state
.vs_const_i
= MALLOC(VS_CONST_I_SIZE(This
->base
.device
));
52 This
->state
.vs_const_b
= MALLOC(VS_CONST_B_SIZE(This
->base
.device
));
53 if (!This
->state
.vs_const_f
|| !This
->state
.ps_const_f
||
54 !This
->state
.vs_const_i
|| !This
->state
.vs_const_b
)
61 NineStateBlock9_dtor( struct NineStateBlock9
*This
)
63 struct nine_state
*state
= &This
->state
;
65 struct nine_range_pool
*pool
= &This
->base
.device
->range_pool
;
67 nine_state_clear(state
, false);
69 FREE(state
->vs_const_f
);
70 FREE(state
->ps_const_f
);
71 FREE(state
->vs_const_i
);
72 FREE(state
->vs_const_b
);
74 FREE(state
->ff
.light
);
76 FREE(state
->ff
.transform
);
78 if (This
->state
.changed
.ps_const_f
) {
79 for (r
= This
->state
.changed
.ps_const_f
; r
->next
; r
= r
->next
);
80 nine_range_pool_put_chain(pool
, This
->state
.changed
.ps_const_f
, r
);
82 if (This
->state
.changed
.vs_const_f
) {
83 for (r
= This
->state
.changed
.vs_const_f
; r
->next
; r
= r
->next
);
84 nine_range_pool_put_chain(pool
, This
->state
.changed
.vs_const_f
, r
);
86 if (This
->state
.changed
.vs_const_i
) {
87 for (r
= This
->state
.changed
.vs_const_i
; r
->next
; r
= r
->next
);
88 nine_range_pool_put_chain(pool
, This
->state
.changed
.vs_const_i
, r
);
90 if (This
->state
.changed
.vs_const_b
) {
91 for (r
= This
->state
.changed
.vs_const_b
; r
->next
; r
= r
->next
);
92 nine_range_pool_put_chain(pool
, This
->state
.changed
.vs_const_b
, r
);
95 NineUnknown_dtor(&This
->base
);
99 NineStateBlock9_BindBuffer( struct NineDevice9
*device
,
100 boolean applyToDevice
,
101 struct NineBuffer9
**slot
,
102 struct NineBuffer9
*buf
)
105 NineBindBufferToDevice(device
, slot
, buf
);
107 nine_bind(slot
, buf
);
111 NineStateBlock9_BindTexture( struct NineDevice9
*device
,
112 boolean applyToDevice
,
113 struct NineBaseTexture9
**slot
,
114 struct NineBaseTexture9
*tex
)
117 NineBindTextureToDevice(device
, slot
, tex
);
119 nine_bind(slot
, tex
);
122 /* Copy state marked changed in @mask from @src to @dst.
123 * If @apply is false, updating dst->changed can be omitted.
127 nine_state_copy_common(struct NineDevice9
*device
,
128 struct nine_state
*dst
,
129 struct nine_state
*src
,
130 struct nine_state
*mask
, /* aliases either src or dst */
132 struct nine_range_pool
*pool
)
136 DBG("apply:%d changed.group: %x\n", (int)apply
, (int)mask
->changed
.group
);
138 dst
->changed
.group
|= mask
->changed
.group
;
140 if (mask
->changed
.group
& NINE_STATE_VIEWPORT
)
141 dst
->viewport
= src
->viewport
;
142 if (mask
->changed
.group
& NINE_STATE_SCISSOR
)
143 dst
->scissor
= src
->scissor
;
145 if (mask
->changed
.group
& NINE_STATE_VS
)
146 nine_bind(&dst
->vs
, src
->vs
);
147 if (mask
->changed
.group
& NINE_STATE_PS
)
148 nine_bind(&dst
->ps
, src
->ps
);
152 * Various possibilities for optimization here, like creating a per-SB
153 * constant buffer, or memcmp'ing for changes.
154 * Will do that later depending on what works best for specific apps.
156 * Note: Currently when we apply stateblocks, it's always on the device state.
157 * Should it affect recording stateblocks ? Since it's on device state, there
158 * is no need to copy which ranges are dirty. If it turns out we should affect
159 * recording stateblocks, the info should be copied.
161 if (mask
->changed
.group
& NINE_STATE_VS_CONST
) {
162 struct nine_range
*r
;
163 for (r
= mask
->changed
.vs_const_f
; r
; r
= r
->next
) {
164 memcpy(&dst
->vs_const_f
[r
->bgn
* 4],
165 &src
->vs_const_f
[r
->bgn
* 4],
166 (r
->end
- r
->bgn
) * 4 * sizeof(float));
168 for (r
= mask
->changed
.vs_const_i
; r
; r
= r
->next
) {
169 memcpy(&dst
->vs_const_i
[r
->bgn
* 4],
170 &src
->vs_const_i
[r
->bgn
* 4],
171 (r
->end
- r
->bgn
) * 4 * sizeof(int));
173 for (r
= mask
->changed
.vs_const_b
; r
; r
= r
->next
) {
174 memcpy(&dst
->vs_const_b
[r
->bgn
],
175 &src
->vs_const_b
[r
->bgn
],
176 (r
->end
- r
->bgn
) * sizeof(int));
180 /* Pixel constants. */
181 if (mask
->changed
.group
& NINE_STATE_PS_CONST
) {
182 struct nine_range
*r
;
183 for (r
= mask
->changed
.ps_const_f
; r
; r
= r
->next
) {
184 memcpy(&dst
->ps_const_f
[r
->bgn
* 4],
185 &src
->ps_const_f
[r
->bgn
* 4],
186 (r
->end
- r
->bgn
) * 4 * sizeof(float));
188 if (mask
->changed
.ps_const_i
) {
189 uint16_t m
= mask
->changed
.ps_const_i
;
190 for (i
= ffs(m
) - 1, m
>>= i
; m
; ++i
, m
>>= 1)
192 memcpy(dst
->ps_const_i
[i
], src
->ps_const_i
[i
], 4 * sizeof(int));
194 if (mask
->changed
.ps_const_b
) {
195 uint16_t m
= mask
->changed
.ps_const_b
;
196 for (i
= ffs(m
) - 1, m
>>= i
; m
; ++i
, m
>>= 1)
198 dst
->ps_const_b
[i
] = src
->ps_const_b
[i
];
203 * TODO: Maybe build a list ?
205 for (i
= 0; i
< ARRAY_SIZE(dst
->changed
.rs
); ++i
) {
206 uint32_t m
= mask
->changed
.rs
[i
];
208 dst
->changed
.rs
[i
] |= m
;
210 const int r
= ffs(m
) - 1;
212 DBG("State %d %s = %d\n", i
* 32 + r
, nine_d3drs_to_string(i
* 32 + r
), (int)src
->rs_advertised
[i
* 32 + r
]);
213 dst
->rs_advertised
[i
* 32 + r
] = src
->rs_advertised
[i
* 32 + r
];
219 if (mask
->changed
.ucp
) {
220 DBG("ucp: %x\n", mask
->changed
.ucp
);
221 for (i
= 0; i
< PIPE_MAX_CLIP_PLANES
; ++i
)
222 if (mask
->changed
.ucp
& (1 << i
))
223 memcpy(dst
->clip
.ucp
[i
],
224 src
->clip
.ucp
[i
], sizeof(src
->clip
.ucp
[0]));
226 dst
->changed
.ucp
|= mask
->changed
.ucp
;
230 if (mask
->changed
.group
& NINE_STATE_SAMPLER
) {
231 for (s
= 0; s
< NINE_MAX_SAMPLERS
; ++s
) {
232 if (mask
->changed
.sampler
[s
] == 0x3ffe) {
233 memcpy(&dst
->samp_advertised
[s
], &src
->samp_advertised
[s
], sizeof(dst
->samp_advertised
[s
]));
235 uint32_t m
= mask
->changed
.sampler
[s
];
236 DBG("samp %d: changed = %x\n", i
, (int)m
);
238 const int i
= ffs(m
) - 1;
240 dst
->samp_advertised
[s
][i
] = src
->samp_advertised
[s
][i
];
244 dst
->changed
.sampler
[s
] |= mask
->changed
.sampler
[s
];
249 if (mask
->changed
.group
& NINE_STATE_IDXBUF
)
250 NineStateBlock9_BindBuffer(device
,
252 (struct NineBuffer9
**)&dst
->idxbuf
,
253 (struct NineBuffer9
*)src
->idxbuf
);
255 /* Vertex streams. */
256 if (mask
->changed
.vtxbuf
| mask
->changed
.stream_freq
) {
257 DBG("vtxbuf/stream_freq: %x/%x\n", mask
->changed
.vtxbuf
, mask
->changed
.stream_freq
);
258 uint32_t m
= mask
->changed
.vtxbuf
| mask
->changed
.stream_freq
;
259 for (i
= 0; m
; ++i
, m
>>= 1) {
260 if (mask
->changed
.vtxbuf
& (1 << i
)) {
261 NineStateBlock9_BindBuffer(device
,
263 (struct NineBuffer9
**)&dst
->stream
[i
],
264 (struct NineBuffer9
*)src
->stream
[i
]);
265 if (src
->stream
[i
]) {
266 dst
->vtxbuf
[i
].buffer_offset
= src
->vtxbuf
[i
].buffer_offset
;
267 dst
->vtxbuf
[i
].stride
= src
->vtxbuf
[i
].stride
;
270 if (mask
->changed
.stream_freq
& (1 << i
))
271 dst
->stream_freq
[i
] = src
->stream_freq
[i
];
274 dst
->changed
.vtxbuf
|= mask
->changed
.vtxbuf
;
275 dst
->changed
.stream_freq
|= mask
->changed
.stream_freq
;
280 if (mask
->changed
.texture
) {
281 uint32_t m
= mask
->changed
.texture
;
282 for (s
= 0; m
; ++s
, m
>>= 1)
284 NineStateBlock9_BindTexture(device
, apply
, &dst
->texture
[s
], src
->texture
[s
]);
287 if (!(mask
->changed
.group
& NINE_STATE_FF
))
289 WARN_ONCE("Fixed function state not handled properly by StateBlocks.\n");
291 /* Fixed function state. */
293 if (mask
->changed
.group
& NINE_STATE_FF_MATERIAL
)
294 dst
->ff
.material
= src
->ff
.material
;
296 if (mask
->changed
.group
& NINE_STATE_FF_PSSTAGES
) {
297 for (s
= 0; s
< NINE_MAX_TEXTURE_STAGES
; ++s
) {
298 for (i
= 0; i
< NINED3DTSS_COUNT
; ++i
)
299 if (mask
->ff
.changed
.tex_stage
[s
][i
/ 32] & (1 << (i
% 32)))
300 dst
->ff
.tex_stage
[s
][i
] = src
->ff
.tex_stage
[s
][i
];
302 /* TODO: it's 32 exactly, just offset by 1 as 0 is unused */
303 dst
->ff
.changed
.tex_stage
[s
][0] |=
304 mask
->ff
.changed
.tex_stage
[s
][0];
305 dst
->ff
.changed
.tex_stage
[s
][1] |=
306 mask
->ff
.changed
.tex_stage
[s
][1];
310 if (mask
->changed
.group
& NINE_STATE_FF_LIGHTING
) {
311 unsigned num_lights
= MAX2(dst
->ff
.num_lights
, src
->ff
.num_lights
);
312 /* Can happen in Capture() if device state has created new lights after
313 * the stateblock was created.
314 * Can happen in Apply() if the stateblock had recorded the creation of
316 if (dst
->ff
.num_lights
< num_lights
) {
317 dst
->ff
.light
= REALLOC(dst
->ff
.light
,
318 dst
->ff
.num_lights
* sizeof(D3DLIGHT9
),
319 num_lights
* sizeof(D3DLIGHT9
));
320 memset(&dst
->ff
.light
[dst
->ff
.num_lights
], 0, (num_lights
- dst
->ff
.num_lights
) * sizeof(D3DLIGHT9
));
321 /* if mask == dst, a Type of 0 will trigger
322 * "dst->ff.light[i] = src->ff.light[i];" later,
323 * which is what we want in that case. */
325 for (i
= dst
->ff
.num_lights
; i
< num_lights
; ++i
)
326 dst
->ff
.light
[i
].Type
= (D3DLIGHTTYPE
)NINED3DLIGHT_INVALID
;
328 dst
->ff
.num_lights
= num_lights
;
330 /* Can happen in Capture() if the stateblock had recorded the creation of
332 * Can happen in Apply() if device state has created new lights after
333 * the stateblock was created. */
334 if (src
->ff
.num_lights
< num_lights
) {
335 src
->ff
.light
= REALLOC(src
->ff
.light
,
336 src
->ff
.num_lights
* sizeof(D3DLIGHT9
),
337 num_lights
* sizeof(D3DLIGHT9
));
338 memset(&src
->ff
.light
[src
->ff
.num_lights
], 0, (num_lights
- src
->ff
.num_lights
) * sizeof(D3DLIGHT9
));
339 for (i
= src
->ff
.num_lights
; i
< num_lights
; ++i
)
340 src
->ff
.light
[i
].Type
= (D3DLIGHTTYPE
)NINED3DLIGHT_INVALID
;
341 src
->ff
.num_lights
= num_lights
;
343 /* Note: mask is either src or dst, so at this point src, dst and mask
344 * have num_lights lights. */
345 for (i
= 0; i
< num_lights
; ++i
)
346 if (mask
->ff
.light
[i
].Type
!= NINED3DLIGHT_INVALID
)
347 dst
->ff
.light
[i
] = src
->ff
.light
[i
];
349 memcpy(dst
->ff
.active_light
, src
->ff
.active_light
, sizeof(src
->ff
.active_light
) );
350 dst
->ff
.num_lights_active
= src
->ff
.num_lights_active
;
352 if (mask
->changed
.group
& NINE_STATE_FF_VSTRANSF
) {
353 for (i
= 0; i
< ARRAY_SIZE(mask
->ff
.changed
.transform
); ++i
) {
354 if (!mask
->ff
.changed
.transform
[i
])
356 for (s
= i
* 32; s
< (i
* 32 + 32); ++s
) {
357 if (!(mask
->ff
.changed
.transform
[i
] & (1 << (s
% 32))))
359 *nine_state_access_transform(&dst
->ff
, s
, TRUE
) =
360 *nine_state_access_transform( /* const because !alloc */
361 (struct nine_ff_state
*)&src
->ff
, s
, FALSE
);
364 dst
->ff
.changed
.transform
[i
] |= mask
->ff
.changed
.transform
[i
];
370 nine_state_copy_common_all(struct NineDevice9
*device
,
371 struct nine_state
*dst
,
372 const struct nine_state
*src
,
373 struct nine_state
*help
,
375 struct nine_range_pool
*pool
,
376 const int MaxStreams
)
381 dst
->changed
.group
|= src
->changed
.group
;
383 dst
->viewport
= src
->viewport
;
384 dst
->scissor
= src
->scissor
;
386 nine_bind(&dst
->vs
, src
->vs
);
387 nine_bind(&dst
->ps
, src
->ps
);
391 * Various possibilities for optimization here, like creating a per-SB
392 * constant buffer, or memcmp'ing for changes.
393 * Will do that later depending on what works best for specific apps.
396 memcpy(&dst
->vs_const_f
[0],
397 &src
->vs_const_f
[0], VS_CONST_F_SIZE(device
));
399 memcpy(dst
->vs_const_i
, src
->vs_const_i
, VS_CONST_I_SIZE(device
));
400 memcpy(dst
->vs_const_b
, src
->vs_const_b
, VS_CONST_B_SIZE(device
));
403 /* Pixel constants. */
405 struct nine_range
*r
= help
->changed
.ps_const_f
;
406 memcpy(&dst
->ps_const_f
[0],
407 &src
->ps_const_f
[0], (r
->end
- r
->bgn
) * 4 * sizeof(float));
409 memcpy(dst
->ps_const_i
, src
->ps_const_i
, sizeof(dst
->ps_const_i
));
410 memcpy(dst
->ps_const_b
, src
->ps_const_b
, sizeof(dst
->ps_const_b
));
414 memcpy(dst
->rs_advertised
, src
->rs_advertised
, sizeof(dst
->rs_advertised
));
416 memcpy(dst
->changed
.rs
, src
->changed
.rs
, sizeof(dst
->changed
.rs
));
420 memcpy(&dst
->clip
, &src
->clip
, sizeof(dst
->clip
));
422 dst
->changed
.ucp
= src
->changed
.ucp
;
425 memcpy(dst
->samp_advertised
, src
->samp_advertised
, sizeof(dst
->samp_advertised
));
427 memcpy(dst
->changed
.sampler
,
428 src
->changed
.sampler
, sizeof(dst
->changed
.sampler
));
431 NineStateBlock9_BindBuffer(device
,
433 (struct NineBuffer9
**)&dst
->idxbuf
,
434 (struct NineBuffer9
*)src
->idxbuf
);
436 /* Vertex streams. */
438 for (i
= 0; i
< ARRAY_SIZE(dst
->stream
); ++i
) {
439 NineStateBlock9_BindBuffer(device
,
441 (struct NineBuffer9
**)&dst
->stream
[i
],
442 (struct NineBuffer9
*)src
->stream
[i
]);
443 if (src
->stream
[i
]) {
444 dst
->vtxbuf
[i
].buffer_offset
= src
->vtxbuf
[i
].buffer_offset
;
445 dst
->vtxbuf
[i
].stride
= src
->vtxbuf
[i
].stride
;
447 dst
->stream_freq
[i
] = src
->stream_freq
[i
];
450 dst
->changed
.vtxbuf
= (1ULL << MaxStreams
) - 1;
451 dst
->changed
.stream_freq
= (1ULL << MaxStreams
) - 1;
457 for (i
= 0; i
< NINE_MAX_SAMPLERS
; i
++)
458 NineStateBlock9_BindTexture(device
, apply
, &dst
->texture
[i
], src
->texture
[i
]);
461 /* keep this check in case we want to disable FF */
462 if (!(help
->changed
.group
& NINE_STATE_FF
))
464 WARN_ONCE("Fixed function state not handled properly by StateBlocks.\n");
466 /* Fixed function state. */
467 dst
->ff
.material
= src
->ff
.material
;
469 memcpy(dst
->ff
.tex_stage
, src
->ff
.tex_stage
, sizeof(dst
->ff
.tex_stage
));
470 if (apply
) /* TODO: memset */
471 memcpy(dst
->ff
.changed
.tex_stage
,
472 src
->ff
.changed
.tex_stage
, sizeof(dst
->ff
.changed
.tex_stage
));
476 if (dst
->ff
.num_lights
< src
->ff
.num_lights
) {
477 dst
->ff
.light
= REALLOC(dst
->ff
.light
,
478 dst
->ff
.num_lights
* sizeof(D3DLIGHT9
),
479 src
->ff
.num_lights
* sizeof(D3DLIGHT9
));
480 dst
->ff
.num_lights
= src
->ff
.num_lights
;
482 memcpy(dst
->ff
.light
,
483 src
->ff
.light
, src
->ff
.num_lights
* sizeof(dst
->ff
.light
[0]));
485 memcpy(dst
->ff
.active_light
, src
->ff
.active_light
, sizeof(src
->ff
.active_light
) );
486 dst
->ff
.num_lights_active
= src
->ff
.num_lights_active
;
491 if (dst
->ff
.num_transforms
< src
->ff
.num_transforms
) {
492 dst
->ff
.transform
= REALLOC(dst
->ff
.transform
,
493 dst
->ff
.num_transforms
* sizeof(dst
->ff
.transform
[0]),
494 src
->ff
.num_transforms
* sizeof(src
->ff
.transform
[0]));
495 dst
->ff
.num_transforms
= src
->ff
.num_transforms
;
497 memcpy(dst
->ff
.transform
,
498 src
->ff
.transform
, src
->ff
.num_transforms
* sizeof(D3DMATRIX
));
499 if (apply
) /* TODO: memset */
500 memcpy(dst
->ff
.changed
.transform
,
501 src
->ff
.changed
.transform
, sizeof(dst
->ff
.changed
.transform
));
505 /* Capture those bits of current device state that have been changed between
506 * BeginStateBlock and EndStateBlock.
509 NineStateBlock9_Capture( struct NineStateBlock9
*This
)
511 struct NineDevice9
*device
= This
->base
.device
;
512 struct nine_state
*dst
= &This
->state
;
513 struct nine_state
*src
= &device
->state
;
514 const int MaxStreams
= device
->caps
.MaxStreams
;
516 DBG("This=%p\n", This
);
518 if (This
->type
== NINESBT_ALL
)
519 nine_state_copy_common_all(device
, dst
, src
, dst
, FALSE
, NULL
, MaxStreams
);
521 nine_state_copy_common(device
, dst
, src
, dst
, FALSE
, NULL
);
523 if (dst
->changed
.group
& NINE_STATE_VDECL
)
524 nine_bind(&dst
->vdecl
, src
->vdecl
);
529 /* Set state managed by this StateBlock as current device state. */
531 NineStateBlock9_Apply( struct NineStateBlock9
*This
)
533 struct NineDevice9
*device
= This
->base
.device
;
534 struct nine_state
*dst
= &device
->state
;
535 struct nine_state
*src
= &This
->state
;
536 struct nine_range_pool
*pool
= &device
->range_pool
;
537 const int MaxStreams
= device
->caps
.MaxStreams
;
539 DBG("This=%p\n", This
);
541 if (This
->type
== NINESBT_ALL
)
542 nine_state_copy_common_all(device
, dst
, src
, src
, TRUE
, pool
, MaxStreams
);
544 nine_state_copy_common(device
, dst
, src
, src
, TRUE
, pool
);
546 nine_context_apply_stateblock(device
, src
);
548 if ((src
->changed
.group
& NINE_STATE_VDECL
) && src
->vdecl
)
549 nine_bind(&dst
->vdecl
, src
->vdecl
);
554 IDirect3DStateBlock9Vtbl NineStateBlock9_vtable
= {
555 (void *)NineUnknown_QueryInterface
,
556 (void *)NineUnknown_AddRef
,
557 (void *)NineUnknown_Release
,
558 (void *)NineUnknown_GetDevice
, /* actually part of StateBlock9 iface */
559 (void *)NineStateBlock9_Capture
,
560 (void *)NineStateBlock9_Apply
563 static const GUID
*NineStateBlock9_IIDs
[] = {
564 &IID_IDirect3DStateBlock9
,
570 NineStateBlock9_new( struct NineDevice9
*pDevice
,
571 struct NineStateBlock9
**ppOut
,
572 enum nine_stateblock_type type
)
574 NINE_DEVICE_CHILD_NEW(StateBlock9
, ppOut
, pDevice
, type
);