diff --git a/src/gallium/drivers/zink/zink_clear.c b/src/gallium/drivers/zink/zink_clear.c index e8dbe01e334..de834b612e8 100644 --- a/src/gallium/drivers/zink/zink_clear.c +++ b/src/gallium/drivers/zink/zink_clear.c @@ -23,8 +23,26 @@ #include "zink_context.h" #include "zink_resource.h" +#include "util/format/u_format.h" #include "util/u_framebuffer.h" +#include "util/format_srgb.h" +#include "util/u_rect.h" +static inline bool +check_3d_layers(struct pipe_surface *psurf) +{ + /* SPEC PROBLEM: + * though the vk spec doesn't seem to explicitly address this, currently drivers + * are claiming that all 3D images have a single "3D" layer regardless of layercount, + * so we can never clear them if we aren't trying to clear only layer 0 + */ + if (psurf->u.tex.first_layer) + return false; + + if (psurf->u.tex.last_layer - psurf->u.tex.first_layer > 0) + return false; + return true; +} static void clear_in_rp(struct pipe_context *pctx, @@ -72,11 +90,16 @@ clear_in_rp(struct pipe_context *pctx, ++num_attachments; } - VkClearRect cr; - cr.rect.offset.x = 0; - cr.rect.offset.y = 0; - cr.rect.extent.width = fb->width; - cr.rect.extent.height = fb->height; + VkClearRect cr = {}; + if (scissor_state) { + cr.rect.offset.x = scissor_state->minx; + cr.rect.offset.y = scissor_state->miny; + cr.rect.extent.width = MIN2(fb->width, scissor_state->maxx - scissor_state->minx); + cr.rect.extent.height = MIN2(fb->height, scissor_state->maxy - scissor_state->miny); + } else { + cr.rect.extent.width = fb->width; + cr.rect.extent.height = fb->height; + } cr.baseArrayLayer = 0; cr.layerCount = util_framebuffer_get_num_layers(fb); vkCmdClearAttachments(batch->cmdbuf, num_attachments, attachments, 1, &cr); @@ -90,16 +113,94 @@ zink_clear(struct pipe_context *pctx, double depth, unsigned stencil) { struct zink_context *ctx = zink_context(pctx); + struct pipe_framebuffer_state *fb = &ctx->fb_state; + struct zink_batch *batch = zink_curr_batch(ctx); - /* FIXME: this is very inefficient; if no renderpass has been started yet, - * we should record the clear if it's full-screen, and apply it as we - * start the render-pass. Otherwise we can do a partial out-of-renderpass - * clear. - */ - zink_batch_rp(ctx); - assert(zink_curr_batch(ctx)->in_rp); - if (zink_curr_batch(ctx)->in_rp || ctx->render_condition_active) { + if (scissor_state) { + bool need_rp = false; + struct u_rect intersect = {0, fb->width, 0, fb->height}; + struct u_rect scissor = {scissor_state->minx, scissor_state->maxx, scissor_state->miny, scissor_state->maxy}; + if (!u_rect_test_intersection(&scissor, &intersect)) + need_rp = true; + else { + u_rect_find_intersection(&scissor, &intersect); + if (intersect.x0 != 0 || intersect.y0 != 0 || + intersect.x1 != fb->width || intersect.y1 != fb->height) + need_rp = true; + } + if (need_rp) + /* FIXME: this is very inefficient; if no renderpass has been started yet, + * we should record the clear if it's full-screen, and apply it as we + * start the render-pass. Otherwise we can do a partial out-of-renderpass + * clear. + */ + batch = zink_batch_rp(ctx); + } + + if (batch->in_rp || ctx->render_condition_active) { clear_in_rp(pctx, buffers, scissor_state, pcolor, depth, stencil); return; } + + VkImageSubresourceRange range = {}; + range.levelCount = 1; + if (buffers & PIPE_CLEAR_COLOR) { + range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + for (unsigned i = 0; i < fb->nr_cbufs; i++) { + if (!(buffers & (PIPE_CLEAR_COLOR0 << i)) || !fb->cbufs[i]) + continue; + VkClearColorValue color; + struct pipe_surface *psurf = fb->cbufs[i]; + + if (psurf->texture->target == PIPE_TEXTURE_3D && !check_3d_layers(psurf)) { + clear_in_rp(pctx, buffers, scissor_state, pcolor, depth, stencil); + return; + } + + struct zink_resource *res = zink_resource(psurf->texture); + if (res->layout != VK_IMAGE_LAYOUT_GENERAL && res->layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) + zink_resource_barrier(batch->cmdbuf, res, range.aspectMask, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + if (psurf->format != res->base.format && + !util_format_is_srgb(psurf->format) && util_format_is_srgb(res->base.format)) { + /* if SRGB mode is disabled for the fb with a backing srgb image then we have to + * convert this to srgb color + */ + color.float32[0] = util_format_srgb_to_linear_float(pcolor->f[0]); + color.float32[1] = util_format_srgb_to_linear_float(pcolor->f[1]); + color.float32[2] = util_format_srgb_to_linear_float(pcolor->f[2]); + } else { + color.float32[0] = pcolor->f[0]; + color.float32[1] = pcolor->f[1]; + color.float32[2] = pcolor->f[2]; + } + color.float32[3] = pcolor->f[3]; + range.baseMipLevel = psurf->u.tex.level; + range.baseArrayLayer = psurf->u.tex.first_layer; + range.layerCount = psurf->u.tex.last_layer - psurf->u.tex.first_layer + 1; + + vkCmdClearColorImage(batch->cmdbuf, res->image, res->layout, &color, 1, &range); + } + } + + range.aspectMask = 0; + if (buffers & PIPE_CLEAR_DEPTHSTENCIL && fb->zsbuf) { + if (fb->zsbuf->texture->target == PIPE_TEXTURE_3D && !check_3d_layers(fb->zsbuf)) { + clear_in_rp(pctx, buffers, scissor_state, pcolor, depth, stencil); + return; + } + + if (buffers & PIPE_CLEAR_DEPTH) + range.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT; + if (buffers & PIPE_CLEAR_STENCIL) + range.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + VkClearDepthStencilValue zs_value = {depth, stencil}; + range.baseMipLevel = fb->zsbuf->u.tex.level; + range.baseArrayLayer = fb->zsbuf->u.tex.first_layer; + range.layerCount = fb->zsbuf->u.tex.last_layer - fb->zsbuf->u.tex.first_layer + 1; + + struct zink_resource *res = zink_resource(fb->zsbuf->texture); + if (res->layout != VK_IMAGE_LAYOUT_GENERAL && res->layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) + zink_resource_barrier(batch->cmdbuf, res, range.aspectMask, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + vkCmdClearDepthStencilImage(batch->cmdbuf, res->image, res->layout, &zs_value, 1, &range); + } }