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(VS_CONST_F_SIZE(This
->base
.device
));
48 This
->state
.ps_const_f
= MALLOC(This
->base
.device
->ps_const_size
);
49 This
->state
.vs_const_i
= MALLOC(VS_CONST_I_SIZE(This
->base
.device
));
50 This
->state
.vs_const_b
= MALLOC(VS_CONST_B_SIZE(This
->base
.device
));
51 if (!This
->state
.vs_const_f
|| !This
->state
.ps_const_f
||
52 !This
->state
.vs_const_i
|| !This
->state
.vs_const_b
)
59 NineStateBlock9_dtor( struct NineStateBlock9
*This
)
61 struct nine_state
*state
= &This
->state
;
63 struct nine_range_pool
*pool
= &This
->base
.device
->range_pool
;
65 nine_state_clear(state
, false);
67 FREE(state
->vs_const_f
);
68 FREE(state
->ps_const_f
);
69 FREE(state
->vs_const_i
);
70 FREE(state
->vs_const_b
);
72 FREE(state
->ff
.light
);
74 FREE(state
->ff
.transform
);
76 if (This
->state
.changed
.ps_const_f
) {
77 for (r
= This
->state
.changed
.ps_const_f
; r
->next
; r
= r
->next
);
78 nine_range_pool_put_chain(pool
, This
->state
.changed
.ps_const_f
, r
);
80 if (This
->state
.changed
.vs_const_f
) {
81 for (r
= This
->state
.changed
.vs_const_f
; r
->next
; r
= r
->next
);
82 nine_range_pool_put_chain(pool
, This
->state
.changed
.vs_const_f
, r
);
84 if (This
->state
.changed
.vs_const_i
) {
85 for (r
= This
->state
.changed
.vs_const_i
; r
->next
; r
= r
->next
);
86 nine_range_pool_put_chain(pool
, This
->state
.changed
.vs_const_i
, r
);
88 if (This
->state
.changed
.vs_const_b
) {
89 for (r
= This
->state
.changed
.vs_const_b
; r
->next
; r
= r
->next
);
90 nine_range_pool_put_chain(pool
, This
->state
.changed
.vs_const_b
, r
);
93 NineUnknown_dtor(&This
->base
);
96 /* Copy state marked changed in @mask from @src to @dst.
97 * If @apply is false, updating dst->changed can be omitted.
101 nine_state_copy_common(struct NineDevice9
*device
,
102 struct nine_state
*dst
,
103 struct nine_state
*src
,
104 struct nine_state
*mask
, /* aliases either src or dst */
106 struct nine_range_pool
*pool
)
110 DBG("apply:%d changed.group: %x\n", (int)apply
, (int)mask
->changed
.group
);
112 dst
->changed
.group
|= mask
->changed
.group
;
114 if (mask
->changed
.group
& NINE_STATE_VIEWPORT
)
115 dst
->viewport
= src
->viewport
;
116 if (mask
->changed
.group
& NINE_STATE_SCISSOR
)
117 dst
->scissor
= src
->scissor
;
119 if (mask
->changed
.group
& NINE_STATE_VS
)
120 nine_bind(&dst
->vs
, src
->vs
);
121 if (mask
->changed
.group
& NINE_STATE_PS
)
122 nine_bind(&dst
->ps
, src
->ps
);
126 * Various possibilities for optimization here, like creating a per-SB
127 * constant buffer, or memcmp'ing for changes.
128 * Will do that later depending on what works best for specific apps.
130 * Note: Currently when we apply stateblocks, it's always on the device state.
131 * Should it affect recording stateblocks ? Since it's on device state, there
132 * is no need to copy which ranges are dirty. If it turns out we should affect
133 * recording stateblocks, the info should be copied.
135 if (mask
->changed
.group
& NINE_STATE_VS_CONST
) {
136 struct nine_range
*r
;
137 for (r
= mask
->changed
.vs_const_f
; r
; r
= r
->next
) {
138 memcpy(&dst
->vs_const_f
[r
->bgn
* 4],
139 &src
->vs_const_f
[r
->bgn
* 4],
140 (r
->end
- r
->bgn
) * 4 * sizeof(float));
142 for (r
= mask
->changed
.vs_const_i
; r
; r
= r
->next
) {
143 memcpy(&dst
->vs_const_i
[r
->bgn
* 4],
144 &src
->vs_const_i
[r
->bgn
* 4],
145 (r
->end
- r
->bgn
) * 4 * sizeof(int));
147 for (r
= mask
->changed
.vs_const_b
; r
; r
= r
->next
) {
148 memcpy(&dst
->vs_const_b
[r
->bgn
],
149 &src
->vs_const_b
[r
->bgn
],
150 (r
->end
- r
->bgn
) * sizeof(int));
154 /* Pixel constants. */
155 if (mask
->changed
.group
& NINE_STATE_PS_CONST
) {
156 struct nine_range
*r
;
157 for (r
= mask
->changed
.ps_const_f
; r
; r
= r
->next
) {
158 memcpy(&dst
->ps_const_f
[r
->bgn
* 4],
159 &src
->ps_const_f
[r
->bgn
* 4],
160 (r
->end
- r
->bgn
) * 4 * sizeof(float));
162 if (mask
->changed
.ps_const_i
) {
163 uint16_t m
= mask
->changed
.ps_const_i
;
164 for (i
= ffs(m
) - 1, m
>>= i
; m
; ++i
, m
>>= 1)
166 memcpy(dst
->ps_const_i
[i
], src
->ps_const_i
[i
], 4 * sizeof(int));
168 if (mask
->changed
.ps_const_b
) {
169 uint16_t m
= mask
->changed
.ps_const_b
;
170 for (i
= ffs(m
) - 1, m
>>= i
; m
; ++i
, m
>>= 1)
172 dst
->ps_const_b
[i
] = src
->ps_const_b
[i
];
177 * TODO: Maybe build a list ?
179 for (i
= 0; i
< ARRAY_SIZE(dst
->changed
.rs
); ++i
) {
180 uint32_t m
= mask
->changed
.rs
[i
];
182 dst
->changed
.rs
[i
] |= m
;
184 const int r
= ffs(m
) - 1;
186 DBG("State %d %s = %d\n", i
* 32 + r
, nine_d3drs_to_string(i
* 32 + r
), (int)src
->rs_advertised
[i
* 32 + r
]);
187 dst
->rs_advertised
[i
* 32 + r
] = src
->rs_advertised
[i
* 32 + r
];
193 if (mask
->changed
.ucp
) {
194 DBG("ucp: %x\n", mask
->changed
.ucp
);
195 for (i
= 0; i
< PIPE_MAX_CLIP_PLANES
; ++i
)
196 if (mask
->changed
.ucp
& (1 << i
))
197 memcpy(dst
->clip
.ucp
[i
],
198 src
->clip
.ucp
[i
], sizeof(src
->clip
.ucp
[0]));
200 dst
->changed
.ucp
|= mask
->changed
.ucp
;
204 if (mask
->changed
.group
& NINE_STATE_SAMPLER
) {
205 for (s
= 0; s
< NINE_MAX_SAMPLERS
; ++s
) {
206 if (mask
->changed
.sampler
[s
] == 0x3ffe) {
207 memcpy(&dst
->samp_advertised
[s
], &src
->samp_advertised
[s
], sizeof(dst
->samp_advertised
[s
]));
209 uint32_t m
= mask
->changed
.sampler
[s
];
210 DBG("samp %d: changed = %x\n", i
, (int)m
);
212 const int i
= ffs(m
) - 1;
214 dst
->samp_advertised
[s
][i
] = src
->samp_advertised
[s
][i
];
218 dst
->changed
.sampler
[s
] |= mask
->changed
.sampler
[s
];
223 if (mask
->changed
.group
& NINE_STATE_IDXBUF
)
224 nine_bind(&dst
->idxbuf
, src
->idxbuf
);
226 /* Vertex streams. */
227 if (mask
->changed
.vtxbuf
| mask
->changed
.stream_freq
) {
228 DBG("vtxbuf/stream_freq: %x/%x\n", mask
->changed
.vtxbuf
, mask
->changed
.stream_freq
);
229 uint32_t m
= mask
->changed
.vtxbuf
| mask
->changed
.stream_freq
;
230 for (i
= 0; m
; ++i
, m
>>= 1) {
231 if (mask
->changed
.vtxbuf
& (1 << i
)) {
232 nine_bind(&dst
->stream
[i
], src
->stream
[i
]);
233 if (src
->stream
[i
]) {
234 dst
->vtxbuf
[i
].buffer_offset
= src
->vtxbuf
[i
].buffer_offset
;
235 dst
->vtxbuf
[i
].stride
= src
->vtxbuf
[i
].stride
;
238 if (mask
->changed
.stream_freq
& (1 << i
))
239 dst
->stream_freq
[i
] = src
->stream_freq
[i
];
242 dst
->changed
.vtxbuf
|= mask
->changed
.vtxbuf
;
243 dst
->changed
.stream_freq
|= mask
->changed
.stream_freq
;
247 if (!(mask
->changed
.group
& NINE_STATE_FF
))
249 WARN_ONCE("Fixed function state not handled properly by StateBlocks.\n");
251 /* Fixed function state. */
253 if (mask
->changed
.group
& NINE_STATE_FF_MATERIAL
)
254 dst
->ff
.material
= src
->ff
.material
;
256 if (mask
->changed
.group
& NINE_STATE_FF_PSSTAGES
) {
257 for (s
= 0; s
< NINE_MAX_TEXTURE_STAGES
; ++s
) {
258 for (i
= 0; i
< NINED3DTSS_COUNT
; ++i
)
259 if (mask
->ff
.changed
.tex_stage
[s
][i
/ 32] & (1 << (i
% 32)))
260 dst
->ff
.tex_stage
[s
][i
] = src
->ff
.tex_stage
[s
][i
];
262 /* TODO: it's 32 exactly, just offset by 1 as 0 is unused */
263 dst
->ff
.changed
.tex_stage
[s
][0] |=
264 mask
->ff
.changed
.tex_stage
[s
][0];
265 dst
->ff
.changed
.tex_stage
[s
][1] |=
266 mask
->ff
.changed
.tex_stage
[s
][1];
270 if (mask
->changed
.group
& NINE_STATE_FF_LIGHTING
) {
271 unsigned num_lights
= MAX2(dst
->ff
.num_lights
, src
->ff
.num_lights
);
272 /* Can happen in Capture() if device state has created new lights after
273 * the stateblock was created.
274 * Can happen in Apply() if the stateblock had recorded the creation of
276 if (dst
->ff
.num_lights
< num_lights
) {
277 dst
->ff
.light
= REALLOC(dst
->ff
.light
,
278 dst
->ff
.num_lights
* sizeof(D3DLIGHT9
),
279 num_lights
* sizeof(D3DLIGHT9
));
280 memset(&dst
->ff
.light
[dst
->ff
.num_lights
], 0, (num_lights
- dst
->ff
.num_lights
) * sizeof(D3DLIGHT9
));
281 /* if mask == dst, a Type of 0 will trigger
282 * "dst->ff.light[i] = src->ff.light[i];" later,
283 * which is what we want in that case. */
285 for (i
= dst
->ff
.num_lights
; i
< num_lights
; ++i
)
286 dst
->ff
.light
[i
].Type
= (D3DLIGHTTYPE
)NINED3DLIGHT_INVALID
;
288 dst
->ff
.num_lights
= num_lights
;
290 /* Can happen in Capture() if the stateblock had recorded the creation of
292 * Can happen in Apply() if device state has created new lights after
293 * the stateblock was created. */
294 if (src
->ff
.num_lights
< num_lights
) {
295 src
->ff
.light
= REALLOC(src
->ff
.light
,
296 src
->ff
.num_lights
* sizeof(D3DLIGHT9
),
297 num_lights
* sizeof(D3DLIGHT9
));
298 memset(&src
->ff
.light
[src
->ff
.num_lights
], 0, (num_lights
- src
->ff
.num_lights
) * sizeof(D3DLIGHT9
));
299 for (i
= src
->ff
.num_lights
; i
< num_lights
; ++i
)
300 src
->ff
.light
[i
].Type
= (D3DLIGHTTYPE
)NINED3DLIGHT_INVALID
;
301 src
->ff
.num_lights
= num_lights
;
303 /* Note: mask is either src or dst, so at this point src, dst and mask
304 * have num_lights lights. */
305 for (i
= 0; i
< num_lights
; ++i
)
306 if (mask
->ff
.light
[i
].Type
!= NINED3DLIGHT_INVALID
)
307 dst
->ff
.light
[i
] = src
->ff
.light
[i
];
309 memcpy(dst
->ff
.active_light
, src
->ff
.active_light
, sizeof(src
->ff
.active_light
) );
310 dst
->ff
.num_lights_active
= src
->ff
.num_lights_active
;
312 if (mask
->changed
.group
& NINE_STATE_FF_VSTRANSF
) {
313 for (i
= 0; i
< ARRAY_SIZE(mask
->ff
.changed
.transform
); ++i
) {
314 if (!mask
->ff
.changed
.transform
[i
])
316 for (s
= i
* 32; s
< (i
* 32 + 32); ++s
) {
317 if (!(mask
->ff
.changed
.transform
[i
] & (1 << (s
% 32))))
319 *nine_state_access_transform(dst
, s
, TRUE
) =
320 *nine_state_access_transform( /* const because !alloc */
321 (struct nine_state
*)src
, s
, FALSE
);
324 dst
->ff
.changed
.transform
[i
] |= mask
->ff
.changed
.transform
[i
];
330 nine_state_copy_common_all(struct NineDevice9
*device
,
331 struct nine_state
*dst
,
332 const struct nine_state
*src
,
333 struct nine_state
*help
,
335 struct nine_range_pool
*pool
,
336 const int MaxStreams
)
341 dst
->changed
.group
|= src
->changed
.group
;
343 dst
->viewport
= src
->viewport
;
344 dst
->scissor
= src
->scissor
;
346 nine_bind(&dst
->vs
, src
->vs
);
347 nine_bind(&dst
->ps
, src
->ps
);
351 * Various possibilities for optimization here, like creating a per-SB
352 * constant buffer, or memcmp'ing for changes.
353 * Will do that later depending on what works best for specific apps.
356 memcpy(&dst
->vs_const_f
[0],
357 &src
->vs_const_f
[0], VS_CONST_F_SIZE(device
));
359 memcpy(dst
->vs_const_i
, src
->vs_const_i
, VS_CONST_I_SIZE(device
));
360 memcpy(dst
->vs_const_b
, src
->vs_const_b
, VS_CONST_B_SIZE(device
));
363 /* Pixel constants. */
365 struct nine_range
*r
= help
->changed
.ps_const_f
;
366 memcpy(&dst
->ps_const_f
[0],
367 &src
->ps_const_f
[0], (r
->end
- r
->bgn
) * 4 * sizeof(float));
369 memcpy(dst
->ps_const_i
, src
->ps_const_i
, sizeof(dst
->ps_const_i
));
370 memcpy(dst
->ps_const_b
, src
->ps_const_b
, sizeof(dst
->ps_const_b
));
374 memcpy(dst
->rs_advertised
, src
->rs_advertised
, sizeof(dst
->rs_advertised
));
376 memcpy(dst
->changed
.rs
, src
->changed
.rs
, sizeof(dst
->changed
.rs
));
380 memcpy(&dst
->clip
, &src
->clip
, sizeof(dst
->clip
));
382 dst
->changed
.ucp
= src
->changed
.ucp
;
385 memcpy(dst
->samp_advertised
, src
->samp_advertised
, sizeof(dst
->samp_advertised
));
387 memcpy(dst
->changed
.sampler
,
388 src
->changed
.sampler
, sizeof(dst
->changed
.sampler
));
391 nine_bind(&dst
->idxbuf
, src
->idxbuf
);
393 /* Vertex streams. */
395 for (i
= 0; i
< ARRAY_SIZE(dst
->stream
); ++i
) {
396 nine_bind(&dst
->stream
[i
], src
->stream
[i
]);
397 if (src
->stream
[i
]) {
398 dst
->vtxbuf
[i
].buffer_offset
= src
->vtxbuf
[i
].buffer_offset
;
399 dst
->vtxbuf
[i
].stride
= src
->vtxbuf
[i
].stride
;
401 dst
->stream_freq
[i
] = src
->stream_freq
[i
];
404 dst
->changed
.vtxbuf
= (1ULL << MaxStreams
) - 1;
405 dst
->changed
.stream_freq
= (1ULL << MaxStreams
) - 1;
409 /* keep this check in case we want to disable FF */
410 if (!(help
->changed
.group
& NINE_STATE_FF
))
412 WARN_ONCE("Fixed function state not handled properly by StateBlocks.\n");
414 /* Fixed function state. */
415 dst
->ff
.material
= src
->ff
.material
;
417 memcpy(dst
->ff
.tex_stage
, src
->ff
.tex_stage
, sizeof(dst
->ff
.tex_stage
));
418 if (apply
) /* TODO: memset */
419 memcpy(dst
->ff
.changed
.tex_stage
,
420 src
->ff
.changed
.tex_stage
, sizeof(dst
->ff
.changed
.tex_stage
));
424 if (dst
->ff
.num_lights
< src
->ff
.num_lights
) {
425 dst
->ff
.light
= REALLOC(dst
->ff
.light
,
426 dst
->ff
.num_lights
* sizeof(D3DLIGHT9
),
427 src
->ff
.num_lights
* sizeof(D3DLIGHT9
));
428 dst
->ff
.num_lights
= src
->ff
.num_lights
;
430 memcpy(dst
->ff
.light
,
431 src
->ff
.light
, src
->ff
.num_lights
* sizeof(dst
->ff
.light
[0]));
433 memcpy(dst
->ff
.active_light
, src
->ff
.active_light
, sizeof(src
->ff
.active_light
) );
434 dst
->ff
.num_lights_active
= src
->ff
.num_lights_active
;
439 if (dst
->ff
.num_transforms
< src
->ff
.num_transforms
) {
440 dst
->ff
.transform
= REALLOC(dst
->ff
.transform
,
441 dst
->ff
.num_transforms
* sizeof(dst
->ff
.transform
[0]),
442 src
->ff
.num_transforms
* sizeof(src
->ff
.transform
[0]));
443 dst
->ff
.num_transforms
= src
->ff
.num_transforms
;
445 memcpy(dst
->ff
.transform
,
446 src
->ff
.transform
, src
->ff
.num_transforms
* sizeof(D3DMATRIX
));
447 if (apply
) /* TODO: memset */
448 memcpy(dst
->ff
.changed
.transform
,
449 src
->ff
.changed
.transform
, sizeof(dst
->ff
.changed
.transform
));
453 /* Capture those bits of current device state that have been changed between
454 * BeginStateBlock and EndStateBlock.
457 NineStateBlock9_Capture( struct NineStateBlock9
*This
)
459 struct NineDevice9
*device
= This
->base
.device
;
460 struct nine_state
*dst
= &This
->state
;
461 struct nine_state
*src
= &device
->state
;
462 const int MaxStreams
= device
->caps
.MaxStreams
;
465 DBG("This=%p\n", This
);
467 if (This
->type
== NINESBT_ALL
)
468 nine_state_copy_common_all(device
, dst
, src
, dst
, FALSE
, NULL
, MaxStreams
);
470 nine_state_copy_common(device
, dst
, src
, dst
, FALSE
, NULL
);
472 if (dst
->changed
.group
& NINE_STATE_VDECL
)
473 nine_bind(&dst
->vdecl
, src
->vdecl
);
476 if (dst
->changed
.texture
) {
477 uint32_t m
= dst
->changed
.texture
;
478 for (s
= 0; m
; ++s
, m
>>= 1)
480 nine_bind(&dst
->texture
[s
], src
->texture
[s
]);
486 /* Set state managed by this StateBlock as current device state. */
488 NineStateBlock9_Apply( struct NineStateBlock9
*This
)
490 struct NineDevice9
*device
= This
->base
.device
;
491 struct nine_state
*dst
= &device
->state
;
492 struct nine_state
*src
= &This
->state
;
493 struct nine_range_pool
*pool
= &device
->range_pool
;
494 const int MaxStreams
= device
->caps
.MaxStreams
;
497 DBG("This=%p\n", This
);
499 if (This
->type
== NINESBT_ALL
)
500 nine_state_copy_common_all(device
, dst
, src
, src
, TRUE
, pool
, MaxStreams
);
502 nine_state_copy_common(device
, dst
, src
, src
, TRUE
, pool
);
504 nine_context_apply_stateblock(device
, src
);
506 if ((src
->changed
.group
& NINE_STATE_VDECL
) && src
->vdecl
)
507 nine_bind(&dst
->vdecl
, src
->vdecl
);
510 if (src
->changed
.texture
) {
511 uint32_t m
= src
->changed
.texture
;
513 for (s
= 0; m
; ++s
, m
>>= 1) {
514 struct NineBaseTexture9
*tex
= src
->texture
[s
];
519 if ((tex
->managed
.dirty
| tex
->dirty_mip
) && LIST_IS_EMPTY(&tex
->list
))
520 list_add(&tex
->list
, &This
->base
.device
->update_textures
);
523 src
->texture
[s
]->bind_count
--;
524 nine_bind(&dst
->texture
[s
], src
->texture
[s
]);
531 IDirect3DStateBlock9Vtbl NineStateBlock9_vtable
= {
532 (void *)NineUnknown_QueryInterface
,
533 (void *)NineUnknown_AddRef
,
534 (void *)NineUnknown_Release
,
535 (void *)NineUnknown_GetDevice
, /* actually part of StateBlock9 iface */
536 (void *)NineStateBlock9_Capture
,
537 (void *)NineStateBlock9_Apply
540 static const GUID
*NineStateBlock9_IIDs
[] = {
541 &IID_IDirect3DStateBlock9
,
547 NineStateBlock9_new( struct NineDevice9
*pDevice
,
548 struct NineStateBlock9
**ppOut
,
549 enum nine_stateblock_type type
)
551 NINE_DEVICE_CHILD_NEW(StateBlock9
, ppOut
, pDevice
, type
);