1 /*
2 * Copyright 2011 Joakim Sindholt <[email protected]>
3 * SPDX-License-Identifier: MIT
4 */
5
6 #include "stateblock9.h"
7 #include "device9.h"
8 #include "basetexture9.h"
9 #include "nine_helpers.h"
10 #include "vertexdeclaration9.h"
11 #include "vertexbuffer9.h"
12 #include "indexbuffer9.h"
13
14 #define DBG_CHANNEL DBG_STATEBLOCK
15
16 /* XXX TODO: handling of lights is broken */
17
18 HRESULT
NineStateBlock9_ctor(struct NineStateBlock9 * This,struct NineUnknownParams * pParams,enum nine_stateblock_type type)19 NineStateBlock9_ctor( struct NineStateBlock9 *This,
20 struct NineUnknownParams *pParams,
21 enum nine_stateblock_type type )
22 {
23 HRESULT hr = NineUnknown_ctor(&This->base, pParams);
24
25 DBG("This=%p pParams=%p type=%d\n", This, pParams, type);
26
27 if (FAILED(hr))
28 return hr;
29
30 This->type = type;
31
32 This->state.vs_const_f = MALLOC(VS_CONST_F_SIZE(This->base.device));
33 This->state.ps_const_f = MALLOC(This->base.device->ps_const_size);
34 This->state.vs_const_i = MALLOC(VS_CONST_I_SIZE(This->base.device));
35 This->state.vs_const_b = MALLOC(VS_CONST_B_SIZE(This->base.device));
36 if (!This->state.vs_const_f || !This->state.ps_const_f ||
37 !This->state.vs_const_i || !This->state.vs_const_b)
38 return E_OUTOFMEMORY;
39
40 return D3D_OK;
41 }
42
43 void
NineStateBlock9_dtor(struct NineStateBlock9 * This)44 NineStateBlock9_dtor( struct NineStateBlock9 *This )
45 {
46 struct nine_state *state = &This->state;
47 struct nine_range *r;
48 struct nine_range_pool *pool = &This->base.device->range_pool;
49 unsigned i;
50
51 for (i = 0; i < ARRAY_SIZE(state->rt); ++i)
52 nine_bind(&state->rt[i], NULL);
53 nine_bind(&state->ds, NULL);
54 nine_bind(&state->vs, NULL);
55 nine_bind(&state->ps, NULL);
56 nine_bind(&state->vdecl, NULL);
57 for (i = 0; i < PIPE_MAX_ATTRIBS; ++i)
58 nine_bind(&state->stream[i], NULL);
59
60 nine_bind(&state->idxbuf, NULL);
61 for (i = 0; i < NINE_MAX_SAMPLERS; ++i)
62 nine_bind(&state->texture[i], NULL);
63
64 FREE(state->vs_const_f);
65 FREE(state->ps_const_f);
66 FREE(state->vs_const_i);
67 FREE(state->vs_const_b);
68
69 FREE(state->ff.light);
70
71 FREE(state->ff.transform);
72
73 if (This->state.changed.ps_const_f) {
74 for (r = This->state.changed.ps_const_f; r->next; r = r->next);
75 nine_range_pool_put_chain(pool, This->state.changed.ps_const_f, r);
76 }
77 if (This->state.changed.vs_const_f) {
78 for (r = This->state.changed.vs_const_f; r->next; r = r->next);
79 nine_range_pool_put_chain(pool, This->state.changed.vs_const_f, r);
80 }
81 if (This->state.changed.vs_const_i) {
82 for (r = This->state.changed.vs_const_i; r->next; r = r->next);
83 nine_range_pool_put_chain(pool, This->state.changed.vs_const_i, r);
84 }
85 if (This->state.changed.vs_const_b) {
86 for (r = This->state.changed.vs_const_b; r->next; r = r->next);
87 nine_range_pool_put_chain(pool, This->state.changed.vs_const_b, r);
88 }
89
90 NineUnknown_dtor(&This->base);
91 }
92
93 static void
NineStateBlock9_BindBuffer(struct NineDevice9 * device,bool applyToDevice,struct NineBuffer9 ** slot,struct NineBuffer9 * buf)94 NineStateBlock9_BindBuffer( struct NineDevice9 *device,
95 bool applyToDevice,
96 struct NineBuffer9 **slot,
97 struct NineBuffer9 *buf )
98 {
99 if (applyToDevice)
100 NineBindBufferToDevice(device, slot, buf);
101 else
102 nine_bind(slot, buf);
103 }
104
105 static void
NineStateBlock9_BindTexture(struct NineDevice9 * device,bool applyToDevice,struct NineBaseTexture9 ** slot,struct NineBaseTexture9 * tex)106 NineStateBlock9_BindTexture( struct NineDevice9 *device,
107 bool applyToDevice,
108 struct NineBaseTexture9 **slot,
109 struct NineBaseTexture9 *tex )
110 {
111 if (applyToDevice)
112 NineBindTextureToDevice(device, slot, tex);
113 else
114 nine_bind(slot, tex);
115 }
116
117 /* Copy state marked changed in @mask from @src to @dst.
118 * If @apply is false, updating dst->changed can be omitted.
119 * TODO: compare ?
120 */
121 static void
nine_state_copy_common(struct NineDevice9 * device,struct nine_state * dst,struct nine_state * src,struct nine_state * mask,const bool apply,struct nine_range_pool * pool)122 nine_state_copy_common(struct NineDevice9 *device,
123 struct nine_state *dst,
124 struct nine_state *src,
125 struct nine_state *mask, /* aliases either src or dst */
126 const bool apply,
127 struct nine_range_pool *pool)
128 {
129 unsigned i, s;
130
131 DBG("apply:%d changed.group: %x\n", (int)apply, (int)mask->changed.group );
132
133 /* device changed.* are unused.
134 * Instead nine_context_apply_stateblock is used and will
135 * internally set the right context->changed fields.
136 * Uncomment these only if we want to apply a stateblock onto a stateblock.
137 *
138 * if (apply)
139 * dst->changed.group |= mask->changed.group;
140 */
141
142 if (mask->changed.group & NINE_STATE_VIEWPORT)
143 dst->viewport = src->viewport;
144 if (mask->changed.group & NINE_STATE_SCISSOR)
145 dst->scissor = src->scissor;
146
147 if (mask->changed.group & NINE_STATE_VS)
148 nine_bind(&dst->vs, src->vs);
149 if (mask->changed.group & NINE_STATE_PS)
150 nine_bind(&dst->ps, src->ps);
151
152 /* Vertex constants.
153 *
154 * Various possibilities for optimization here, like creating a per-SB
155 * constant buffer, or memcmp'ing for changes.
156 * Will do that later depending on what works best for specific apps.
157 *
158 * Note: Currently when we apply stateblocks, it's always on the device state.
159 * Should it affect recording stateblocks ? Since it's on device state, there
160 * is no need to copy which ranges are dirty. If it turns out we should affect
161 * recording stateblocks, the info should be copied.
162 */
163 if (mask->changed.group & NINE_STATE_VS_CONST) {
164 struct nine_range *r;
165 for (r = mask->changed.vs_const_f; r; r = r->next) {
166 memcpy(&dst->vs_const_f[r->bgn * 4],
167 &src->vs_const_f[r->bgn * 4],
168 (r->end - r->bgn) * 4 * sizeof(float));
169 }
170 for (r = mask->changed.vs_const_i; r; r = r->next) {
171 memcpy(&dst->vs_const_i[r->bgn * 4],
172 &src->vs_const_i[r->bgn * 4],
173 (r->end - r->bgn) * 4 * sizeof(int));
174 }
175 for (r = mask->changed.vs_const_b; r; r = r->next) {
176 memcpy(&dst->vs_const_b[r->bgn],
177 &src->vs_const_b[r->bgn],
178 (r->end - r->bgn) * sizeof(int));
179 }
180 }
181
182 /* Pixel constants. */
183 if (mask->changed.group & NINE_STATE_PS_CONST) {
184 struct nine_range *r;
185 for (r = mask->changed.ps_const_f; r; r = r->next) {
186 memcpy(&dst->ps_const_f[r->bgn * 4],
187 &src->ps_const_f[r->bgn * 4],
188 (r->end - r->bgn) * 4 * sizeof(float));
189 }
190 if (mask->changed.ps_const_i) {
191 uint16_t m = mask->changed.ps_const_i;
192 for (i = ffs(m) - 1, m >>= i; m; ++i, m >>= 1)
193 if (m & 1)
194 memcpy(dst->ps_const_i[i], src->ps_const_i[i], 4 * sizeof(int));
195 }
196 if (mask->changed.ps_const_b) {
197 uint16_t m = mask->changed.ps_const_b;
198 for (i = ffs(m) - 1, m >>= i; m; ++i, m >>= 1)
199 if (m & 1)
200 dst->ps_const_b[i] = src->ps_const_b[i];
201 }
202 }
203
204 /* Render states.
205 * TODO: Maybe build a list ?
206 */
207 for (i = 0; i < ARRAY_SIZE(mask->changed.rs); ++i) {
208 uint32_t m = mask->changed.rs[i];
209 /* if (apply)
210 * dst->changed.rs[i] |= m; */
211 while (m) {
212 const int r = ffs(m) - 1;
213 m &= ~(1 << r);
214 DBG("State %d %s = %d\n", i * 32 + r, nine_d3drs_to_string(i * 32 + r), (int)src->rs_advertised[i * 32 + r]);
215 dst->rs_advertised[i * 32 + r] = src->rs_advertised[i * 32 + r];
216 }
217 }
218
219
220 /* Clip planes. */
221 if (mask->changed.ucp) {
222 DBG("ucp: %x\n", mask->changed.ucp);
223 for (i = 0; i < PIPE_MAX_CLIP_PLANES; ++i)
224 if (mask->changed.ucp & (1 << i))
225 memcpy(dst->clip.ucp[i],
226 src->clip.ucp[i], sizeof(src->clip.ucp[0]));
227 /* if (apply)
228 * dst->changed.ucp |= mask->changed.ucp;*/
229 }
230
231 /* Sampler state. */
232 if (mask->changed.group & NINE_STATE_SAMPLER) {
233 for (s = 0; s < NINE_MAX_SAMPLERS; ++s) {
234 if (mask->changed.sampler[s] == 0x3ffe) {
235 memcpy(&dst->samp_advertised[s], &src->samp_advertised[s], sizeof(dst->samp_advertised[s]));
236 } else {
237 uint32_t m = mask->changed.sampler[s];
238 DBG("samp %d: changed = %x\n", i, (int)m);
239 while (m) {
240 const int i = ffs(m) - 1;
241 m &= ~(1 << i);
242 dst->samp_advertised[s][i] = src->samp_advertised[s][i];
243 }
244 }
245 /* if (apply)
246 * dst->changed.sampler[s] |= mask->changed.sampler[s];*/
247 }
248 }
249
250 /* Index buffer. */
251 if (mask->changed.group & NINE_STATE_IDXBUF)
252 NineStateBlock9_BindBuffer(device,
253 apply,
254 (struct NineBuffer9 **)&dst->idxbuf,
255 (struct NineBuffer9 *)src->idxbuf);
256
257 /* Vertex streams. */
258 if (mask->changed.vtxbuf | mask->changed.stream_freq) {
259 DBG("vtxbuf/stream_freq: %x/%x\n", mask->changed.vtxbuf, mask->changed.stream_freq);
260 uint32_t m = mask->changed.vtxbuf | mask->changed.stream_freq;
261 for (i = 0; m; ++i, m >>= 1) {
262 if (mask->changed.vtxbuf & (1 << i)) {
263 NineStateBlock9_BindBuffer(device,
264 apply,
265 (struct NineBuffer9 **)&dst->stream[i],
266 (struct NineBuffer9 *)src->stream[i]);
267 if (src->stream[i]) {
268 dst->vtxbuf[i].buffer_offset = src->vtxbuf[i].buffer_offset;
269 dst->vtxstride[i] = src->vtxstride[i];
270 }
271 }
272 if (mask->changed.stream_freq & (1 << i))
273 dst->stream_freq[i] = src->stream_freq[i];
274 }
275 /*
276 * if (apply) {
277 * dst->changed.vtxbuf |= mask->changed.vtxbuf;
278 * dst->changed.stream_freq |= mask->changed.stream_freq;
279 * }*/
280 }
281
282 /* Textures */
283 if (mask->changed.texture) {
284 uint32_t m = mask->changed.texture;
285 for (s = 0; m; ++s, m >>= 1)
286 if (m & 1)
287 NineStateBlock9_BindTexture(device, apply, &dst->texture[s], src->texture[s]);
288 }
289
290 if (!(mask->changed.group & NINE_STATE_FF))
291 return;
292 WARN_ONCE("Fixed function state not handled properly by StateBlocks.\n");
293
294 /* Fixed function state. */
295
296 if (mask->changed.group & NINE_STATE_FF_MATERIAL)
297 dst->ff.material = src->ff.material;
298
299 if (mask->changed.group & NINE_STATE_FF_PS_CONSTS) {
300 for (s = 0; s < NINE_MAX_TEXTURE_STAGES; ++s) {
301 for (i = 0; i < NINED3DTSS_COUNT; ++i)
302 if (mask->ff.changed.tex_stage[s][i / 32] & (1 << (i % 32)))
303 dst->ff.tex_stage[s][i] = src->ff.tex_stage[s][i];
304 /*
305 * if (apply) {
306 * TODO: it's 32 exactly, just offset by 1 as 0 is unused
307 * dst->ff.changed.tex_stage[s][0] |=
308 * mask->ff.changed.tex_stage[s][0];
309 * dst->ff.changed.tex_stage[s][1] |=
310 * mask->ff.changed.tex_stage[s][1];
311 * }*/
312 }
313 }
314 if (mask->changed.group & NINE_STATE_FF_LIGHTING) {
315 unsigned num_lights = MAX2(dst->ff.num_lights, src->ff.num_lights);
316 /* Can happen in Capture() if device state has created new lights after
317 * the stateblock was created.
318 * Can happen in Apply() if the stateblock had recorded the creation of
319 * new lights. */
320 if (dst->ff.num_lights < num_lights) {
321 dst->ff.light = REALLOC(dst->ff.light,
322 dst->ff.num_lights * sizeof(D3DLIGHT9),
323 num_lights * sizeof(D3DLIGHT9));
324 memset(&dst->ff.light[dst->ff.num_lights], 0, (num_lights - dst->ff.num_lights) * sizeof(D3DLIGHT9));
325 /* if mask == dst, a Type of 0 will trigger
326 * "dst->ff.light[i] = src->ff.light[i];" later,
327 * which is what we want in that case. */
328 if (mask != dst) {
329 for (i = dst->ff.num_lights; i < num_lights; ++i)
330 dst->ff.light[i].Type = (D3DLIGHTTYPE)NINED3DLIGHT_INVALID;
331 }
332 dst->ff.num_lights = num_lights;
333 }
334 /* Can happen in Capture() if the stateblock had recorded the creation of
335 * new lights.
336 * Can happen in Apply() if device state has created new lights after
337 * the stateblock was created. */
338 if (src->ff.num_lights < num_lights) {
339 src->ff.light = REALLOC(src->ff.light,
340 src->ff.num_lights * sizeof(D3DLIGHT9),
341 num_lights * sizeof(D3DLIGHT9));
342 memset(&src->ff.light[src->ff.num_lights], 0, (num_lights - src->ff.num_lights) * sizeof(D3DLIGHT9));
343 for (i = src->ff.num_lights; i < num_lights; ++i)
344 src->ff.light[i].Type = (D3DLIGHTTYPE)NINED3DLIGHT_INVALID;
345 src->ff.num_lights = num_lights;
346 }
347 /* Note: mask is either src or dst, so at this point src, dst and mask
348 * have num_lights lights. */
349 for (i = 0; i < num_lights; ++i)
350 if (mask->ff.light[i].Type != NINED3DLIGHT_INVALID)
351 dst->ff.light[i] = src->ff.light[i];
352
353 memcpy(dst->ff.active_light, src->ff.active_light, sizeof(src->ff.active_light) );
354 dst->ff.num_lights_active = src->ff.num_lights_active;
355 }
356 if (mask->changed.group & NINE_STATE_FF_VSTRANSF) {
357 for (i = 0; i < ARRAY_SIZE(mask->ff.changed.transform); ++i) {
358 if (!mask->ff.changed.transform[i])
359 continue;
360 for (s = i * 32; s < (i * 32 + 32); ++s) {
361 if (!(mask->ff.changed.transform[i] & (1 << (s % 32))))
362 continue;
363 *nine_state_access_transform(&dst->ff, s, true) =
364 *nine_state_access_transform(&src->ff, s, false);
365 }
366 /* if (apply)
367 * dst->ff.changed.transform[i] |= mask->ff.changed.transform[i];*/
368 }
369 }
370 }
371
372 static void
nine_state_copy_common_all(struct NineDevice9 * device,struct nine_state * dst,struct nine_state * src,struct nine_state * help,const bool apply,struct nine_range_pool * pool,const int MaxStreams)373 nine_state_copy_common_all(struct NineDevice9 *device,
374 struct nine_state *dst,
375 struct nine_state *src,
376 struct nine_state *help,
377 const bool apply,
378 struct nine_range_pool *pool,
379 const int MaxStreams)
380 {
381 unsigned i;
382
383 /* if (apply)
384 * dst->changed.group |= src->changed.group;
385 */
386
387 dst->viewport = src->viewport;
388 dst->scissor = src->scissor;
389
390 nine_bind(&dst->vs, src->vs);
391 nine_bind(&dst->ps, src->ps);
392
393 /* Vertex constants.
394 *
395 * Various possibilities for optimization here, like creating a per-SB
396 * constant buffer, or memcmp'ing for changes.
397 * Will do that later depending on what works best for specific apps.
398 */
399 if (1) {
400 memcpy(&dst->vs_const_f[0],
401 &src->vs_const_f[0], VS_CONST_F_SIZE(device));
402
403 memcpy(dst->vs_const_i, src->vs_const_i, VS_CONST_I_SIZE(device));
404 memcpy(dst->vs_const_b, src->vs_const_b, VS_CONST_B_SIZE(device));
405 }
406
407 /* Pixel constants. */
408 if (1) {
409 struct nine_range *r = help->changed.ps_const_f;
410 memcpy(&dst->ps_const_f[0],
411 &src->ps_const_f[0], (r->end - r->bgn) * 4 * sizeof(float));
412
413 memcpy(dst->ps_const_i, src->ps_const_i, sizeof(dst->ps_const_i));
414 memcpy(dst->ps_const_b, src->ps_const_b, sizeof(dst->ps_const_b));
415 }
416
417 /* Render states. */
418 memcpy(dst->rs_advertised, src->rs_advertised, sizeof(dst->rs_advertised));
419 /* if (apply)
420 * memcpy(dst->changed.rs, src->changed.rs, sizeof(dst->changed.rs));*/
421
422
423 /* Clip planes. */
424 memcpy(&dst->clip, &src->clip, sizeof(dst->clip));
425 /* if (apply)
426 * dst->changed.ucp = src->changed.ucp;*/
427
428 /* Sampler state. */
429 memcpy(dst->samp_advertised, src->samp_advertised, sizeof(dst->samp_advertised));
430 /* if (apply)
431 * memcpy(dst->changed.sampler,
432 * src->changed.sampler, sizeof(dst->changed.sampler));*/
433
434 /* Index buffer. */
435 NineStateBlock9_BindBuffer(device,
436 apply,
437 (struct NineBuffer9 **)&dst->idxbuf,
438 (struct NineBuffer9 *)src->idxbuf);
439
440 /* Vertex streams. */
441 if (1) {
442 for (i = 0; i < ARRAY_SIZE(dst->stream); ++i) {
443 NineStateBlock9_BindBuffer(device,
444 apply,
445 (struct NineBuffer9 **)&dst->stream[i],
446 (struct NineBuffer9 *)src->stream[i]);
447 if (src->stream[i]) {
448 dst->vtxbuf[i].buffer_offset = src->vtxbuf[i].buffer_offset;
449 dst->vtxstride[i] = src->vtxstride[i];
450 }
451 dst->stream_freq[i] = src->stream_freq[i];
452 }
453 /* if (apply) {
454 * dst->changed.vtxbuf = (1ULL << MaxStreams) - 1;
455 * dst->changed.stream_freq = (1ULL << MaxStreams) - 1;
456 * }*/
457 }
458
459 /* Textures */
460 if (1) {
461 for (i = 0; i < NINE_MAX_SAMPLERS; i++)
462 NineStateBlock9_BindTexture(device, apply, &dst->texture[i], src->texture[i]);
463 }
464
465 /* keep this check in case we want to disable FF */
466 if (!(help->changed.group & NINE_STATE_FF))
467 return;
468 WARN_ONCE("Fixed function state not handled properly by StateBlocks.\n");
469
470 /* Fixed function state. */
471 dst->ff.material = src->ff.material;
472
473 memcpy(dst->ff.tex_stage, src->ff.tex_stage, sizeof(dst->ff.tex_stage));
474 /* if (apply) TODO: memset
475 * memcpy(dst->ff.changed.tex_stage,
476 * src->ff.changed.tex_stage, sizeof(dst->ff.changed.tex_stage));*/
477
478 /* Lights. */
479 if (1) {
480 if (dst->ff.num_lights < src->ff.num_lights) {
481 dst->ff.light = REALLOC(dst->ff.light,
482 dst->ff.num_lights * sizeof(D3DLIGHT9),
483 src->ff.num_lights * sizeof(D3DLIGHT9));
484 dst->ff.num_lights = src->ff.num_lights;
485 }
486 memcpy(dst->ff.light,
487 src->ff.light, src->ff.num_lights * sizeof(dst->ff.light[0]));
488
489 memcpy(dst->ff.active_light, src->ff.active_light, sizeof(src->ff.active_light) );
490 dst->ff.num_lights_active = src->ff.num_lights_active;
491 }
492
493 /* Transforms. */
494 if (1) {
495 /* Increase dst size if required (to copy the new states).
496 * Increase src size if required (to initialize missing transforms).
497 */
498 if (dst->ff.num_transforms != src->ff.num_transforms) {
499 int num_transforms = MAX2(src->ff.num_transforms, dst->ff.num_transforms);
500 nine_state_resize_transform(&src->ff, num_transforms);
501 nine_state_resize_transform(&dst->ff, num_transforms);
502 }
503 memcpy(dst->ff.transform,
504 src->ff.transform, dst->ff.num_transforms * sizeof(D3DMATRIX));
505 /* Apply is always used on device state.
506 * src is then the D3DSBT_ALL stateblock which
507 * ff.changed.transform indicates all matrices are dirty.
508 *
509 * if (apply)
510 * memcpy(dst->ff.changed.transform,
511 * src->ff.changed.transform, sizeof(dst->ff.changed.transform));*/
512 }
513 }
514
515 /* Capture those bits of current device state that have been changed between
516 * BeginStateBlock and EndStateBlock.
517 */
518 HRESULT NINE_WINAPI
NineStateBlock9_Capture(struct NineStateBlock9 * This)519 NineStateBlock9_Capture( struct NineStateBlock9 *This )
520 {
521 struct NineDevice9 *device = This->base.device;
522 struct nine_state *dst = &This->state;
523 struct nine_state *src = &device->state;
524 const int MaxStreams = device->caps.MaxStreams;
525
526 DBG("This=%p\n", This);
527
528 if (This->type == NINESBT_ALL)
529 nine_state_copy_common_all(device, dst, src, dst, false, NULL, MaxStreams);
530 else
531 nine_state_copy_common(device, dst, src, dst, false, NULL);
532
533 if (dst->changed.group & NINE_STATE_VDECL)
534 nine_bind(&dst->vdecl, src->vdecl);
535
536 return D3D_OK;
537 }
538
539 /* Set state managed by this StateBlock as current device state. */
540 HRESULT NINE_WINAPI
NineStateBlock9_Apply(struct NineStateBlock9 * This)541 NineStateBlock9_Apply( struct NineStateBlock9 *This )
542 {
543 struct NineDevice9 *device = This->base.device;
544 struct nine_state *dst = &device->state;
545 struct nine_state *src = &This->state;
546 struct nine_range_pool *pool = &device->range_pool;
547 const int MaxStreams = device->caps.MaxStreams;
548
549 DBG("This=%p\n", This);
550
551 if (This->type == NINESBT_ALL)
552 nine_state_copy_common_all(device, dst, src, src, true, pool, MaxStreams);
553 else
554 nine_state_copy_common(device, dst, src, src, true, pool);
555
556 nine_context_apply_stateblock(device, src);
557
558 if ((src->changed.group & NINE_STATE_VDECL) && src->vdecl)
559 nine_bind(&dst->vdecl, src->vdecl);
560
561 return D3D_OK;
562 }
563
564 IDirect3DStateBlock9Vtbl NineStateBlock9_vtable = {
565 (void *)NineUnknown_QueryInterface,
566 (void *)NineUnknown_AddRef,
567 (void *)NineUnknown_Release,
568 (void *)NineUnknown_GetDevice, /* actually part of StateBlock9 iface */
569 (void *)NineStateBlock9_Capture,
570 (void *)NineStateBlock9_Apply
571 };
572
573 static const GUID *NineStateBlock9_IIDs[] = {
574 &IID_IDirect3DStateBlock9,
575 &IID_IUnknown,
576 NULL
577 };
578
579 HRESULT
NineStateBlock9_new(struct NineDevice9 * pDevice,struct NineStateBlock9 ** ppOut,enum nine_stateblock_type type)580 NineStateBlock9_new( struct NineDevice9 *pDevice,
581 struct NineStateBlock9 **ppOut,
582 enum nine_stateblock_type type)
583 {
584 NINE_DEVICE_CHILD_NEW(StateBlock9, ppOut, pDevice, type);
585 }
586