+/* Check if some states need to be set dirty */
+
+static inline DWORD
+check_multisample(struct NineDevice9 *device)
+{
+ DWORD *rs = device->state.rs;
+ DWORD new_value = (rs[D3DRS_ZENABLE] || rs[D3DRS_STENCILENABLE]) &&
+ device->state.rt[0]->desc.MultiSampleType >= 1 &&
+ rs[D3DRS_MULTISAMPLEANTIALIAS];
+ if (rs[NINED3DRS_MULTISAMPLE] != new_value) {
+ rs[NINED3DRS_MULTISAMPLE] = new_value;
+ return NINE_STATE_RASTERIZER;
+ }
+ return 0;
+}
+
+/* State preparation only */
+
+static inline void
+prepare_blend(struct NineDevice9 *device)
+{
+ nine_convert_blend_state(&device->state.pipe.blend, device->state.rs);
+ device->state.commit |= NINE_STATE_COMMIT_BLEND;
+}
+
+static inline void
+prepare_dsa(struct NineDevice9 *device)
+{
+ nine_convert_dsa_state(&device->state.pipe.dsa, device->state.rs);
+ device->state.commit |= NINE_STATE_COMMIT_DSA;
+}
+
+static inline void
+prepare_rasterizer(struct NineDevice9 *device)
+{
+ nine_convert_rasterizer_state(device, &device->state.pipe.rast, device->state.rs);
+ device->state.commit |= NINE_STATE_COMMIT_RASTERIZER;
+}
+
+static void
+prepare_vs_constants_userbuf_swvp(struct NineDevice9 *device)
+{
+ struct nine_state *state = &device->state;
+
+ if (state->changed.vs_const_f || state->changed.group & NINE_STATE_SWVP) {
+ struct pipe_constant_buffer cb;
+
+ cb.buffer = NULL;
+ cb.buffer_offset = 0;
+ cb.buffer_size = 4096 * sizeof(float[4]);
+ cb.user_buffer = state->vs_const_f_swvp;
+
+ if (state->vs->lconstf.ranges) {
+ const struct nine_lconstf *lconstf = &device->state.vs->lconstf;
+ const struct nine_range *r = lconstf->ranges;
+ unsigned n = 0;
+ float *dst = device->state.vs_lconstf_temp;
+ float *src = (float *)cb.user_buffer;
+ memcpy(dst, src, cb.buffer_size);
+ while (r) {
+ unsigned p = r->bgn;
+ unsigned c = r->end - r->bgn;
+ memcpy(&dst[p * 4], &lconstf->data[n * 4], c * 4 * sizeof(float));
+ n += c;
+ r = r->next;
+ }
+ cb.user_buffer = dst;
+ }
+
+ state->pipe.cb0_swvp = cb;
+
+ cb.user_buffer = (char *)cb.user_buffer + 4096 * sizeof(float[4]);
+ state->pipe.cb1_swvp = cb;
+ }
+
+ if (state->changed.vs_const_i || state->changed.group & NINE_STATE_SWVP) {
+ struct pipe_constant_buffer cb;
+
+ cb.buffer = NULL;
+ cb.buffer_offset = 0;
+ cb.buffer_size = 2048 * sizeof(float[4]);
+ cb.user_buffer = state->vs_const_i;
+
+ state->pipe.cb2_swvp = cb;
+ state->changed.vs_const_i = 0;
+ }
+
+ if (state->changed.vs_const_b || state->changed.group & NINE_STATE_SWVP) {
+ struct pipe_constant_buffer cb;
+
+ cb.buffer = NULL;
+ cb.buffer_offset = 0;
+ cb.buffer_size = 512 * sizeof(float[4]);
+ cb.user_buffer = state->vs_const_b;
+
+ state->pipe.cb3_swvp = cb;
+ state->changed.vs_const_b = 0;
+ }
+
+ if (!device->driver_caps.user_cbufs) {
+ struct pipe_constant_buffer *cb = &(state->pipe.cb0_swvp);
+ u_upload_data(device->constbuf_uploader,
+ 0,
+ cb->buffer_size,
+ device->constbuf_alignment,
+ cb->user_buffer,
+ &(cb->buffer_offset),
+ &(cb->buffer));
+ u_upload_unmap(device->constbuf_uploader);
+ cb->user_buffer = NULL;
+
+ cb = &(state->pipe.cb1_swvp);
+ u_upload_data(device->constbuf_uploader,
+ 0,
+ cb->buffer_size,
+ device->constbuf_alignment,
+ cb->user_buffer,
+ &(cb->buffer_offset),
+ &(cb->buffer));
+ u_upload_unmap(device->constbuf_uploader);
+ cb->user_buffer = NULL;
+
+ cb = &(state->pipe.cb2_swvp);
+ u_upload_data(device->constbuf_uploader,
+ 0,
+ cb->buffer_size,
+ device->constbuf_alignment,
+ cb->user_buffer,
+ &(cb->buffer_offset),
+ &(cb->buffer));
+ u_upload_unmap(device->constbuf_uploader);
+ cb->user_buffer = NULL;
+
+ cb = &(state->pipe.cb3_swvp);
+ u_upload_data(device->constbuf_uploader,
+ 0,
+ cb->buffer_size,
+ device->constbuf_alignment,
+ cb->user_buffer,
+ &(cb->buffer_offset),
+ &(cb->buffer));
+ u_upload_unmap(device->constbuf_uploader);
+ cb->user_buffer = NULL;
+ }
+
+ if (device->state.changed.vs_const_f) {
+ struct nine_range *r = device->state.changed.vs_const_f;
+ struct nine_range *p = r;
+ while (p->next)
+ p = p->next;
+ nine_range_pool_put_chain(&device->range_pool, r, p);
+ device->state.changed.vs_const_f = NULL;
+ }
+
+ if (device->state.changed.vs_const_i) {
+ struct nine_range *r = device->state.changed.vs_const_i;
+ struct nine_range *p = r;
+ while (p->next)
+ p = p->next;
+ nine_range_pool_put_chain(&device->range_pool, r, p);
+ device->state.changed.vs_const_i = NULL;
+ }
+
+ if (device->state.changed.vs_const_b) {
+ struct nine_range *r = device->state.changed.vs_const_b;
+ struct nine_range *p = r;
+ while (p->next)
+ p = p->next;
+ nine_range_pool_put_chain(&device->range_pool, r, p);
+ device->state.changed.vs_const_b = NULL;
+ }
+
+ state->changed.group &= ~NINE_STATE_VS_CONST;
+ state->commit |= NINE_STATE_COMMIT_CONST_VS;
+}
+
+static void
+prepare_vs_constants_userbuf(struct NineDevice9 *device)
+{
+ struct nine_state *state = &device->state;
+ struct pipe_constant_buffer cb;
+ cb.buffer = NULL;
+ cb.buffer_offset = 0;
+ cb.buffer_size = device->state.vs->const_used_size;
+ cb.user_buffer = device->state.vs_const_f;
+
+ if (device->swvp) {
+ prepare_vs_constants_userbuf_swvp(device);
+ return;
+ }
+
+ if (state->changed.vs_const_i || state->changed.group & NINE_STATE_SWVP) {
+ int *idst = (int *)&state->vs_const_f[4 * device->max_vs_const_f];
+ memcpy(idst, state->vs_const_i, NINE_MAX_CONST_I * sizeof(int[4]));
+ state->changed.vs_const_i = 0;
+ }
+
+ if (state->changed.vs_const_b || state->changed.group & NINE_STATE_SWVP) {
+ int *idst = (int *)&state->vs_const_f[4 * device->max_vs_const_f];
+ uint32_t *bdst = (uint32_t *)&idst[4 * NINE_MAX_CONST_I];
+ memcpy(bdst, state->vs_const_b, NINE_MAX_CONST_B * sizeof(BOOL));
+ state->changed.vs_const_b = 0;
+ }
+
+ if (!cb.buffer_size)
+ return;
+
+ if (device->state.vs->lconstf.ranges) {
+ /* TODO: Can we make it so that we don't have to copy everything ? */
+ const struct nine_lconstf *lconstf = &device->state.vs->lconstf;
+ const struct nine_range *r = lconstf->ranges;
+ unsigned n = 0;
+ float *dst = device->state.vs_lconstf_temp;
+ float *src = (float *)cb.user_buffer;
+ memcpy(dst, src, cb.buffer_size);
+ while (r) {
+ unsigned p = r->bgn;
+ unsigned c = r->end - r->bgn;
+ memcpy(&dst[p * 4], &lconstf->data[n * 4], c * 4 * sizeof(float));
+ n += c;
+ r = r->next;
+ }
+ cb.user_buffer = dst;
+ }
+
+ if (!device->driver_caps.user_cbufs) {
+ u_upload_data(device->constbuf_uploader,
+ 0,
+ cb.buffer_size,
+ device->constbuf_alignment,
+ cb.user_buffer,
+ &cb.buffer_offset,
+ &cb.buffer);
+ u_upload_unmap(device->constbuf_uploader);
+ cb.user_buffer = NULL;
+ }
+
+ state->pipe.cb_vs = cb;
+
+ if (device->state.changed.vs_const_f) {
+ struct nine_range *r = device->state.changed.vs_const_f;
+ struct nine_range *p = r;
+ while (p->next)
+ p = p->next;
+ nine_range_pool_put_chain(&device->range_pool, r, p);
+ device->state.changed.vs_const_f = NULL;
+ }
+
+ if (device->state.changed.vs_const_i) {
+ struct nine_range *r = device->state.changed.vs_const_i;
+ struct nine_range *p = r;
+ while (p->next)
+ p = p->next;
+ nine_range_pool_put_chain(&device->range_pool, r, p);
+ device->state.changed.vs_const_i = NULL;
+ }
+
+ if (device->state.changed.vs_const_b) {
+ struct nine_range *r = device->state.changed.vs_const_b;
+ struct nine_range *p = r;
+ while (p->next)
+ p = p->next;
+ nine_range_pool_put_chain(&device->range_pool, r, p);
+ device->state.changed.vs_const_b = NULL;
+ }
+ state->changed.group &= ~NINE_STATE_VS_CONST;
+ state->commit |= NINE_STATE_COMMIT_CONST_VS;
+}
+
+static void
+prepare_ps_constants_userbuf(struct NineDevice9 *device)
+{
+ struct nine_state *state = &device->state;
+ struct pipe_constant_buffer cb;
+ cb.buffer = NULL;
+ cb.buffer_offset = 0;
+ cb.buffer_size = device->state.ps->const_used_size;
+ cb.user_buffer = device->state.ps_const_f;
+
+ if (state->changed.ps_const_i) {
+ int *idst = (int *)&state->ps_const_f[4 * device->max_ps_const_f];
+ memcpy(idst, state->ps_const_i, sizeof(state->ps_const_i));
+ state->changed.ps_const_i = 0;
+ }
+ if (state->changed.ps_const_b) {
+ int *idst = (int *)&state->ps_const_f[4 * device->max_ps_const_f];
+ uint32_t *bdst = (uint32_t *)&idst[4 * NINE_MAX_CONST_I];
+ memcpy(bdst, state->ps_const_b, sizeof(state->ps_const_b));
+ state->changed.ps_const_b = 0;
+ }
+
+ /* Upload special constants needed to implement PS1.x instructions like TEXBEM,TEXBEML and BEM */
+ if (device->state.ps->bumpenvmat_needed) {
+ memcpy(device->state.ps_lconstf_temp, cb.user_buffer, cb.buffer_size);
+ memcpy(&device->state.ps_lconstf_temp[4 * 8], &device->state.bumpmap_vars, sizeof(device->state.bumpmap_vars));
+
+ cb.user_buffer = device->state.ps_lconstf_temp;
+ }
+
+ if (state->ps->byte_code.version < 0x30 &&
+ state->rs[D3DRS_FOGENABLE]) {
+ float *dst = &state->ps_lconstf_temp[4 * 32];
+ if (cb.user_buffer != state->ps_lconstf_temp) {
+ memcpy(state->ps_lconstf_temp, cb.user_buffer, cb.buffer_size);
+ cb.user_buffer = state->ps_lconstf_temp;
+ }
+
+ d3dcolor_to_rgba(dst, state->rs[D3DRS_FOGCOLOR]);
+ if (state->rs[D3DRS_FOGTABLEMODE] == D3DFOG_LINEAR) {
+ dst[4] = asfloat(state->rs[D3DRS_FOGEND]);
+ dst[5] = 1.0f / (asfloat(state->rs[D3DRS_FOGEND]) - asfloat(state->rs[D3DRS_FOGSTART]));
+ } else if (state->rs[D3DRS_FOGTABLEMODE] != D3DFOG_NONE) {
+ dst[4] = asfloat(state->rs[D3DRS_FOGDENSITY]);
+ }
+ cb.buffer_size = 4 * 4 * 34;
+ }
+
+ if (!cb.buffer_size)
+ return;
+
+ if (!device->driver_caps.user_cbufs) {
+ u_upload_data(device->constbuf_uploader,
+ 0,
+ cb.buffer_size,
+ device->constbuf_alignment,
+ cb.user_buffer,
+ &cb.buffer_offset,
+ &cb.buffer);
+ u_upload_unmap(device->constbuf_uploader);
+ cb.user_buffer = NULL;
+ }
+
+ state->pipe.cb_ps = cb;
+
+ if (device->state.changed.ps_const_f) {
+ struct nine_range *r = device->state.changed.ps_const_f;
+ struct nine_range *p = r;
+ while (p->next)
+ p = p->next;
+ nine_range_pool_put_chain(&device->range_pool, r, p);
+ device->state.changed.ps_const_f = NULL;
+ }
+ state->changed.group &= ~NINE_STATE_PS_CONST;
+ state->commit |= NINE_STATE_COMMIT_CONST_PS;
+}
+
+static inline uint32_t
+prepare_vs(struct NineDevice9 *device, uint8_t shader_changed)
+{
+ struct nine_state *state = &device->state;
+ struct NineVertexShader9 *vs = state->vs;
+ uint32_t changed_group = 0;
+ int has_key_changed = 0;
+
+ if (likely(state->programmable_vs))
+ has_key_changed = NineVertexShader9_UpdateKey(vs, device);
+
+ if (!shader_changed && !has_key_changed)
+ return 0;
+
+ /* likely because we dislike FF */
+ if (likely(state->programmable_vs)) {
+ state->cso.vs = NineVertexShader9_GetVariant(vs);
+ } else {
+ vs = device->ff.vs;
+ state->cso.vs = vs->ff_cso;
+ }
+
+ if (state->rs[NINED3DRS_VSPOINTSIZE] != vs->point_size) {
+ state->rs[NINED3DRS_VSPOINTSIZE] = vs->point_size;
+ changed_group |= NINE_STATE_RASTERIZER;
+ }
+
+ if ((state->bound_samplers_mask_vs & vs->sampler_mask) != vs->sampler_mask)
+ /* Bound dummy sampler. */
+ changed_group |= NINE_STATE_SAMPLER;
+
+ state->commit |= NINE_STATE_COMMIT_VS;
+ return changed_group;
+}
+
+static inline uint32_t
+prepare_ps(struct NineDevice9 *device, uint8_t shader_changed)
+{
+ struct nine_state *state = &device->state;
+ struct NinePixelShader9 *ps = state->ps;
+ uint32_t changed_group = 0;
+ int has_key_changed = 0;
+
+ if (likely(ps))
+ has_key_changed = NinePixelShader9_UpdateKey(ps, state);
+
+ if (!shader_changed && !has_key_changed)
+ return 0;
+
+ if (likely(ps)) {
+ state->cso.ps = NinePixelShader9_GetVariant(ps);
+ } else {
+ ps = device->ff.ps;
+ state->cso.ps = ps->ff_cso;
+ }
+
+ if ((state->bound_samplers_mask_ps & ps->sampler_mask) != ps->sampler_mask)
+ /* Bound dummy sampler. */
+ changed_group |= NINE_STATE_SAMPLER;
+
+ state->commit |= NINE_STATE_COMMIT_PS;
+ return changed_group;
+}
+
+/* State preparation incremental */
+
+/* State preparation + State commit */
+
+static void
+update_framebuffer(struct NineDevice9 *device, bool is_clear)