We use separate clear operations because our blitters do not support clearing resources of different sizes properly. In the case of the OpenGL blitter, we cannot reliably clear attachments of different sizes using a single glClear() command. The OpenGL spec says:
"If the attachment sizes are not all identical, the results of rendering are defined only within the largest area that can fit in all of the attachments. This area is defined as the intersection of rectangles having a lower left of (0, 0) and an upper right of (width, height) for each attachment. Contents of attachments outside this area are undefined after execution of a rendering command (as defined in section 2.4)."
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46067 Signed-off-by: Józef Kucia jkucia@codeweavers.com --- dlls/wined3d/cs.c | 105 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 84 insertions(+), 21 deletions(-)
diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c index aa8eef7a2e01..4a0ca3386bc1 100644 --- a/dlls/wined3d/cs.c +++ b/dlls/wined3d/cs.c @@ -600,48 +600,111 @@ static void wined3d_cs_exec_clear(struct wined3d_cs *cs, const void *data) void wined3d_cs_emit_clear(struct wined3d_cs *cs, DWORD rect_count, const RECT *rects, DWORD flags, const struct wined3d_color *color, float depth, DWORD stencil) { + RECT initial_draw_rect, draw_rect, previous_draw_rect, view_rect; + BOOL have_previous_draw_rect = FALSE, have_identical_size = TRUE; const struct wined3d_state *state = &cs->device->state; const struct wined3d_viewport *vp = &state->viewports[0]; struct wined3d_rendertarget_view *view; struct wined3d_cs_clear *op; unsigned int rt_count, i; - RECT view_rect;
- rt_count = flags & WINED3DCLEAR_TARGET ? cs->device->adapter->d3d_info.limits.max_rt_count : 0; - - op = wined3d_cs_require_space(cs, FIELD_OFFSET(struct wined3d_cs_clear, rects[rect_count]), - WINED3D_CS_QUEUE_DEFAULT); - op->opcode = WINED3D_CS_OP_CLEAR; - op->flags = flags; - op->rt_count = rt_count; - op->fb = &cs->fb; - SetRect(&op->draw_rect, vp->x, vp->y, vp->x + vp->width, vp->y + vp->height); + SetRect(&initial_draw_rect, vp->x, vp->y, vp->x + vp->width, vp->y + vp->height); if (state->render_states[WINED3D_RS_SCISSORTESTENABLE]) - IntersectRect(&op->draw_rect, &op->draw_rect, &state->scissor_rects[0]); - op->color = *color; - op->depth = depth; - op->stencil = stencil; - op->rect_count = rect_count; - memcpy(op->rects, rects, sizeof(*rects) * rect_count); + IntersectRect(&initial_draw_rect, &initial_draw_rect, &state->scissor_rects[0]);
+ rt_count = flags & WINED3DCLEAR_TARGET ? cs->device->adapter->d3d_info.limits.max_rt_count : 0; for (i = 0; i < rt_count; ++i) { if ((view = state->fb->render_targets[i])) { - SetRect(&view_rect, 0, 0, view->width, view->height); - IntersectRect(&op->draw_rect, &op->draw_rect, &view_rect); wined3d_resource_acquire(view->resource); + + SetRect(&view_rect, 0, 0, view->width, view->height); + IntersectRect(&draw_rect, &initial_draw_rect, &view_rect); + if (have_previous_draw_rect && !EqualRect(&previous_draw_rect, &draw_rect)) + have_identical_size = FALSE; + previous_draw_rect = draw_rect; + have_previous_draw_rect = TRUE; } } if (flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL)) { view = state->fb->depth_stencil; - SetRect(&view_rect, 0, 0, view->width, view->height); - IntersectRect(&op->draw_rect, &op->draw_rect, &view_rect); wined3d_resource_acquire(view->resource); + + SetRect(&view_rect, 0, 0, view->width, view->height); + IntersectRect(&draw_rect, &initial_draw_rect, &view_rect); + if (have_previous_draw_rect && !EqualRect(&previous_draw_rect, &draw_rect)) + have_identical_size = FALSE; + previous_draw_rect = draw_rect; + have_previous_draw_rect = TRUE; }
- wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); + if (have_identical_size) + { + op = wined3d_cs_require_space(cs, + FIELD_OFFSET(struct wined3d_cs_clear, rects[rect_count]), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_CLEAR; + op->flags = flags; + op->rt_count = rt_count; + op->fb = &cs->fb; + op->draw_rect = draw_rect; + op->color = *color; + op->depth = depth; + op->stencil = stencil; + op->rect_count = rect_count; + memcpy(op->rects, rects, sizeof(*rects) * rect_count); + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); + return; + } + + /* Emit a separate clear operation per resource, if the resource sizes are not all identical. + * The CPU blitter and the OpenGL blitter assume that all regions to clear are identical. */ + for (i = 0; i < rt_count; ++i) + { + if ((view = state->fb->render_targets[i])) + { + op = wined3d_cs_require_space(cs, FIELD_OFFSET(struct wined3d_cs_clear, rects[rect_count]) + + sizeof(struct wined3d_fb_state), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_CLEAR; + op->flags = WINED3DCLEAR_TARGET; + op->rt_count = 1; + op->fb = (void *)&op->rects[rect_count]; + SetRect(&view_rect, 0, 0, view->width, view->height); + IntersectRect(&op->draw_rect, &initial_draw_rect, &view_rect); + op->color = *color; + op->rect_count = rect_count; + memcpy(op->rects, rects, sizeof(*rects) * rect_count); + + op->fb->render_targets[0] = view; + op->fb->depth_stencil = NULL; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); + } + } + if (flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL)) + { + view = state->fb->depth_stencil; + + op = wined3d_cs_require_space(cs, FIELD_OFFSET(struct wined3d_cs_clear, rects[rect_count]) + + sizeof(struct wined3d_fb_state), WINED3D_CS_QUEUE_DEFAULT); + op->opcode = WINED3D_CS_OP_CLEAR; + op->flags = flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL); + op->rt_count = 0; + op->fb = (void *)&op->rects[rect_count]; + SetRect(&view_rect, 0, 0, view->width, view->height); + IntersectRect(&op->draw_rect, &initial_draw_rect, &view_rect); + op->depth = depth; + op->stencil = stencil; + op->rect_count = rect_count; + memcpy(op->rects, rects, sizeof(*rects) * rect_count); + + op->fb->render_targets[0] = NULL; + op->fb->depth_stencil = view; + + wined3d_cs_submit(cs, WINED3D_CS_QUEUE_DEFAULT); + } }
void wined3d_cs_emit_clear_rendertarget_view(struct wined3d_cs *cs, struct wined3d_rendertarget_view *view,